<?php

declare(strict_types=1);

namespace App\Services;

use App\Shared\Schema\PartySchemaSupport;
use App\Shared\Finance\AccountBalanceSupport;
use App\Shared\Schema\FinanceSchemaSupport;
use App\Core\SchemaRuntime;
use App\Repositories\CashPaymentRepository;
use InvalidArgumentException;
use PDO;

final class CashPaymentService
{
    public function __construct(private PDO $pdo, private CashPaymentRepository $repo) {}

    public function ensureSchema(): void
    {
        SchemaRuntime::ensure($this->pdo, 'cash_payments_schema', function (): void { FinanceSchemaSupport::ensureCashPaymentsSchema($this->pdo); }, 'sql/migrate_v1_22_cash_payments.sql'); SchemaRuntime::ensure($this->pdo, 'bank_accounts_schema', function (): void { FinanceSchemaSupport::ensureBankAccountsSchema($this->pdo); }, 'sql/migrate_v1_21_bank_accounts.sql'); SchemaRuntime::ensure($this->pdo, 'parties_schema', function (): void { PartySchemaSupport::ensurePartiesSchema($this->pdo); }, 'sql/migrate_v1_18_parties.sql'); SchemaRuntime::ensure($this->pdo, 'party_ledger_schema', function (): void { PartySchemaSupport::ensurePartyLedgerSchema($this->pdo); }, 'sql/migrate_v1_20_party_ledger.sql'); SchemaRuntime::ensure($this->pdo, 'expenses_schema', function (): void { FinanceSchemaSupport::ensureExpensesSchema($this->pdo); }, 'sql/migrate_v1_17_expenses.sql'); SchemaRuntime::ensure($this->pdo, 'parties_migration_bridge', function (): void { PartySchemaSupport::ensurePartiesMigrateFromCustomersSuppliers($this->pdo); }, 'sql/migrate_v1_18_parties.sql');
    }

    public function loadForm(): array
    {
        $cashboxes = $this->repo->activeCashboxes(); $bankAccounts = $this->repo->activeBankAccounts(); $expenseItems = $this->repo->activeExpenseItems(); $openCustodies = [];
        $form = ['voucher_date'=>date('Y-m-d'),'account_source_type'=>'cashbox','payment_type'=>'party','cashbox_id'=>(int)($cashboxes[0]['id'] ?? 1),'bank_account_id'=>(int)($bankAccounts[0]['id'] ?? 0),'party_id'=>0,'party_name'=>'','expense_item_id'=>0,'amount'=>'','pay_method'=>'cash','payee_name'=>'','purpose'=>'','ref_no'=>'','notes'=>''];
        return compact('cashboxes','bankAccounts','expenseItems','openCustodies','form') + ['errors'=>[]];
    }

