<?php
/**
 * سودانا — InvoiceEngine v1
 * SECTION 5: Offline Payment Invoice-Based Flow
 * ✔ No execution before payment
 * ✔ Idempotency via reference_code
 * ✔ No duplicate receipts
 */
class InvoiceEngine {

    const BANK_ACCOUNT = '1821425';
    const BANK_NAME    = 'بنك الخرطوم';
    const EXPIRE_HOURS = 48;

    /**
     * Create invoice — User requests service payment
     * @param int    $userId
     * @param string $service
     * @param float  $amount
     * @param array  $metadata
     * @return array
     */
    public static function create(int $userId, string $service, float $amount, array $metadata = []): array {
        try {
            // Idempotency: generate unique reference
            $refCode = self::generateRef($userId, $service, $amount);

            // Check if duplicate exists (idempotent)
            $existing = db()->prepare("SELECT id, status FROM invoices WHERE reference_code=?");
            $existing->execute([$refCode]);
            $found = $existing->fetch(PDO::FETCH_ASSOC);

            if ($found) {
                if ($found['status'] === 'paid') {
                    return ['success'=>false, 'error'=>'Invoice already paid', 'invoice_id'=>$found['id']];
                }
                // ── FIX 4: Don't reuse expired invoice ────────────────────
                if (!empty($found['expires_at']) && strtotime($found['expires_at']) < time()) {
                    // Don't reuse — fall through to create a new one
                    $refCode = self::generateRef($userId, $service, $amount);
                } else {
                    return ['success'=>true, 'invoice_id'=>$found['id'], 'reference_code'=>$refCode,
                            'reused'=>true, 'message'=>'Using existing pending invoice'];
                }
            }

            $expiresAt = date('Y-m-d H:i:s', strtotime('+' . self::EXPIRE_HOURS . ' hours'));

            // ── AUTO-INIT WALLET (ensures wallet exists before payment) ─────
            try {
                db()->prepare(
                    "INSERT IGNORE INTO wallet (user_id, balance_sdg, status, created_at)
                     VALUES (?, 0.00, 'active', NOW())"
                )->execute([$userId]);
            } catch (Throwable $we) {
                error_log("[InvoiceEngine::wallet_init] " . $we->getMessage());
                // Not fatal — continue
            }

            $stmt = db()->prepare(
                "INSERT INTO invoices (user_id, service, amount, amount_sdg, reference_code, status, bank_account, notes, created_at, expires_at)
                 VALUES (?,?,?,?,?,'pending',?,?,NOW(),?)"
            );
            $stmt->execute([
                $userId, $service, $amount, $refCode,
                self::BANK_ACCOUNT,
                json_encode($metadata, JSON_UNESCAPED_UNICODE),
                $expiresAt,
            ]);
            $invoiceId = (int)db()->lastInsertId();

            return [
                'success'        => true,
                'invoice_id'     => $invoiceId,
                'reference_code' => $refCode,
                'amount'         => $amount,
                'bank_account'   => self::BANK_ACCOUNT,
                'bank_name'      => self::BANK_NAME,
                'expires_at'     => $expiresAt,
                'instructions'   => [
                    "حوّل {$amount} جنيه إلى " . self::BANK_NAME . " حساب " . self::BANK_ACCOUNT,
                    "استخدم الرقم المرجعي: {$refCode}",
                    "ارفع صورة الإيصال في التطبيق",
                    "ستتلقى تأكيداً خلال ساعتين",
                ],
            ];
        } catch (Throwable $e) {
            error_log("[InvoiceEngine::create] " . $e->getMessage());
            return ['success'=>false, 'error'=>$e->getMessage()];
        }
    }

