<?php
declare(strict_types=1);

namespace App\Modules\Procurement\Services;

use App\Core\App;
use App\Core\EventBus;
use App\Core\Logger;
use App\Modules\Inventory\Services\CreditLimitService;

final class VendorInvoiceService
{
    /**
     * Create a new vendor invoice with items.
     */
    public static function createInvoice(array $header, array $items): int
    {
        $db = App::getInstance()->db();
        $employee = App::getInstance()->currentEmployee();

        if (empty($items)) {
            throw new \RuntimeException('يجب إضافة بند واحد على الأقل');
        }

        $supplierId = (int) ($header['supplier_id'] ?? 0);
        if ($supplierId <= 0) {
            throw new \RuntimeException('يجب تحديد المورد');
        }

        $internalNumber = ProcurementNumberGenerator::nextInvoiceNumber();

        $subtotal = '0.00';
        $taxTotal = '0.00';
        $discountTotal = '0.00';

        foreach ($items as $item) {
            $qty       = (string) ($item['quantity'] ?? '0');
            $unitCost  = (string) ($item['unit_cost'] ?? '0');
            $taxRate   = (string) ($item['tax_rate'] ?? '0');
            $discount  = (string) ($item['discount_amount'] ?? '0');
            $lineBase  = bcmul($qty, $unitCost, 2);
            $lineTax   = bcmul($lineBase, bcdiv($taxRate, '100', 4), 2);
            $subtotal  = bcadd($subtotal, $lineBase, 2);
            $taxTotal  = bcadd($taxTotal, $lineTax, 2);
            $discountTotal = bcadd($discountTotal, $discount, 2);
        }

        $totalAmount = bcsub(bcadd($subtotal, $taxTotal, 2), $discountTotal, 2);

        $db->beginTransaction();
        try {
            $invoiceId = $db->insert('vendor_invoices', [
                'invoice_number'    => $header['invoice_number'] ?? '',
                'internal_number'   => $internalNumber,
                'supplier_id'       => $supplierId,
                'purchase_order_id' => !empty($header['purchase_order_id']) ? (int) $header['purchase_order_id'] : null,
                'grn_id'            => !empty($header['grn_id']) ? (int) $header['grn_id'] : null,
                'invoice_date'      => $header['invoice_date'] ?? date('Y-m-d'),
                'due_date'          => $header['due_date'] ?? null,
                'subtotal'          => $subtotal,
                'tax_amount'        => $taxTotal,
                'discount_amount'   => $discountTotal,
                'total_amount'      => $totalAmount,
                'status'            => 'draft',
                'notes'             => $header['notes'] ?? null,
                'branch_id'         => $header['branch_id'] ?? null,
                'created_by'        => $employee ? (int) $employee->id : null,
                'created_at'        => date('Y-m-d H:i:s'),
                'updated_at'        => date('Y-m-d H:i:s'),
            ]);

            foreach ($items as $item) {
                $qty       = (string) ($item['quantity'] ?? '0');
                $unitCost  = (string) ($item['unit_cost'] ?? '0');
                $taxRate   = (string) ($item['tax_rate'] ?? '0');
                $discount  = (string) ($item['discount_amount'] ?? '0');
                $lineBase  = bcmul($qty, $unitCost, 2);
                $lineTax   = bcmul($lineBase, bcdiv($taxRate, '100', 4), 2);
                $lineTotal = bcsub(bcadd($lineBase, $lineTax, 2), $discount, 2);

                $db->insert('vendor_invoice_items', [
                    'invoice_id'      => $invoiceId,
                    'po_item_id'      => !empty($item['po_item_id']) ? (int) $item['po_item_id'] : null,
                    'item_id'         => !empty($item['item_id']) ? (int) $item['item_id'] : null,
                    'description'     => $item['description'] ?? null,
                    'quantity'        => $qty,
                    'unit_cost'       => $unitCost,
                    'tax_rate'        => $taxRate,
                    'tax_amount'      => $lineTax,
                    'discount_amount' => $discount,
                    'line_total'      => $lineTotal,
                ]);
            }

            $db->commit();
        } catch (\Throwable $e) {
            $db->rollBack();
            throw $e;
        }

        Logger::info("Vendor invoice #{$invoiceId} ({$internalNumber}) created, total: {$totalAmount}");
        return $invoiceId;
    }

