<?php
declare(strict_types=1);

namespace App\Modules\Procurement\Services;

use App\Core\App;
use App\Core\Logger;

/**
 * 3-Way Match: PO ↔ GRN ↔ Invoice
 * Compares quantities and costs across PO items, GRN accepted quantities, and invoice line items.
 */
final class ThreeWayMatchService
{
    /**
     * Perform a 3-way match for a vendor invoice.
     *
     * @return array ['status' => string, 'details' => array, 'notes' => string]
     */
    public static function performMatch(int $invoiceId): array
    {
        $db = App::getInstance()->db();

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

        $poId  = (int) ($invoice['purchase_order_id'] ?? 0);
        $grnId = (int) ($invoice['grn_id'] ?? 0);

        if ($poId <= 0) {
            return ['status' => 'unmatched', 'details' => [], 'notes' => 'لا يوجد أمر شراء مرتبط'];
        }

        // Get tolerance from config
        $config = $db->selectOne(
            "SELECT `config_value` FROM `system_config` WHERE `config_key` = 'procurement.match_tolerance_pct'"
        );
        $tolerancePct = (float) ($config['config_value'] ?? 2.0);

        // Get invoice items
        $invoiceItems = $db->select(
            "SELECT * FROM `vendor_invoice_items` WHERE `invoice_id` = ?",
            [$invoiceId]
        );

        // Get PO items
        $poItems = $db->select(
            "SELECT * FROM `purchase_order_items` WHERE `purchase_order_id` = ?",
            [$poId]
        );
        $poItemsMap = [];
        foreach ($poItems as $poi) {
            $poItemsMap[(int) $poi['id']] = $poi;
        }

        // Get GRN items if GRN specified
        $grnItemsMap = [];
        if ($grnId > 0) {
            $grnItems = $db->select(
                "SELECT * FROM `grn_items` WHERE `grn_id` = ?",
                [$grnId]
            );
            foreach ($grnItems as $gi) {
                $grnItemsMap[(int) $gi['po_item_id']] = $gi;
            }
        }

        $details = [];
        $hasDiscrepancy = false;
        $allWithinTolerance = true;

        foreach ($invoiceItems as $invItem) {
            $poItemId = (int) ($invItem['po_item_id'] ?? 0);
            $detail = [
                'item_id'          => (int) ($invItem['item_id'] ?? 0),
                'invoice_qty'      => (string) $invItem['quantity'],
                'invoice_cost'     => (string) $invItem['unit_cost'],
                'po_qty'           => '0',
                'po_cost'          => '0',
                'grn_accepted_qty' => '0',
                'qty_match'        => false,
                'cost_match'       => false,
                'notes'            => [],
            ];

            if ($poItemId > 0 && isset($poItemsMap[$poItemId])) {
                $poi = $poItemsMap[$poItemId];
                $detail['po_qty']  = (string) $poi['quantity_ordered'];
                $detail['po_cost'] = (string) $poi['unit_cost'];

                // Check GRN
                if (isset($grnItemsMap[$poItemId])) {
                    $detail['grn_accepted_qty'] = (string) $grnItemsMap[$poItemId]['quantity_accepted'];
                }

                // Quantity match: invoice qty vs GRN accepted qty (or PO qty if no GRN)
                $referenceQty = bccomp($detail['grn_accepted_qty'], '0', 3) > 0
                    ? $detail['grn_accepted_qty']
                    : $detail['po_qty'];

                $qtyDiff = bcsub($detail['invoice_qty'], $referenceQty, 3);
                $qtyDiffPct = bccomp($referenceQty, '0', 3) > 0
                    ? (float) bcdiv(bcmul($qtyDiff, '100', 3), $referenceQty, 3)
                    : 0.0;

                if (bccomp($qtyDiff, '0', 3) === 0) {
                    $detail['qty_match'] = true;
                } elseif (abs($qtyDiffPct) <= $tolerancePct) {
                    $detail['qty_match'] = true;
                    $detail['notes'][] = "فرق كمية {$qtyDiffPct}% (ضمن التسامح)";
                } else {
                    $detail['notes'][] = "فرق كمية: فاتورة {$detail['invoice_qty']} مقابل مرجع {$referenceQty}";
                    $hasDiscrepancy = true;
                    $allWithinTolerance = false;
                }

                // Cost match: invoice unit_cost vs PO unit_cost
                $costDiff = bcsub($detail['invoice_cost'], $detail['po_cost'], 2);
                $costDiffPct = bccomp($detail['po_cost'], '0', 2) > 0
                    ? (float) bcdiv(bcmul($costDiff, '100', 2), $detail['po_cost'], 2)
                    : 0.0;

                if (bccomp($costDiff, '0', 2) === 0) {
                    $detail['cost_match'] = true;
                } elseif (abs($costDiffPct) <= $tolerancePct) {
                    $detail['cost_match'] = true;
                    $detail['notes'][] = "فرق سعر {$costDiffPct}% (ضمن التسامح)";
                } else {
                    $detail['notes'][] = "فرق سعر: فاتورة {$detail['invoice_cost']} مقابل أمر شراء {$detail['po_cost']}";
                    $hasDiscrepancy = true;
                    $allWithinTolerance = false;
                }
            } else {
                $detail['notes'][] = 'لا يوجد بند مقابل في أمر الشراء';
                $hasDiscrepancy = true;
                $allWithinTolerance = false;
            }

            $details[] = $detail;
        }

        // Determine overall status
        if (!$hasDiscrepancy) {
            $matchStatus = 'matched';
            $matchNotes  = 'مطابقة كاملة';
        } elseif ($allWithinTolerance) {
            $matchStatus = 'tolerance_pass';
            $matchNotes  = 'مطابقة ضمن نسبة التسامح';
        } else {
            $matchStatus = 'discrepancy';
            $matchNotes  = 'يوجد اختلاف — يرجى المراجعة';
        }

        // Update invoice match status
        $db->update('vendor_invoices', [
            'match_status' => $matchStatus,
            'match_notes'  => $matchNotes,
            'updated_at'   => date('Y-m-d H:i:s'),
        ], '`id` = ?', [$invoiceId]);

        Logger::info("3-way match for invoice #{$invoiceId}: {$matchStatus}");

        return [
            'status'  => $matchStatus,
            'details' => $details,
            'notes'   => $matchNotes,
        ];
    }
}
