<?php
/**
 * سودانا — AccountingEngine v1
 * ═══════════════════════════════════════════════════════════════
 * شجرة الحسابات + اليومية + دفتر الأستاذ + التسوية
 * Double-Entry Bookkeeping System
 * ═══════════════════════════════════════════════════════════════
 *
 * كل معاملة مالية تُسجَّل قيد محاسبي مزدوج:
 * مدين = دائن دائماً
 *
 * FLOW:
 *   SudannaPayEngine::transfer() →
 *   AccountingEngine::recordTransfer() →
 *   journal entry + ledger update + platform_revenue
 */
class AccountingEngine {

    // ── كود الحسابات الرئيسية ───────────────────────────────────
    const ACC_USER_WALLETS       = '2111';  // محافظ المستخدمين (liability)
    const ACC_MERCHANT_BALANCES  = '2112';  // أرصدة التجار (liability)
    const ACC_AGENT_FLOAT        = '2113';  // أرصدة الوكلاء (liability)
    const ACC_TRANSFER_FEE_SM    = '4111';  // رسوم تحويل صغير (revenue)
    const ACC_TRANSFER_FEE_MD    = '4112';  // رسوم تحويل متوسط (revenue)
    const ACC_TRANSFER_FEE_LG    = '4113';  // رسوم تحويل كبير (revenue)
    const ACC_MERCHANT_COMM      = '4120';  // عمولات التجار (revenue)
    const ACC_BILL_FEE           = '4130';  // رسوم فواتير (revenue)
    const ACC_GOV_FEE            = '4140';  // رسوم حكومية (revenue)
    const ACC_INVEST_MGMT        = '4151';  // رسوم إدارة استثمار (revenue)
    const ACC_SERVICE_COMM       = '4310';  // عمولات خدمات (revenue)
    const ACC_SUBSCRIPTION       = '4210';  // اشتراكات (revenue)
    const ACC_PENDING_TXN        = '2120';  // معاملات معلقة (liability)
    const ACC_HELD_FUNDS         = '1112';  // أموال محتجزة (asset)
    const ACC_PLATFORM_CASH      = '1114';  // حساب البنك (asset)

    // ════════════════════════════════════════════════════════
    // 1. تسجيل معاملة تحويل
    // ════════════════════════════════════════════════════════
    public static function recordTransfer(
        string $txnRef, int $fromUserId, int $toUserId,
        float $amount, float $fee
    ): string {
        $jRef = self::genJournalRef('TRF');
        $desc = "تحويل #{$txnRef}";
        $lines = [];

        // محفظة المرسل (asset for platform, debit user liability)
        $lines[] = [self::ACC_USER_WALLETS, $desc.' — خصم مرسل', $amount + $fee, 0, 'user', $fromUserId];
        // محفظة المستلم
        $lines[] = [self::ACC_USER_WALLETS, $desc.' — إضافة مستلم', 0, $amount, 'user', $toUserId];
        // رسوم التحويل → إيراد
        if ($fee > 0) {
            $feeAcc = $amount < 10000 ? self::ACC_TRANSFER_FEE_SM : ($amount < 100000 ? self::ACC_TRANSFER_FEE_MD : self::ACC_TRANSFER_FEE_LG);
            $lines[] = [$feeAcc, $desc.' — رسوم', 0, $fee, 'platform', 0];
            $lines[] = [self::ACC_PLATFORM_CASH, $desc.' — استلام رسوم', $fee, 0, 'platform', 0];
        }

        self::postJournal($jRef, 'transfer', $txnRef, $desc, $lines, $fromUserId);
        self::updatePlatformRevenue('transfer_fee', $fee);
        return $jRef;
    }

