<?php
declare(strict_types=1);

namespace App\Modules\Accounting\Services;

use App\Core\App;
use App\Core\Logger;
use App\Modules\Accounting\AccountCodes;
use App\Modules\Accounting\Models\BankReconciliation;

/**
 * Bank reconciliation logic — matches book entries against bank statement.
 */
final class BankReconciliationService
{
    /**
     * Create a new reconciliation session.
     */
    public static function create(array $data): array
    {
        $db = App::getInstance()->db();
        $employee = App::getInstance()->currentEmployee();

        $bankAccountId = (int) ($data['bank_account_id'] ?? 0);
        if ($bankAccountId <= 0) {
            return ['success' => false, 'error' => 'الحساب البنكي غير محدد'];
        }

        // Get book balance from GL
        $bankAccount = $db->selectOne("SELECT * FROM bank_accounts WHERE id = ? AND is_archived = 0", [$bankAccountId]);
        if (!$bankAccount) {
            return ['success' => false, 'error' => 'الحساب البنكي غير موجود'];
        }

        $bookBalance = $bankAccount['current_balance'] ?? '0.00';
        $statementBalance = $data['statement_balance'] ?? '0.00';
        $difference = bcsub((string) $statementBalance, (string) $bookBalance, 2);

        $db->beginTransaction();
        try {
            $id = $db->insert('bank_reconciliations', [
                'bank_account_id'     => $bankAccountId,
                'reconciliation_date' => $data['reconciliation_date'] ?? date('Y-m-d'),
                'statement_date'      => $data['statement_date'] ?? date('Y-m-d'),
                'statement_balance'   => $statementBalance,
                'book_balance'        => $bookBalance,
                'adjusted_balance'    => $bookBalance,
                'difference'          => $difference,
                'status'              => 'draft',
                'notes'               => $data['notes'] ?? null,
                'created_at'          => date('Y-m-d H:i:s'),
                'updated_at'          => date('Y-m-d H:i:s'),
                'created_by'          => $employee ? (int) $employee->id : null,
            ]);

            $db->commit();
            return ['success' => true, 'reconciliation_id' => $id];
        } catch (\Throwable $e) {
            $db->rollBack();
            return ['success' => false, 'error' => 'فشل إنشاء المطابقة: ' . $e->getMessage()];
        }
    }

    /**
     * Add a reconciliation item (outstanding check, deposit in transit, etc.)
     */
    public static function addItem(int $reconciliationId, array $data): array
    {
        $db = App::getInstance()->db();

        $recon = $db->selectOne("SELECT * FROM bank_reconciliations WHERE id = ? AND is_archived = 0", [$reconciliationId]);
        if (!$recon) {
            return ['success' => false, 'error' => 'المطابقة غير موجودة'];
        }
        if ($recon['status'] === 'completed' || $recon['status'] === 'approved') {
            return ['success' => false, 'error' => 'المطابقة مكتملة — لا يمكن إضافة بنود'];
        }

        $db->beginTransaction();
        try {
            $db->insert('bank_reconciliation_items', [
                'reconciliation_id' => $reconciliationId,
                'item_type'         => $data['item_type'],
                'description_ar'    => $data['description_ar'],
                'description_en'    => $data['description_en'] ?? null,
                'amount'            => $data['amount'],
                'transaction_date'  => $data['transaction_date'] ?? null,
                'reference_number'  => $data['reference_number'] ?? null,
                'is_cleared'        => 0,
                'created_at'        => date('Y-m-d H:i:s'),
            ]);

            // Recalculate adjusted balance
            self::recalculate($reconciliationId);

            $db->commit();
            return ['success' => true];
        } catch (\Throwable $e) {
            $db->rollBack();
            return ['success' => false, 'error' => 'فشل إضافة البند: ' . $e->getMessage()];
        }
    }

    /**
     * Recalculate the adjusted balance based on items.
     */
    public static function recalculate(int $reconciliationId): void
    {
        $db = App::getInstance()->db();

        $recon = $db->selectOne("SELECT * FROM bank_reconciliations WHERE id = ?", [$reconciliationId]);
        if (!$recon) {
            return;
        }

        $items = $db->select(
            "SELECT * FROM bank_reconciliation_items WHERE reconciliation_id = ? AND is_cleared = 0",
            [$reconciliationId]
        );

        $adjustedBalance = (string) $recon['book_balance'];

        foreach ($items as $item) {
            $amount = (string) $item['amount'];
            switch ($item['item_type']) {
                case 'bank_charge':
                    // Bank deducted but not in books yet — reduce book balance
                    $adjustedBalance = bcsub($adjustedBalance, $amount, 2);
                    break;
                case 'bank_interest':
                    // Bank added but not in books yet — increase book balance
                    $adjustedBalance = bcadd($adjustedBalance, $amount, 2);
                    break;
                case 'outstanding_check':
                    // Issued check not yet cleared at bank — add back to statement
                    break;
                case 'deposit_in_transit':
                    // Deposited but not yet in bank — add to statement
                    break;
                case 'book_error':
                    // Adjust for book errors
                    $adjustedBalance = bcadd($adjustedBalance, $amount, 2);
                    break;
                case 'bank_error':
                    // Bank error — no book adjustment
                    break;
            }
        }

        $difference = bcsub((string) $recon['statement_balance'], $adjustedBalance, 2);

        $db->update('bank_reconciliations', [
            'adjusted_balance' => $adjustedBalance,
            'difference'       => $difference,
            'updated_at'       => date('Y-m-d H:i:s'),
        ], '`id` = ?', [$reconciliationId]);
    }