    /**
     * Verify an invoice (run 3-way match).
     */
    public static function verifyInvoice(int $invoiceId): array
    {
        $db = App::getInstance()->db();

        $invoice = $db->selectOne("SELECT * FROM `vendor_invoices` WHERE `id` = ?", [$invoiceId]);
        if (!$invoice) {
            throw new \RuntimeException('الفاتورة غير موجودة');
        }
        if ($invoice['status'] !== 'draft') {
            throw new \RuntimeException('الفاتورة ليست في حالة مسودة');
        }

        $matchResult = ThreeWayMatchService::performMatch($invoiceId);

        $db->update('vendor_invoices', [
            'status'     => 'verified',
            'updated_at' => date('Y-m-d H:i:s'),
        ], '`id` = ?', [$invoiceId]);

        Logger::info("Vendor invoice #{$invoiceId} verified, match: {$matchResult['status']}");
        return $matchResult;
    }

    /**
     * Approve a verified invoice — dispatches event for accounting.
     */
    public static function approveInvoice(int $invoiceId): void
    {
        $db = App::getInstance()->db();

        $invoice = $db->selectOne("SELECT * FROM `vendor_invoices` WHERE `id` = ?", [$invoiceId]);
        if (!$invoice) {
            throw new \RuntimeException('الفاتورة غير موجودة');
        }
        if ($invoice['status'] !== 'verified') {
            throw new \RuntimeException('الفاتورة ليست في حالة تم التحقق');
        }

        $creditCheck = CreditLimitService::checkSupplierCanTransact(
            (int) $invoice['supplier_id'],
            (string) $invoice['total_amount']
        );
        if (!$creditCheck['allowed']) {
            throw new \RuntimeException($creditCheck['message']);
        }

        $db->update('vendor_invoices', [
            'status'     => 'approved',
            'updated_at' => date('Y-m-d H:i:s'),
        ], '`id` = ?', [$invoiceId]);

        EventBus::dispatch('procurement.invoice_approved', [
            'invoice_id'      => $invoiceId,
            'invoice_number'  => $invoice['internal_number'] ?? '',
            'supplier_id'     => (int) $invoice['supplier_id'],
            'internal_number' => $invoice['internal_number'] ?? '',
            'subtotal'        => (string) $invoice['subtotal'],
            'tax_amount'      => (string) $invoice['tax_amount'],
            'total_amount'    => (string) $invoice['total_amount'],
            'due_date'        => $invoice['due_date'],
        ]);

        Logger::info("Vendor invoice #{$invoiceId} approved — accounting event dispatched");
    }

    /**
     * Cancel an invoice (draft or verified only).
     */
    public static function cancelInvoice(int $invoiceId): void
    {
        $db = App::getInstance()->db();

        $invoice = $db->selectOne("SELECT * FROM `vendor_invoices` WHERE `id` = ?", [$invoiceId]);
        if (!$invoice) {
            throw new \RuntimeException('الفاتورة غير موجودة');
        }
        if (!in_array($invoice['status'], ['draft', 'verified'], true)) {
            throw new \RuntimeException('لا يمكن إلغاء الفاتورة في حالتها الحالية');
        }

        $db->update('vendor_invoices', [
            'status'     => 'cancelled',
            'updated_at' => date('Y-m-d H:i:s'),
        ], '`id` = ?', [$invoiceId]);

        Logger::info("Vendor invoice #{$invoiceId} cancelled");
    }
}