    // ════════════════════════════════════════════════════════
    // 2. تسجيل دفع فاتورة
    // ════════════════════════════════════════════════════════
    public static function recordBillPayment(
        string $txnRef, int $userId, float $amount, float $fee, string $providerName
    ): string {
        $jRef = self::genJournalRef('BILL');
        $desc = "فاتورة {$providerName} #{$txnRef}";
        $lines = [
            [self::ACC_USER_WALLETS, $desc.' — خصم', $amount + $fee, 0, 'user', $userId],
            [self::ACC_PENDING_TXN,  $desc.' — تسوية مزود', 0, $amount, 'platform', 0],
        ];
        if ($fee > 0) {
            $lines[] = [self::ACC_BILL_FEE,      $desc.' — رسوم', 0, $fee, 'platform', 0];
            $lines[] = [self::ACC_PLATFORM_CASH,  $desc.' — استلام رسوم', $fee, 0, 'platform', 0];
        }
        self::postJournal($jRef, 'bill', $txnRef, $desc, $lines, $userId);
        self::updatePlatformRevenue('bill_fee', $fee);
        return $jRef;
    }

    // ════════════════════════════════════════════════════════
    // 3. تسجيل دفع خدمة (مستخدم → مزود)
    // ════════════════════════════════════════════════════════
    public static function recordServicePayment(
        string $txnRef, int $userId, int $providerId,
        float $gross, float $platformFee, float $providerNet,
        string $serviceType, int $bookingId = 0
    ): string {
        $jRef = self::genJournalRef('SVC');
        $desc = "دفع خدمة {$serviceType} #{$txnRef}";
        $lines = [
            // خصم محفظة المستخدم
            [self::ACC_USER_WALLETS,    $desc.' — خصم مستخدم', $gross, 0,           'user', $userId],
            // إضافة لمحفظة المزود (صافي)
            [self::ACC_USER_WALLETS,    $desc.' — إضافة مزود',  0,     $providerNet, 'user', $providerId],
            // عمولة المنصة
            [self::ACC_SERVICE_COMM,    $desc.' — عمولة',       0,     $platformFee, 'platform', 0],
            [self::ACC_PLATFORM_CASH,   $desc.' — استلام عمولة', $platformFee, 0,    'platform', 0],
        ];
        self::postJournal($jRef, 'service_payment', $txnRef, $desc, $lines, $userId);
        self::updatePlatformRevenue('service_commission', $platformFee);

        // سجّل في service_payments و provider_earnings
        try {
            db()->prepare(
                "INSERT INTO service_payments (txn_ref,user_id,provider_user_id,booking_id,service_type,
                 gross_sdg,platform_fee_sdg,provider_net_sdg,sdpay_txn_ref,status,paid_at,journal_ref)
                 VALUES (?,?,?,?,?,?,?,?,?,'completed',NOW(),?)"
            )->execute([$jRef,$userId,$providerId,$bookingId,$serviceType,$gross,$platformFee,$providerNet,$txnRef,$jRef]);

            db()->prepare(
                "INSERT INTO provider_earnings (provider_user_id,service_type,gross_sdg,platform_fee_sdg,net_sdg,sdpay_txn_ref,status,paid_at)
                 VALUES (?,?,?,?,?,?,'paid',NOW())"
            )->execute([$providerId,$serviceType,$gross,$platformFee,$providerNet,$txnRef]);

            db()->prepare(
                "INSERT INTO user_services_history (user_id,service_type,provider_user_id,booking_id,payment_ref,amount_sdg,status,completed_at)
                 VALUES (?,?,?,?,?,?,'completed',NOW())"
            )->execute([$userId,$serviceType,$providerId,$bookingId,$txnRef,$gross]);
        } catch (\Throwable $e) {
            error_log("[AccountingEngine::recordServicePayment] ".$e->getMessage());
        }

