<?php
/**
 * PayeeAccounts — Slice 8Z-k
 *
 * Approved disbursement destinations per payee (STAFF/VENDOR/CUSTOMER). Adding a new account
 * kicks off an approval chain — FM only for accounts backing payments ≤ exec threshold, FM+Exec
 * for larger. Accountant may still use a PENDING account at disbursement time with a mandatory
 * override reason (audit-logged as USE_AD_HOC).
 *
 * The chain-routing amount ("intended max") is optional at creation; if omitted the FM-only chain
 * is used (Exec re-approval can be triggered later if a large payment ever tries to use it).
 */
class PayeeAccounts extends Model
{
    /**
     * List accounts optionally scoped. Filters:
     *   ['payee_type' => 'STAFF|VENDOR|CUSTOMER', 'payee_id' => int, 'status' => 'PENDING|APPROVED|DISABLED',
     *    'created_by' => int (my accounts), 'method' => string]
     */
    public function list(array $f = []): array
    {
        $db = new DB();
        $where = "WHERE a.deleted = 0";
        $p = [];
        foreach (['payee_type','status','method'] as $k) {
            if (!empty($f[$k])) { $where .= " AND a.$k = :$k"; $p[":$k"] = $f[$k]; }
        }
        if (!empty($f['payee_id'])) { $where .= " AND a.payee_id = :pid"; $p[':pid'] = (int)$f['payee_id']; }
        if (!empty($f['created_by'])) { $where .= " AND a.created_by = :cb"; $p[':cb'] = (int)$f['created_by']; }
        return $db->mySelect(
            "SELECT a.*, pv.provider_code
               FROM payee_accounts a
          LEFT JOIN payment_providers pv ON pv.c0d = a.provider_id
               $where
              ORDER BY a.status = 'APPROVED' DESC, a.status, a.payee_name, a.c0d DESC",
            $p
        ) ?: [];
    }

    public function byId(int $id): array
    {
        $db = new DB();
        $r = $db->mySelect(
            "SELECT a.*, pv.provider_code
               FROM payee_accounts a
          LEFT JOIN payment_providers pv ON pv.c0d = a.provider_id
              WHERE a.c0d = :id AND a.deleted = 0 LIMIT 1",
            [':id' => $id]
        );
        return $r[0] ?? [];
    }

    /** Accounts for a specific payee (used to prefill request forms). Approved first, then pending. */
    public function accountsFor(string $payeeType, int $payeeId): array
    {
        return $this->list(['payee_type' => $payeeType, 'payee_id' => $payeeId]);
    }