    /**
     * Upload payment proof
     * ✔ idempotency: UNIQUE invoice_id
     * ✔ no duplicate: file_hash
     */
    public static function uploadProof(int $invoiceId, string $imagePath, string $fileHash): array {
        try {
            // Check invoice exists and is pending
            $inv = db()->prepare("SELECT * FROM invoices WHERE id=? AND status NOT IN ('paid','refunded')");
            $inv->execute([$invoiceId]);
            $invoice = $inv->fetch(PDO::FETCH_ASSOC);

            if (!$invoice) {
                return ['success'=>false, 'error'=>'Invoice not found or not in pending status'];
            }

            // ── FIX 4: Expiry check ────────────────────────────────────────
            if (!empty($invoice['expires_at']) && strtotime($invoice['expires_at']) < time()) {
                // Mark as expired
                db()->prepare("UPDATE invoices SET status='rejected', notes=CONCAT(IFNULL(notes,''),' [EXPIRED]') WHERE id=?")
                    ->execute([$invoiceId]);
                return ['success'=>false, 'error'=>'invoice_expired',
                        'expired_at' => $invoice['expires_at'],
                        'message'    => 'انتهت صلاحية الفاتورة — يرجى إنشاء فاتورة جديدة'];
            }

            // Check for duplicate file hash (same image uploaded twice)
            $dupHash = db()->prepare("SELECT id FROM payment_proofs WHERE file_hash=?");
            $dupHash->execute([$fileHash]);
            if ($dupHash->fetchColumn()) {
                return ['success'=>false, 'error'=>'Duplicate proof image detected', 'code'=>'DUPLICATE_PROOF'];
            }

            // Insert proof (UNIQUE invoice_id prevents second upload)
            try {
                db()->prepare(
                    "INSERT INTO payment_proofs (invoice_id, image_path, file_hash, status, uploaded_at)
                     VALUES (?,?,?,'pending',NOW())"
                )->execute([$invoiceId, $imagePath, $fileHash]);
            } catch (Throwable $e) {
                if (str_contains($e->getMessage(), 'Duplicate entry')) {
                    return ['success'=>false, 'error'=>'Proof already uploaded for this invoice'];
                }
                throw $e;
            }

            // Update invoice status
            db()->prepare("UPDATE invoices SET status='proof_uploaded' WHERE id=?")
                ->execute([$invoiceId]);

            // Notify admin
            db()->prepare(
                "INSERT INTO notifications (user_id, type, title, body, data, created_at)
                 VALUES (1, 'payment_proof_pending', '🧾 إيصال دفع جديد',
                 'فاتورة #{$invoiceId} — يرجى المراجعة والتأكيد', ?, NOW())"
            )->execute([json_encode(['invoice_id'=>$invoiceId,'user_id'=>$invoice['user_id']], JSON_UNESCAPED_UNICODE)]);

            return [
                'success'    => true,
                'invoice_id' => $invoiceId,
                'status'     => 'proof_uploaded',
                'message'    => 'تم رفع الإيصال بنجاح — سيتم التحقق خلال ساعتين',
            ];
        } catch (Throwable $e) {
            error_log("[InvoiceEngine::uploadProof] " . $e->getMessage());
            return ['success'=>false, 'error'=>$e->getMessage()];
        }
    }

    /**
     * Admin approves — triggers FinancialAgent::confirmPayment
     */
    public static function approve(int $invoiceId, int $adminId): array {
        require_once __DIR__ . '/FinancialAgent.php';
        require_once __DIR__ . '/EventBus.php';

        db()->prepare("UPDATE invoices SET status='under_review' WHERE id=? AND status='proof_uploaded'")
            ->execute([$invoiceId]);

        return FinancialAgent::confirmPayment($invoiceId, $adminId);
    }

    /**
     * Admin rejects proof
     */
    public static function reject(int $invoiceId, int $adminId, string $reason = ''): array {
        db()->prepare("UPDATE invoices SET status='rejected' WHERE id=?")->execute([$invoiceId]);
        db()->prepare("UPDATE payment_proofs SET status='rejected', reviewed_by=?, reviewed_at=NOW() WHERE invoice_id=?")
            ->execute([$adminId, $invoiceId]);

        $inv = db()->prepare("SELECT user_id FROM invoices WHERE id=?");
        $inv->execute([$invoiceId]);
        $userId = (int)$inv->fetchColumn();

        if ($userId) {
            db()->prepare(
                "INSERT INTO notifications (user_id, type, title, body, data, created_at)
                 VALUES (?, 'payment_rejected', '❌ الإيصال مرفوض', ?, ?, NOW())"
            )->execute([$userId,
                $reason ?: 'الإيصال غير واضح أو غير صحيح — يرجى إعادة الرفع',
                json_encode(['invoice_id'=>$invoiceId], JSON_UNESCAPED_UNICODE)
            ]);
        }

        return ['success'=>true, 'invoice_id'=>$invoiceId, 'status'=>'rejected'];
    }

    // ── Private helpers ─────────────────────────────────────────────

    private static function generateRef(int $userId, string $service, float $amount): string {
        // Format: SD-USERID-SERVICE-YYYYMMDD-RANDOM
        return 'SD-' . $userId . '-' . strtoupper(substr($service,0,3)) .
               '-' . date('Ymd') . '-' . strtoupper(substr(md5(uniqid()),0,6));
    }
}