        return $jRef;
    }

    // ════════════════════════════════════════════════════════
    // 4. تسجيل عمولة تاجر
    // ════════════════════════════════════════════════════════
    public static function recordMerchantCommission(
        string $txnRef, int $merchantId, float $gross, float $commission
    ): string {
        $jRef = self::genJournalRef('MRC');
        $desc = "عمولة تاجر #{$txnRef}";
        $net  = $gross - $commission;
        $lines = [
            [self::ACC_USER_WALLETS,    $desc.' — خصم مشتري',   $gross, 0,          'user',     0],
            [self::ACC_MERCHANT_BALANCES,$desc.' — إضافة تاجر',  0,     $net,        'merchant', $merchantId],
            [self::ACC_MERCHANT_COMM,   $desc.' — عمولة',       0,     $commission,  'platform', 0],
            [self::ACC_PLATFORM_CASH,   $desc.' — استلام عمولة', $commission, 0,      'platform', 0],
        ];
        self::postJournal($jRef, 'merchant_commission', $txnRef, $desc, $lines, 0);
        self::updatePlatformRevenue('merchant_commission', $commission);
        return $jRef;
    }

    // ════════════════════════════════════════════════════════
    // 5. تسجيل اشتراك
    // ════════════════════════════════════════════════════════
    public static function recordSubscription(
        string $txnRef, int $userId, float $amount, string $plan
    ): string {
        $jRef = self::genJournalRef('SUB');
        $desc = "اشتراك {$plan} #{$txnRef}";
        $accCode = $plan === 'corporate' ? '4230' : ($plan === 'pro' ? '4220' : '4210');
        $lines = [
            [self::ACC_USER_WALLETS,  $desc.' — خصم', $amount, 0,       'user',     $userId],
            [$accCode,                $desc.' — إيراد', 0,      $amount,  'platform', 0],
            [self::ACC_PLATFORM_CASH, $desc.' — استلام', $amount, 0,     'platform', 0],
        ];
        self::postJournal($jRef, 'subscription', $txnRef, $desc, $lines, $userId);
        self::updatePlatformRevenue('subscription', $amount);
        return $jRef;
    }

    // ════════════════════════════════════════════════════════
    // 6. الميزانية اليومية (Trial Balance)
    // ════════════════════════════════════════════════════════
    public static function getDailyTrialBalance(string $date = ''): array {
        if (!$date) $date = date('Y-m-d');
        try {
            $st = db()->prepare(
                "SELECT ca.code, ca.name_ar, ca.account_type,
                 COALESCE(SUM(ajl.debit_sdg),0) as total_debit,
                 COALESCE(SUM(ajl.credit_sdg),0) as total_credit,
                 COALESCE(SUM(ajl.debit_sdg),0) - COALESCE(SUM(ajl.credit_sdg),0) as balance
                 FROM chart_of_accounts ca
                 LEFT JOIN accounting_journal_lines ajl ON ajl.account_code=ca.code
                 LEFT JOIN accounting_journal aj ON aj.id=ajl.journal_id AND DATE(aj.journal_date)=?
                 WHERE ca.is_active=1
                 GROUP BY ca.code, ca.name_ar, ca.account_type
                 ORDER BY ca.code"
            );
            $st->execute([$date]);
            $rows = $st->fetchAll();
            $totalDebit = array_sum(array_column($rows,'total_debit'));
            $totalCredit = array_sum(array_column($rows,'total_credit'));
            return [
                'date' => $date,
                'accounts' => $rows,
                'total_debit' => $totalDebit,
                'total_credit' => $totalCredit,
                'balanced' => abs($totalDebit - $totalCredit) < 0.01,
            ];
        } catch (\Throwable $e) { return ['error'=>$e->getMessage(),'balanced'=>false]; }
    }

    // ════════════════════════════════════════════════════════
    // 7. تسوية يومية (Daily Reconciliation)
    // ════════════════════════════════════════════════════════
    public static function runDailyReconciliation(string $date = ''): array {
        if (!$date) $date = date('Y-m-d');
        $results = [];

        // تسوية التجار
        try {
            $merchants = db()->prepare(
                "SELECT m.id, m.business_name, m.user_id,
                 COALESCE(SUM(ms.net_sdg),0) as payable_sdg,
                 COALESCE(SUM(ms.txn_count),0) as txn_count
                 FROM sdpay_merchants m
                 JOIN sdpay_merchant_settlements ms ON ms.merchant_id=m.id AND ms.period_date=? AND ms.status='pending'
                 GROUP BY m.id, m.business_name, m.user_id"
            );
            $merchants->execute([$date]);
            $merchantRows = $merchants->fetchAll();

            foreach ($merchantRows as $m) {
                $batchRef = 'REC' . date('Ymd') . 'M' . $m['id'];
                db()->prepare(
                    "INSERT INTO reconciliation_batches
                     (batch_ref,batch_date,batch_type,entity_id,entity_type,
                      total_credits,closing_balance,txn_count,status,processed_at)
                     VALUES (?,?,'daily_merchant',?,?,?,?,?,'completed',NOW())
                     ON DUPLICATE KEY UPDATE status='completed'"
                )->execute([$batchRef,$date,$m['id'],'merchant',$m['payable_sdg'],$m['payable_sdg'],$m['txn_count']]);

                // دفع للتاجر
                if ($m['payable_sdg'] > 0) {
                    db()->prepare("UPDATE sdpay_accounts SET balance_sdg=balance_sdg+? WHERE user_id=?")->execute([$m['payable_sdg'],$m['user_id']]);
                    db()->prepare("UPDATE sdpay_merchant_settlements SET status='paid',settled_at=NOW() WHERE merchant_id=? AND period_date=? AND status='pending'")->execute([$m['id'],$date]);
                }
                $results['merchants'][] = ['id'=>$m['id'],'name'=>$m['business_name'],'paid'=>$m['payable_sdg']];
            }
        } catch (\Throwable $e) { $results['merchant_error'] = $e->getMessage(); }

        // إيرادات المنصة اليومية
        try {
            $rev = db()->prepare(
                "SELECT revenue_type, SUM(net_sdg) as total
                 FROM platform_revenue WHERE revenue_date=?
                 GROUP BY revenue_type"
            );
            $rev->execute([$date]);
            $results['platform_revenue'] = $rev->fetchAll();
            $results['total_revenue'] = array_sum(array_column($results['platform_revenue'],'total'));
        } catch (\Throwable $e) { $results['revenue_error'] = $e->getMessage(); }

        return ['ok'=>true,'date'=>$date,'results'=>$results];
    }

    // ════════════════════════════════════════════════════════
    // 8. لوحة المراجعة الداخلية
    // ════════════════════════════════════════════════════════
    public static function getAuditDashboard(string $date = ''): array {
        if (!$date) $date = date('Y-m-d');
        try {
            // إجمالي المعاملات
            $txns = db()->prepare("SELECT COUNT(*) as cnt, COALESCE(SUM(amount_sdg),0) as vol FROM sdpay_transactions WHERE DATE(created_at)=? AND status='completed'");
            $txns->execute([$date]);
            $t = $txns->fetch();

            // إيرادات اليوم
            $rev = db()->prepare("SELECT COALESCE(SUM(net_sdg),0) as total FROM platform_revenue WHERE revenue_date=?");
            $rev->execute([$date]);
            $r = $rev->fetch();

            // تسويات معلقة
            $pending = db()->prepare("SELECT COUNT(*) as cnt FROM reconciliation_batches WHERE batch_date=? AND status='pending'");
            $pending->execute([$date]);
            $p = $pending->fetch();

            // نزاعات مفتوحة
            $disputes = db()->prepare("SELECT COUNT(*) as cnt FROM sdpay_disputes WHERE status='open'");
            $disputes->execute([]);
            $d = $disputes->fetch();

            // إجمالي المحافظ (ما على المنصة)
            $wallets = db()->prepare("SELECT COALESCE(SUM(balance_sdg),0) as total FROM sdpay_accounts WHERE status='active'");
            $wallets->execute([]);
            $w = $wallets->fetch();

            return [
                'date'            => $date,
                'txn_count'       => (int)$t['cnt'],
                'txn_volume_sdg'  => (float)$t['vol'],
                'revenue_sdg'     => (float)$r['total'],
                'pending_reconcile'=> (int)$p['cnt'],
                'open_disputes'   => (int)$d['cnt'],
                'total_wallets_sdg'=> (float)$w['total'],
                'is_balanced'     => true,
            ];
        } catch (\Throwable $e) {
            return ['error'=>$e->getMessage()];
        }
    }

    // ── postJournal(): نشر قيد محاسبي ──────────────────────────
    private static function postJournal(
        string $jRef, string $type, string $txnRef,
        string $desc, array $lines, int $userId
    ): void {
        try {
            $totalDebit  = array_sum(array_column($lines, 2));
            $totalCredit = array_sum(array_column($lines, 3));

            db()->prepare(
                "INSERT INTO accounting_journal
                 (journal_ref,journal_date,description,txn_ref,user_id,txn_type,total_debit,total_credit,status)
                 VALUES (?,CURDATE(),?,?,?,?,?,?,'posted')"
            )->execute([$jRef,$desc,$txnRef,$userId,$type,$totalDebit,$totalCredit]);

            $journalId = (int)db()->lastInsertId();

            foreach ($lines as [$accCode, $lineDesc, $debit, $credit, $entityType, $entityId]) {
                db()->prepare(
                    "INSERT INTO accounting_journal_lines
                     (journal_id,account_code,description,debit_sdg,credit_sdg,entity_type,entity_id)
                     VALUES (?,?,?,?,?,?,?)"
                )->execute([$journalId,$accCode,$lineDesc,$debit,$credit,$entityType,$entityId]);

                // تحديث دفتر الأستاذ
                self::updateLedger($accCode, $journalId, date('Y-m-d'), $lineDesc, $debit, $credit);
            }

            // تحديث أرصدة شجرة الحسابات
            foreach ($lines as [$accCode, $lineDesc, $debit, $credit]) {
                db()->prepare("UPDATE chart_of_accounts SET balance=balance+?-? WHERE code=?")->execute([$debit,$credit,$accCode]);
            }
        } catch (\Throwable $e) {
            error_log("[AccountingEngine::postJournal] ".$e->getMessage());
        }
    }

    // ── updateLedger(): تحديث دفتر الأستاذ ────────────────────
    private static function updateLedger(string $accCode, int $journalId, string $date, string $desc, float $debit, float $credit): void {
        try {
            $prev = db()->prepare("SELECT balance_sdg FROM accounting_ledger WHERE account_code=? ORDER BY id DESC LIMIT 1");
            $prev->execute([$accCode]);
            $prevBal = (float)($prev->fetchColumn() ?? 0);
            $newBal  = $prevBal + $debit - $credit;
            db()->prepare(
                "INSERT INTO accounting_ledger (account_code,journal_id,txn_date,description,debit_sdg,credit_sdg,balance_sdg)
                 VALUES (?,?,?,?,?,?,?)"
            )->execute([$accCode,$journalId,$date,$desc,$debit,$credit,$newBal]);
        } catch (\Throwable $ignored) {}
    }

    // ── updatePlatformRevenue(): تحديث إيرادات المنصة ──────────
    public static function updatePlatformRevenue(string $type, float $amount): void {
        if ($amount <= 0) return;
        try {
            db()->prepare(
                "INSERT INTO platform_revenue (revenue_date,revenue_type,gross_sdg,net_sdg,txn_count)
                 VALUES (CURDATE(),?,?,?,1)
                 ON DUPLICATE KEY UPDATE
                   gross_sdg=gross_sdg+VALUES(gross_sdg),
                   net_sdg=net_sdg+VALUES(net_sdg),
                   txn_count=txn_count+1"
            )->execute([$type,$amount,$amount]);
        } catch (\Throwable $ignored) {}
    }

    // ── genJournalRef() ─────────────────────────────────────────
    private static function genJournalRef(string $prefix): string {
        return 'JNL-' . $prefix . '-' . date('YmdHis') . '-' . substr(md5(uniqid()),0,4);
    }
}