    /**
     * Create a new payee account (starts PENDING). Auto-submits the approval chain if $intendedMax
     * is supplied — FM-only chain below the exec threshold, FM+Exec above.
     */
    public function create(array $d, int $userId, string $userName, float $intendedMax = 0.0): array
    {
        $payeeType = strtoupper((string)($d['payee_type'] ?? ''));
        if (!in_array($payeeType, ['STAFF','VENDOR','CUSTOMER'], true)) return ['ok' => false, 'msg' => 'Invalid payee_type.'];
        $method = (string)($d['method'] ?? '');
        if (!in_array($method, ['MOBILE_MONEY','BANK_TRANSFER','CHEQUE'], true)) return ['ok' => false, 'msg' => 'Invalid method.'];
        $providerId = (int)($d['provider_id'] ?? 0);
        if ($providerId <= 0 && $method !== 'CHEQUE')                              return ['ok' => false, 'msg' => 'Provider (bank / mobile-money operator) required.'];
        $acctNo = trim((string)($d['account_number'] ?? ''));
        if ($acctNo === '' && $method !== 'CHEQUE')                                return ['ok' => false, 'msg' => 'Account / MSISDN required.'];
        $holder = trim((string)($d['account_holder_name'] ?? ''));
        if ($holder === '')                                                        return ['ok' => false, 'msg' => 'Account holder name required.'];
        $payeeId = (int)($d['payee_id'] ?? 0);
        if ($payeeId <= 0)                                                         return ['ok' => false, 'msg' => 'payee_id required.'];

        $db = new DB();
        $providerName = '';
        if ($providerId > 0) {
            $pv = $db->mySelect("SELECT provider_name FROM payment_providers WHERE c0d = :id", [':id' => $providerId]);
            $providerName = (string)($pv[0]['provider_name'] ?? '');
        }
        $r = $db->insertUpdate(
            "INSERT INTO payee_accounts
                (payee_type, payee_id, payee_name, method, provider_id, provider_name,
                 account_number, account_holder_name, branch_name, swift_code, status, notes,
                 created_by, created_by_name, edited_by)
             VALUES
                (:pt, :pid, :pn, :m, :prid, :prnm,
                 :acc, :hld, :br, :sw, 'PENDING', :nt,
                 :u, :un, :u)",
            [
                ':pt'=>$payeeType, ':pid'=>$payeeId, ':pn'=>trim((string)($d['payee_name'] ?? $holder)),
                ':m'=>$method, ':prid'=>$providerId ?: null, ':prnm'=>$providerName ?: null,
                ':acc'=>$acctNo, ':hld'=>$holder,
                ':br'=>trim((string)($d['branch_name'] ?? '')) ?: null,
                ':sw'=>trim((string)($d['swift_code'] ?? '')) ?: null,
                ':nt'=>trim((string)($d['notes'] ?? '')) ?: null,
                ':u'=>$userId, ':un'=>$userName,
            ]
        );
        $id = (int)$r['last_id'];
        if ($id <= 0) return ['ok' => false, 'msg' => 'Save failed.'];

        $this->auditLog($id, 'CREATE', $userId, $userName, 'Account created — pending approval.');

        // Submit approval chain — pick chain based on intended max amount vs settings threshold.
        try {
            if (!class_exists('FinanceSettings')) require_once ROOT . '/app/models/FinanceSettings.php';
            if (!class_exists('ApprovalChain'))   require_once ROOT . '/app/models/ApprovalChain.php';
            $threshold = (new FinanceSettings())->getFloat('account_exec_approval_threshold', 5000000);
            $chainKey = ($intendedMax > $threshold) ? 'PAYEE_ACCOUNT_APPROVAL_FM_EXEC' : 'PAYEE_ACCOUNT_APPROVAL_FM';
            $ref = $providerName . ' · ' . self::mask($acctNo) . ' · ' . $holder;
            $ac = new ApprovalChain();
            $sr = $ac->submit([
                'chain_key'    => $chainKey,
                'entity_type'  => 'payee_account',
                'entity_id'    => $id,
                'ref_label'    => $ref,
                'return_url'   => 'PayeeAccounts/index',
                'user_id'      => $userId,
                'user_name'    => $userName,
                'user_group'   => $_SESSION['user_group_name'] ?? '',
                'comment'      => 'Payee account submitted for approval (' . $payeeType . ' #' . $payeeId . ').',
            ]);
            if (!empty($sr['ok'])) {
                $db->insertUpdate(
                    "UPDATE payee_accounts SET approval_id = :aid, edited_on = NOW() WHERE c0d = :id",
                    [':aid'=>(int)$sr['approval_id'], ':id'=>$id]
                );
            }
        } catch (\Throwable $e) { /* best-effort */ }

        // Notify FM group so accounts don't sit unapproved.
        try {
            if (!class_exists('Notifications')) require_once ROOT . '/app/models/Notifications.php';
            (new Notifications())->notifyGroup(
                'Finance Manager', 'PAYEE_ACCOUNT_PENDING_FM',
                'Payee account awaiting approval',
                $providerName . ' · ' . self::mask($acctNo) . ' · ' . $holder . ' (' . $payeeType . ')',
                'PayeeAccounts/index', 'payee_account', $id, $userId, $userName
            );
        } catch (\Throwable $e) { /* best-effort */ }

        return ['ok' => true, 'id' => $id, 'msg' => 'Account submitted for approval.'];
    }

