<?php
declare(strict_types=1);

namespace App\Modules\Cashier\Services;

use App\Core\App;
use App\Core\EventBus;
use App\Core\Logger;
use App\Modules\Treasury\Services\CustodyService;
use App\Modules\Treasury\Services\TreasuryService;

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

        $amount = $data['amount'] ?? '0.00';
        $bankAccountId = (int) ($data['bank_account_id'] ?? 0);
        $depositDate = $data['deposit_date'] ?? date('Y-m-d');
        $bankReceiptSerial = $data['bank_receipt_serial'] ?? null;
        $bankReceiptPath = $data['bank_receipt_path'] ?? null;
        $notes = $data['notes'] ?? null;

        if (bccomp((string) $amount, '0.01', 2) < 0) {
            return ['success' => false, 'error' => 'المبلغ يجب أن يكون أكبر من صفر'];
        }

        $mainTreasury = TreasuryService::getMainTreasury();
        if (!$mainTreasury) {
            return ['success' => false, 'error' => 'لم يتم تعريف الخزنة الرئيسية'];
        }

        $depositNumber = self::generateDepositNumber($db);
        $now = date('Y-m-d H:i:s');

        $id = $db->insert('treasury_deposits', [
            'deposit_number'      => $depositNumber,
            'treasury_id'         => (int) $mainTreasury['id'],
            'bank_account_id'     => $bankAccountId > 0 ? $bankAccountId : null,
            'amount'              => $amount,
            'deposit_date'        => $depositDate,
            'bank_receipt_serial' => $bankReceiptSerial,
            'bank_receipt_path'   => $bankReceiptPath,
            'deposited_by'        => $employee ? (int) $employee->id : 0,
            'deposited_at'        => $now,
            'status'              => 'deposited',
            'notes'               => $notes,
            'created_at'          => $now,
            'updated_at'          => $now,
        ]);

        CustodyService::recordDeposit(
            (int) $mainTreasury['id'],
            $employee ? (int) $employee->id : 0,
            (string) $amount,
            $id
        );

        EventBus::dispatch('treasury.deposit.created', [
            'deposit_id'  => $id,
            'treasury_id' => (int) $mainTreasury['id'],
            'amount'      => (string) $amount,
            'deposited_by' => $employee ? (int) $employee->id : 0,
        ]);

        Logger::info("Treasury deposit created", ['id' => $id, 'amount' => $amount, 'serial' => $bankReceiptSerial]);

        return [
            'success'        => true,
            'deposit_id'     => $id,
            'deposit_number' => $depositNumber,
        ];
    }

    public static function confirmDeposit(int $depositId, int $confirmedBy): array
    {
        $db = App::getInstance()->db();

        $deposit = $db->selectOne(
            "SELECT * FROM treasury_deposits WHERE id = ? AND status = 'deposited'",
            [$depositId]
        );
        if (!$deposit) {
            return ['success' => false, 'error' => 'الإيداع غير موجود أو تمت معالجته'];
        }

        $now = date('Y-m-d H:i:s');
        $db->update('treasury_deposits', [
            'confirmed_by' => $confirmedBy,
            'confirmed_at' => $now,
            'status'       => 'confirmed',
            'updated_at'   => $now,
        ], '`id` = ?', [$depositId]);

        CustodyService::recordDepositConfirmed(
            (int) $deposit['treasury_id'],
            (int) $deposit['deposited_by'],
            (string) $deposit['amount'],
            $depositId
        );

        EventBus::dispatch('treasury.deposit.confirmed', [
            'deposit_id'      => $depositId,
            'treasury_id'     => (int) $deposit['treasury_id'],
            'amount'          => (string) $deposit['amount'],
            'confirmed_by'    => $confirmedBy,
            'bank_account_id' => $deposit['bank_account_id'] ? (int) $deposit['bank_account_id'] : null,
        ]);

        Logger::info("Treasury deposit confirmed", ['id' => $depositId, 'amount' => $deposit['amount']]);

        return ['success' => true, 'amount' => (string) $deposit['amount']];
    }

    public static function rejectDeposit(int $depositId, int $rejectedBy, string $reason): array
    {
        $db = App::getInstance()->db();

        $deposit = $db->selectOne(
            "SELECT * FROM treasury_deposits WHERE id = ? AND status = 'deposited'",
            [$depositId]
        );
        if (!$deposit) {
            return ['success' => false, 'error' => 'الإيداع غير موجود أو تمت معالجته'];
        }

        $now = date('Y-m-d H:i:s');
        $db->update('treasury_deposits', [
            'confirmed_by'     => $rejectedBy,
            'confirmed_at'     => $now,
            'status'           => 'rejected',
            'rejection_reason' => $reason,
            'updated_at'       => $now,
        ], '`id` = ?', [$depositId]);

        // Reverse custody deduction — amount goes back to main treasury manager
        CustodyService::recordSettlementIn(
            (int) $deposit['treasury_id'],
            (int) $deposit['deposited_by'],
            (string) $deposit['amount'],
            $depositId
        );

        EventBus::dispatch('treasury.deposit.rejected', [
            'deposit_id' => $depositId,
            'reason'     => $reason,
            'rejected_by' => $rejectedBy,
        ]);

        Logger::info("Treasury deposit rejected", ['id' => $depositId, 'reason' => $reason]);

        return ['success' => true];
    }

    public static function getDeposits(array $filters = []): array
    {
        $db = App::getInstance()->db();
        $where = '1=1';
        $params = [];

        $status = $filters['status'] ?? '';
        if ($status !== '') {
            $where .= ' AND d.status = ?';
            $params[] = $status;
        }

        return $db->select(
            "SELECT d.*, e_dep.full_name_ar as deposited_by_name, e_conf.full_name_ar as confirmed_by_name,
                    ba.account_name_ar as bank_name
             FROM treasury_deposits d
             LEFT JOIN employees e_dep ON e_dep.id = d.deposited_by
             LEFT JOIN employees e_conf ON e_conf.id = d.confirmed_by
             LEFT JOIN bank_accounts ba ON ba.id = d.bank_account_id
             WHERE {$where}
             ORDER BY d.id DESC
             LIMIT 100",
            $params
        );
    }

    public static function getPendingDeposits(): array
    {
        $db = App::getInstance()->db();
        return $db->select(
            "SELECT d.*, e.full_name_ar as deposited_by_name, ba.account_name_ar as bank_name
             FROM treasury_deposits d
             LEFT JOIN employees e ON e.id = d.deposited_by
             LEFT JOIN bank_accounts ba ON ba.id = d.bank_account_id
             WHERE d.status = 'deposited'
             ORDER BY d.deposited_at ASC"
        );
    }

    public static function find(int $depositId): ?array
    {
        $db = App::getInstance()->db();
        return $db->selectOne(
            "SELECT d.*, e_dep.full_name_ar as deposited_by_name, e_conf.full_name_ar as confirmed_by_name,
                    ba.account_name_ar as bank_name, ba.bank_name_ar as bank_institution
             FROM treasury_deposits d
             LEFT JOIN employees e_dep ON e_dep.id = d.deposited_by
             LEFT JOIN employees e_conf ON e_conf.id = d.confirmed_by
             LEFT JOIN bank_accounts ba ON ba.id = d.bank_account_id
             WHERE d.id = ?",
            [$depositId]
        );
    }

    private static function generateDepositNumber($db): string
    {
        $year = date('Y');
        $row = $db->selectOne(
            "SELECT MAX(CAST(SUBSTRING(deposit_number, 10) AS UNSIGNED)) as max_num
             FROM treasury_deposits WHERE deposit_number LIKE ?",
            ["DEP-{$year}-%"]
        );
        $next = ((int) ($row['max_num'] ?? 0)) + 1;
        return sprintf('DEP-%s-%06d', $year, $next);
    }
}