    /**
     * Complete reconciliation — creates adjusting journal entries for bank charges/interest.
     */
    public static function complete(int $reconciliationId): array
    {
        $db = App::getInstance()->db();

        $recon = $db->selectOne("SELECT * FROM bank_reconciliations WHERE id = ? AND is_archived = 0", [$reconciliationId]);
        if (!$recon) {
            return ['success' => false, 'error' => 'المطابقة غير موجودة'];
        }

        if (bccomp((string) $recon['difference'], '0.00', 2) !== 0) {
            return ['success' => false, 'error' => 'يوجد فرق ' . $recon['difference'] . ' — يجب تسوية جميع الفروقات أولاً'];
        }

        // Get items that need adjusting entries (bank charges, bank interest)
        $adjustItems = $db->select(
            "SELECT * FROM bank_reconciliation_items
             WHERE reconciliation_id = ? AND item_type IN ('bank_charge','bank_interest') AND is_cleared = 0",
            [$reconciliationId]
        );

        $bankAccount = $db->selectOne("SELECT * FROM bank_accounts WHERE id = ?", [(int) $recon['bank_account_id']]);
        $glAccountId = $bankAccount ? (int) $bankAccount['gl_account_id'] : null;

        // Create adjusting entries for bank charges/interest
        foreach ($adjustItems as $item) {
            if ($glAccountId) {
                $lines = [];
                if ($item['item_type'] === 'bank_charge') {
                    // Dr. Bank Charges Expense, Cr. Bank
                    $bankChargesAccount = $db->selectOne(
                        "SELECT id FROM chart_of_accounts WHERE account_code = ? AND is_archived = 0",
                        [AccountCodes::BANK_CHARGES]
                    );
                    if ($bankChargesAccount) {
                        $lines = [
                            ['account_id' => (int) $bankChargesAccount['id'], 'debit' => $item['amount'], 'credit' => '0.00',
                             'description_ar' => 'مصاريف بنكية — ' . $item['description_ar']],
                            ['account_id' => $glAccountId, 'debit' => '0.00', 'credit' => $item['amount'],
                             'description_ar' => 'مصاريف بنكية — ' . $item['description_ar']],
                        ];
                    }
                } elseif ($item['item_type'] === 'bank_interest') {
                    // Dr. Bank, Cr. Interest Income
                    $interestAccount = $db->selectOne(
                        "SELECT id FROM chart_of_accounts WHERE account_code = ? AND is_archived = 0",
                        [AccountCodes::BANK_INTEREST_INCOME]
                    );
                    if ($interestAccount) {
                        $lines = [
                            ['account_id' => $glAccountId, 'debit' => $item['amount'], 'credit' => '0.00',
                             'description_ar' => 'فوائد بنكية — ' . $item['description_ar']],
                            ['account_id' => (int) $interestAccount['id'], 'debit' => '0.00', 'credit' => $item['amount'],
                             'description_ar' => 'فوائد بنكية — ' . $item['description_ar']],
                        ];
                    }
                }

                if (!empty($lines)) {
                    $result = JournalService::createEntry([
                        'entry_date'        => $recon['reconciliation_date'],
                        'description_ar'    => 'تسوية مطابقة بنكية — ' . $item['description_ar'],
                        'reference_type'    => 'bank_reconciliation',
                        'reference_id'      => $reconciliationId,
                        'source_module'     => 'accounting',
                        'is_auto_generated' => 1,
                    ], $lines, true);

                    if ($result['success']) {
                        $db->update('bank_reconciliation_items', [
                            'journal_entry_id' => $result['journal_entry_id'],
                            'is_cleared'       => 1,
                            'cleared_date'     => date('Y-m-d'),
                        ], '`id` = ?', [(int) $item['id']]);
                    }
                }
            }
        }

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

        return ['success' => true];
    }

    /**
     * Get unreconciled bank transactions for a bank account.
     */
    public static function getUnreconciledTransactions(int $bankAccountId, string $asOfDate): array
    {
        $db = App::getInstance()->db();

        $bankAccount = $db->selectOne("SELECT gl_account_id FROM bank_accounts WHERE id = ?", [$bankAccountId]);
        if (!$bankAccount || !$bankAccount['gl_account_id']) {
            return [];
        }

        return $db->select(
            "SELECT jel.*, je.entry_number, je.entry_date, je.description_ar as entry_description,
                    je.reference_type, je.reference_number
             FROM journal_entry_lines jel
             JOIN journal_entries je ON je.id = jel.journal_entry_id
             WHERE jel.account_id = ?
               AND je.status = 'posted'
               AND je.entry_date <= ?
               AND je.is_archived = 0
             ORDER BY je.entry_date DESC, je.id DESC
             LIMIT 200",
            [(int) $bankAccount['gl_account_id'], $asOfDate]
        );
    }
}