    /** FM/Exec approves via ApprovalChain. This method just flips status + logs. */
    public function markApproved(int $id, int $userId, string $userName): array
    {
        $db = new DB();
        $db->insertUpdate(
            "UPDATE payee_accounts SET status = 'APPROVED', approved_by = :u, approved_by_name = :un, approved_on = NOW(),
                                        edited_by = :u, edited_on = NOW() WHERE c0d = :id",
            [':u'=>$userId, ':un'=>$userName, ':id'=>$id]
        );
        $this->auditLog($id, 'APPROVE', $userId, $userName, 'Approved for use.');

        // Notify creator
        try {
            if (!class_exists('Notifications')) require_once ROOT . '/app/models/Notifications.php';
            $r = $db->mySelect("SELECT created_by, provider_name, account_number, payee_name FROM payee_accounts WHERE c0d = :id", [':id' => $id]);
            if (!empty($r) && (int)$r[0]['created_by'] > 0) {
                (new Notifications())->notify(
                    (int)$r[0]['created_by'], 'PAYEE_ACCOUNT_APPROVED',
                    'Payee account approved',
                    $r[0]['provider_name'] . ' · ' . self::mask((string)$r[0]['account_number']) . ' · ' . $r[0]['payee_name'],
                    'PayeeAccounts/my', 'payee_account', $id, $userId, $userName
                );
            }
        } catch (\Throwable $e) { /* best-effort */ }

        return ['ok' => true];
    }

    public function disable(int $id, int $userId, string $userName, string $reason): array
    {
        if (trim($reason) === '') return ['ok' => false, 'msg' => 'Reason required.'];
        $db = new DB();
        $db->insertUpdate(
            "UPDATE payee_accounts SET status = 'DISABLED', edited_by = :u, edited_on = NOW() WHERE c0d = :id",
            [':u'=>$userId, ':id'=>$id]
        );
        $this->auditLog($id, 'DISABLE', $userId, $userName, $reason);
        return ['ok' => true];
    }

    public function reactivate(int $id, int $userId, string $userName): array
    {
        $db = new DB();
        $db->insertUpdate(
            "UPDATE payee_accounts SET status = 'APPROVED', edited_by = :u, edited_on = NOW() WHERE c0d = :id AND status = 'DISABLED'",
            [':u'=>$userId, ':id'=>$id]
        );
        $this->auditLog($id, 'REACTIVATE', $userId, $userName, 'Account reactivated.');
        return ['ok' => true];
    }

    /** Record ad-hoc use (Accountant used a PENDING account with a mandatory reason). */
    public function logAdHocUse(int $id, int $lpoId, int $userId, string $userName, string $reason): void
    {
        $this->auditLog($id, 'USE_AD_HOC', $userId, $userName, $reason, $lpoId);
    }

    /** History for the accounts detail view. */
    public function auditFor(int $id): array
    {
        $db = new DB();
        return $db->mySelect(
            "SELECT * FROM payee_account_audit WHERE account_id = :id ORDER BY c0d DESC",
            [':id' => $id]
        ) ?: [];
    }

    private function auditLog(int $id, string $action, int $userId, string $userName, string $notes = '', int $lpoId = 0): void
    {
        $db = new DB();
        $db->insertUpdate(
            "INSERT INTO payee_account_audit (account_id, action, performed_by, performed_by_name, lpo_id, notes)
             VALUES (:a, :ac, :u, :un, :l, :n)",
            [':a'=>$id, ':ac'=>$action, ':u'=>$userId, ':un'=>$userName, ':l'=>$lpoId ?: null, ':n'=>trim($notes) ?: null]
        );
    }

    /** Show ***-***-1234 style — keeps the last 4. */
    public static function mask(string $s): string
    {
        $s = trim($s);
        $n = strlen($s);
        if ($n <= 4) return $s;
        return str_repeat('•', max(3, $n - 4)) . substr($s, -4);
    }
}
