<?php
/**
 * سودانا — SettlementEngine v1
 * Provider earnings → reconciliation check → payout
 * RULE: NO payout before reconciliation passes
 * RULE: DEBIT_ONLY — never extend credit
 */
class SettlementEngine {

    /**
     * Process all pending settlements for a date
     * Called by cron daily AFTER reconciliation
     */
    public static function processDate(string $date = null): array {
        $date    = $date ?: date('Y-m-d', strtotime('yesterday'));
        $traceId = uniqid('set_', true);

        require_once __DIR__ . '/ReconciliationEngine.php';
        require_once __DIR__ . '/BankAdapter.php';

        // ── GATE: Reconciliation MUST pass ─────────────────────────
        if (!ReconciliationEngine::isPassed($date)) {
            error_log("[SettlementEngine] BLOCKED: recon not passed for $date");
            return [
                'success' => false,
                'blocked' => true,
                'reason'  => 'reconciliation_not_passed',
                'date'    => $date,
                'trace_id'=> $traceId,
            ];
        }

        // ── Fetch pending provider earnings ─────────────────────────
        try {
            $earningsQ = db()->prepare(
                "SELECT pe.*, u.name, u.email,
                        u.provider_bank_account, u.provider_bank_name
                 FROM provider_earnings pe
                 JOIN users u ON u.id = pe.provider_id
                 WHERE pe.status = 'pending'
                   AND DATE(pe.created_at) = ?
                   AND pe.net_sdg > 0
                 FOR UPDATE"
            );
            $earningsQ->execute([$date]);
            $earnings = $earningsQ->fetchAll(PDO::FETCH_ASSOC);

            if (empty($earnings)) {
                return [
                    'success'  => true,
                    'message'  => 'No pending settlements',
                    'date'     => $date,
                    'trace_id' => $traceId,
                ];
            }

            $paid = 0; $failed = 0; $results = [];

            foreach ($earnings as $e) {
                $result = self::settleOne($e, $traceId);
                if ($result['success']) $paid++;
                else $failed++;
                $results[] = $result;
            }

            return [
                'success'  => $failed === 0,
                'date'     => $date,
                'total'    => count($earnings),
                'paid'     => $paid,
                'failed'   => $failed,
                'results'  => $results,
                'trace_id' => $traceId,
            ];

        } catch (Throwable $e) {
            error_log("[SettlementEngine] " . $e->getMessage());
            return ['success'=>false, 'error'=>$e->getMessage(), 'trace_id'=>$traceId];
        }
    }

    private static function settleOne(array $earning, string $traceId): array {
        $providerId = (int)$earning['provider_id'];
        $amount     = (float)$earning['net_sdg'];

        // Validate bank account exists
        if (empty($earning['provider_bank_account'])) {
            self::markFailed($earning['id'], 'no_bank_account');
            return ['success'=>false, 'provider_id'=>$providerId,
                    'reason'=>'no_bank_account', 'amount'=>$amount];
        }

        // Initiate bank payout
        $payout = BankAdapter::initiatePayout([
            'provider_id'            => $providerId,
            'provider_bank_account'  => $earning['provider_bank_account'],
            'amount'                 => $amount,
            'reference'              => "SETTLE-{$earning['id']}-{$traceId}",
        ]);

        if ($payout['success'] ?? false) {
            // Mark settled
            db()->prepare(
                "UPDATE provider_earnings SET status='settled', settled_at=NOW(),
                 bank_txn_id=? WHERE id=?"
            )->execute([$payout['bank_txn_id'], $earning['id']]);

            // Ledger: record settlement
            self::ledgerSettle($earning, $payout['bank_txn_id'] ?? $traceId);

            // Notify provider
            self::notifyProvider($earning, $amount);

            return ['success'=>true, 'provider_id'=>$providerId, 'amount'=>$amount,
                    'bank_txn_id'=>$payout['bank_txn_id']];
        } else {
            self::markFailed($earning['id'], $payout['error'] ?? 'bank_error');
            return ['success'=>false, 'provider_id'=>$providerId,
                    'error'=>$payout['error'] ?? 'bank_error', 'amount'=>$amount];
        }
    }

