<?php
declare(strict_types=1);

namespace App\Modules\Procurement\Services;

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

final class VendorPaymentService
{
    public static function createPayment(array $data): int
    {
        $db = App::getInstance()->db();
        $employee = App::getInstance()->currentEmployee();

        $supplierId = (int) ($data['supplier_id'] ?? 0);
        $amount     = (string) ($data['amount'] ?? '0');

        if ($supplierId <= 0) {
            throw new \RuntimeException('يجب تحديد المورد');
        }
        if (bccomp($amount, '0', 2) <= 0) {
            throw new \RuntimeException('يجب تحديد مبلغ الدفعة');
        }

        $paymentNumber = ProcurementNumberGenerator::nextPaymentNumber();

        $paymentId = $db->insert('vendor_payments', [
            'payment_number'  => $paymentNumber,
            'supplier_id'     => $supplierId,
            'invoice_id'      => !empty($data['invoice_id']) ? (int) $data['invoice_id'] : null,
            'amount'          => $amount,
            'payment_method'  => $data['payment_method'] ?? 'bank_transfer',
            'bank_account_id' => !empty($data['bank_account_id']) ? (int) $data['bank_account_id'] : null,
            'check_number'    => $data['check_number'] ?? null,
            'reference_number' => $data['reference_number'] ?? null,
            'payment_date'    => $data['payment_date'] ?? date('Y-m-d'),
            'status'          => 'draft',
            'notes'           => $data['notes'] ?? null,
            'branch_id'       => $data['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'),
        ]);

        Logger::info("Vendor payment #{$paymentId} ({$paymentNumber}) created, amount: {$amount}");
        return $paymentId;
    }

    public static function approvePayment(int $paymentId): void
    {
        $db = App::getInstance()->db();
        $employee = App::getInstance()->currentEmployee();

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

        $db->update('vendor_payments', [
            'status'      => 'approved',
            'approved_by' => $employee ? (int) $employee->id : null,
            'approved_at' => date('Y-m-d H:i:s'),
            'updated_at'  => date('Y-m-d H:i:s'),
        ], '`id` = ?', [$paymentId]);

        Logger::info("Vendor payment #{$paymentId} approved");
    }

    public static function completePayment(int $paymentId): void
    {
        $db = App::getInstance()->db();
        $employee = App::getInstance()->currentEmployee();

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

        $db->update('vendor_payments', [
            'status'       => 'completed',
            'completed_by' => $employee ? (int) $employee->id : null,
            'completed_at' => date('Y-m-d H:i:s'),
            'updated_at'   => date('Y-m-d H:i:s'),
        ], '`id` = ?', [$paymentId]);

        // Update invoice status if linked
        if (!empty($payment['invoice_id'])) {
            $invoice = $db->selectOne("SELECT * FROM `vendor_invoices` WHERE `id` = ?", [(int) $payment['invoice_id']]);
            if ($invoice) {
                // Check total paid against invoice
                $totalPaid = $db->selectOne(
                    "SELECT COALESCE(SUM(`amount`), 0) as total FROM `vendor_payments` WHERE `invoice_id` = ? AND `status` = 'completed'",
                    [(int) $payment['invoice_id']]
                );
                $paidAmount = bcadd((string) ($totalPaid['total'] ?? '0'), (string) $payment['amount'], 2);

                $newInvoiceStatus = bccomp($paidAmount, (string) $invoice['total_amount'], 2) >= 0
                    ? 'paid'
                    : 'partial_paid';

                $db->update('vendor_invoices', [
                    'status'     => $newInvoiceStatus,
                    'updated_at' => date('Y-m-d H:i:s'),
                ], '`id` = ?', [(int) $payment['invoice_id']]);
            }
        }

        EventBus::dispatch('procurement.payment_completed', [
            'payment_id'     => $paymentId,
            'supplier_id'    => (int) $payment['supplier_id'],
            'invoice_id'     => $payment['invoice_id'] ? (int) $payment['invoice_id'] : null,
            'amount'         => (string) $payment['amount'],
            'payment_number' => $payment['payment_number'] ?? '',
            'payment_method' => $payment['payment_method'],
            'bank_account_id' => $payment['bank_account_id'] ? (int) $payment['bank_account_id'] : null,
        ]);

        Logger::info("Vendor payment #{$paymentId} completed — accounting event dispatched");
    }

    public static function voidPayment(int $paymentId, string $reason): void
    {
        $db = App::getInstance()->db();
        $employee = App::getInstance()->currentEmployee();

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

        $wasCompleted = $payment['status'] === 'completed';

        $db->update('vendor_payments', [
            'status'      => 'voided',
            'voided_by'   => $employee ? (int) $employee->id : null,
            'voided_at'   => date('Y-m-d H:i:s'),
            'void_reason' => $reason,
            'updated_at'  => date('Y-m-d H:i:s'),
        ], '`id` = ?', [$paymentId]);

        if ($wasCompleted) {
            EventBus::dispatch('procurement.payment_voided', [
                'payment_id'       => $paymentId,
                'supplier_id'      => (int) $payment['supplier_id'],
                'invoice_id'       => $payment['invoice_id'] ? (int) $payment['invoice_id'] : null,
                'amount'           => (string) $payment['amount'],
                'reason'           => $reason,
                'journal_entry_id' => $payment['journal_entry_id'] ? (int) $payment['journal_entry_id'] : null,
            ]);

            // Re-evaluate invoice status if linked
            if (!empty($payment['invoice_id'])) {
                $totalPaid = $db->selectOne(
                    "SELECT COALESCE(SUM(`amount`), 0) as total FROM `vendor_payments` WHERE `invoice_id` = ? AND `status` = 'completed'",
                    [(int) $payment['invoice_id']]
                );
                $paidAmount = (string) ($totalPaid['total'] ?? '0');
                $invoice = $db->selectOne("SELECT `total_amount` FROM `vendor_invoices` WHERE `id` = ?", [(int) $payment['invoice_id']]);

                if ($invoice) {
                    $newStatus = bccomp($paidAmount, '0', 2) <= 0 ? 'approved' :
                        (bccomp($paidAmount, (string) $invoice['total_amount'], 2) >= 0 ? 'paid' : 'partial_paid');

                    $db->update('vendor_invoices', [
                        'status'     => $newStatus,
                        'updated_at' => date('Y-m-d H:i:s'),
                    ], '`id` = ?', [(int) $payment['invoice_id']]);
                }
            }
        }

        Logger::info("Vendor payment #{$paymentId} voided: {$reason}");
    }
}