    public function save(array $input, int $userId): int
    {
        $form = [
          'voucher_date' => trim((string)($input['voucher_date'] ?? date('Y-m-d'))),
          'account_source_type' => trim((string)($input['account_source_type'] ?? 'cashbox')),
          'payment_type' => trim((string)($input['payment_type'] ?? 'party')),
          'cashbox_id' => (int)($input['cashbox_id'] ?? 0),
          'bank_account_id' => (int)($input['bank_account_id'] ?? 0),
          'party_id' => (int)($input['party_id'] ?? 0),
          'party_name' => trim((string)($input['party_name'] ?? '')),
          'expense_item_id' => (int)($input['expense_item_id'] ?? 0),
          'amount' => trim((string)($input['amount'] ?? '')),
          'pay_method' => strtolower(trim((string)($input['pay_method'] ?? 'cash'))),
          'payee_name' => trim((string)($input['payee_name'] ?? '')),
          'purpose' => trim((string)($input['purpose'] ?? '')),
          'ref_no' => trim((string)($input['ref_no'] ?? '')),
          'notes' => trim((string)($input['notes'] ?? '')),
        ];
        if (!in_array($form['account_source_type'], ['cashbox','bank_account'], true)) $form['account_source_type'] = 'cashbox';
        if (!in_array($form['payment_type'], ['party','expense'], true)) $form['payment_type'] = 'party';
        if ($form['account_source_type'] === 'cashbox' && $form['cashbox_id'] <= 0) throw new InvalidArgumentException('اختر خزنة.');
        if ($form['account_source_type'] === 'bank_account' && $form['bank_account_id'] <= 0) throw new InvalidArgumentException('اختر حسابًا بنكيًا.');
        if ($form['voucher_date'] === '') throw new InvalidArgumentException('التاريخ مطلوب.');
        if ($form['amount'] === '' || !is_numeric($form['amount']) || (float)$form['amount'] <= 0) throw new InvalidArgumentException('المبلغ غير صحيح.');
        if (!in_array($form['pay_method'], ['cash','cheque','bank_transfer'], true)) $form['pay_method'] = 'cash';
        if ($form['payment_type'] === 'party') {
            if ($form['party_id'] > 0) {
                $sup = $this->repo->findSupplierParty($form['party_id']);
                if (!$sup) throw new InvalidArgumentException('اختر مورد صحيح.');
                $form['party_name'] = (string)$sup['name']; if ($form['payee_name'] === '') $form['payee_name'] = (string)$sup['name'];
            }
            if ($form['payee_name'] === '' && (int)$form['party_id'] <= 0) throw new InvalidArgumentException('اسم المستفيد/الجهة مطلوب.');
        } else {
            if ($form['expense_item_id'] <= 0) throw new InvalidArgumentException('اختر بند المصروف.');
            $selected = $this->repo->findExpenseItem($form['expense_item_id']);
            if (!$selected) throw new InvalidArgumentException('اختر بند مصروف صحيح.');
            if ($form['payee_name'] === '') $form['payee_name'] = (string)$selected['name'];
            $form['party_id'] = 0; $form['party_name'] = '';
        }
        $amount = (float)$form['amount'];
        if ($form['account_source_type'] === 'cashbox') {
            $avail = AccountBalanceSupport::cashboxBalance($this->pdo, (int)$form['cashbox_id'], $form['voucher_date']); if ($amount > $avail) throw new InvalidArgumentException('رصيد الخزنة لا يكفي لتنفيذ سند الصرف. المتاح: ' . number_format($avail, 2) . '.');
        } else { $avail = AccountBalanceSupport::bankAccountBalance($this->pdo, (int)$form['bank_account_id'], $form['voucher_date']); if ($amount > $avail) throw new InvalidArgumentException('رصيد الحساب البنكي لا يكفي لتنفيذ سند الصرف. المتاح: ' . number_format($avail, 2) . '.'); }
        $this->pdo->beginTransaction();
        try {
            $voucherNo = AccountBalanceSupport::nextCashPaymentNo($this->pdo);
            $id = $this->repo->create([
                'voucher_no'=>$voucherNo,'voucher_date'=>$form['voucher_date'],'cashbox_id'=>$form['account_source_type']==='cashbox'?(int)$form['cashbox_id']:null,'bank_account_id'=>$form['account_source_type']==='bank_account'?(int)$form['bank_account_id']:null,'account_source_type'=>$form['account_source_type'],'payment_type'=>$form['payment_type'],'party_id'=>$form['payment_type']==='party'?(int)$form['party_id']:null,'expense_item_id'=>$form['payment_type']==='expense'?(int)$form['expense_item_id']:null,'amount'=>$amount,'pay_method'=>$form['pay_method'],'payee_name'=>$form['payee_name'],'purpose'=>$form['purpose'] !== '' ? $form['purpose'] : null,'ref_no'=>$form['ref_no'] !== '' ? $form['ref_no'] : null,'notes'=>$form['notes'] !== '' ? $form['notes'] : null,'user_id'=>$userId]);
            $dt = $form['voucher_date'].' 00:00:00';
            if ($form['account_source_type'] === 'cashbox') AccountBalanceSupport::addCashboxTransaction($this->pdo, $form['cashbox_id'], $dt, 'out', $amount, 'cash_payment', $id, $form['notes'] !== '' ? $form['notes'] : null, $userId);
            else $this->pdo->prepare("INSERT INTO bank_transactions (bank_account_id, trx_date, trx_type, amount, source_type, source_id, ref_no, notes, user_id) VALUES (?,?,?,?,?,?,?,?,?)")->execute([(int)$form['bank_account_id'], $dt, 'out', $amount, 'cash_payment', $id, $form['ref_no'] !== '' ? $form['ref_no'] : null, $form['notes'] !== '' ? $form['notes'] : null, $userId]);
            if ($form['payment_type'] === 'party' && (int)$form['party_id'] > 0) PartyLedgerSupport::addLedger($this->pdo, (int)$form['party_id'], 'PAY', $id, $voucherNo, date('Y-m-d', strtotime($form['voucher_date'])), $amount, 0, $form['notes'] !== '' ? $form['notes'] : 'سند صرف');
            audit_log('CREATE_CASH_PAYMENT', 'cash_payments', $id, ['voucher_no'=>$voucherNo,'amount'=>$amount,'account_source_type'=>$form['account_source_type'],'cashbox_id'=>$form['cashbox_id'],'bank_account_id'=>$form['bank_account_id'],'pay_method'=>$form['pay_method'],'payment_type'=>$form['payment_type'],'expense_item_id'=>$form['expense_item_id'],'party_id'=>$form['party_id']]);
            $this->pdo->commit(); return $id;
        } catch (\Throwable $e) { if ($this->pdo->inTransaction()) $this->pdo->rollBack(); throw $e; }
    }

    public function loadView(int $id): array
    {
        $row = $this->repo->findViewById($id); if (!$row) throw new InvalidArgumentException('سند غير موجود'); return ['row'=>$row];
    }
}