    private static function markFailed(int $earningId, string $reason): void {
        try {
            db()->prepare(
                "UPDATE provider_earnings SET status='failed', notes=? WHERE id=?"
            )->execute([$reason, $earningId]);
        } catch (Throwable $e) { error_log("[SettlementEngine::fail] " . $e->getMessage()); }
    }

    private static function ledgerSettle(array $earning, string $bankTxnId): void {
        try {
            // DEBIT UserLiability(2002) CREDIT Cash(1001) — money leaves Sudanna
            $acct2002 = db()->query("SELECT id FROM accounts WHERE code='2002'")->fetchColumn();
            $acct1001 = db()->query("SELECT id FROM accounts WHERE code='1001'")->fetchColumn();
            $amount   = (float)$earning['net_sdg'];

            db()->prepare(
                "INSERT INTO ledger (account_id,debit,credit,reference_type,reference_id,description,created_at)
                 VALUES (?,?,0,'settlement',?,?,NOW())"
            )->execute([$acct2002, $amount, $earning['id'], "تسوية مزود #{$earning['provider_id']}"]);

            db()->prepare(
                "INSERT INTO ledger (account_id,debit,credit,reference_type,reference_id,description,created_at)
                 VALUES (?,0,?,?,?,NOW())"
            )->execute([$acct1001, $amount, 'settlement', $earning['id']]);

        } catch (Throwable $e) { error_log("[SettlementEngine::ledger] " . $e->getMessage()); }
    }

    private static function notifyProvider(array $earning, float $amount): void {
        try {
            db()->prepare(
                "INSERT INTO notifications (user_id,type,title,body,data,created_at)
                 VALUES (?,'settlement_paid','💰 تم تحويل أرباحك',?,?,NOW())"
            )->execute([$earning['provider_id'],
                "تم تحويل {$amount} جنيه لحسابك البنكي.",
                json_encode(['amount'=>$amount,'earning_id'=>$earning['id']], JSON_UNESCAPED_UNICODE)
            ]);
        } catch (Throwable $e) { error_log("[SettlementEngine::notify] " . $e->getMessage()); }
    }

    // ── batch(): معالجة دُفعة تسوية ─────────────────────────
    public static function batch(string $date = ''): array {
        require_once __DIR__ . '/AccountingEngine.php';
        return AccountingEngine::runDailyReconciliation($date ?: date('Y-m-d'));
    }

    // ── reconcile(): تسوية حسابات ────────────────────────────
    public static function reconcile(string $entityType, int $entityId, string $date = ''): array {
        if (!$date) $date = date('Y-m-d');
        try {
            $batchRef = 'STL' . date('Ymd') . strtoupper($entityType[0]) . $entityId;
            $st = db()->prepare(
                "SELECT COALESCE(SUM(amount_sdg),0) as vol, COUNT(*) as cnt
                 FROM sdpay_transactions
                 WHERE DATE(created_at)=? AND status='completed'"
            );
            $st->execute([$date]);
            $stats = $st->fetch();
            db()->prepare(
                "INSERT INTO reconciliation_batches (batch_ref,batch_date,batch_type,entity_id,entity_type,total_credits,txn_count,status,processed_at)
                 VALUES (?,?,'daily_platform',?,?,?,?,'completed',NOW())
                 ON DUPLICATE KEY UPDATE status='completed'"
            )->execute([$batchRef,$date,$entityId,$entityType,$stats['vol'],$stats['cnt']]);
            return ['ok'=>true,'batch_ref'=>$batchRef,'volume'=>$stats['vol'],'count'=>$stats['cnt']];
        } catch (\Throwable $e) { return ['ok'=>false,'error'=>$e->getMessage()]; }
    }

}
