<?php

namespace App\Controllers;

use CodeIgniter\RESTful\ResourceController;

class TransactionChecker extends ResourceController
{
    protected $format = 'json';
    protected $maxAttempts = 3;

    /**
     * Check a specific transaction's status
     */
    public function checkTransaction($transactionId = null)
    {
        if (!$transactionId) {
            return $this->fail('Transaction ID is required', 400);
        }

        $result = $this->performStatusCheck($transactionId);
        return $this->respond($result);
    }

    /**
     * Process all pending transactions
     */
    public function processPendingTransactions()
    {
        $txnModel = new \App\Models\TransactionModel();

        // Find pending transactions (status = 0 or 'pending'; process() inserts 'pending')
        $pendingTransactions = $txnModel->whereIn('status', [0, '0', 'pending'])
            ->orderBy('created_at', 'ASC')
            ->limit(3)
            ->findAll();

        $results = [];

        foreach ($pendingTransactions as $transaction) {
            // We'll track attempts using updated_at timestamp instead of attempt_count
            $results[$transaction['transaction_id']] = $this->performStatusCheck(
                $transaction['transaction_id']
            );
        }

        return $this->respond([
            'success' => true,
            'total_processed' => count($results),
            'results' => $results,
            'timestamp' => date('Y-m-d H:i:s')
        ]);
    }

    /**
     * Performs the status check for a transaction
     */
    private function performStatusCheck($transactionId)
    {
        $client = \Config\Services::curlrequest();
        $txnModel = new \App\Models\TransactionModel();

        try {
            // Get transaction from database
            $transaction = $txnModel->where('transaction_id', $transactionId)->first();

            if (!$transaction) {
                log_message('error', "Transaction {$transactionId} not found");
                return [
                    'success' => false,
                    'transaction_id' => $transactionId,
                    'error' => 'Transaction not found'
                ];
            }

            // Skip if already processed (success or failed); only poll MM when still pending
            $s = $transaction['status'];
            if (in_array($s, [1, 2, '1', '2', 'success', 'failed'], true)) {
                $statusText = $this->getStatusText($s);
                return [
                    'success' => true,
                    'transaction_id' => $transactionId,
                    'status' => $statusText,
                    'message' => 'Already processed',
                ];
            }

            // GET https://mm.finflo.net/public/api/mobile-money/status/{reference}
            // Response: { "success": true, "transaction": { "reference": "...", "status": "SUCCESS"|"PENDING"|"FAILE", ... } }
            $statusUrl = config('MobileMoney')->getStatusUrl($transactionId);
            $response = $client->get($statusUrl, [
                'headers' => ['Content-Type' => 'application/json'],
                'verify' => false,
                'timeout' => 15,
            ]);

            if ($response->getStatusCode() != 200) {
                log_message('error', "TransactionChecker: API returned status code {$response->getStatusCode()} for {$transactionId}");
                throw new \RuntimeException("API returned status code: " . $response->getStatusCode());
            }

            $responseData = json_decode($response->getBody(), true);
            log_message('debug', "TransactionChecker: Status API response for {$transactionId}: " . json_encode($responseData));
            
            // Validate response structure - new API format
            if (!isset($responseData['transaction']) || !isset($responseData['transaction']['status'])) {
                log_message('error', "TransactionChecker: Invalid API response format for {$transactionId}: " . json_encode($responseData));
                throw new \RuntimeException("Invalid API response format");
            }

            $apiStatus = strtoupper((string) ($responseData['transaction']['status'] ?? ''));
            $validStatuses = ['SUCCESS', 'PENDING', 'FAILED', 'FAILE']; // MM API returns "FAILE" not "FAILED"
            if (!in_array($apiStatus, $validStatuses)) {
                throw new \RuntimeException("Invalid transaction status: {$apiStatus}");
            }

            // Calculate attempts based on time since creation
            $createdAt = strtotime($transaction['created_at']);
            $now = time();
            $hoursSinceCreation = round(($now - $createdAt) / 3600, 1);
            $attempts = min(ceil($hoursSinceCreation / 4), $this->maxAttempts); // Assuming cron runs every 4 hours

            // Prepare update data
            $updateData = [
                'updated_at' => date('Y-m-d H:i:s'),
            ];

            // Handle status cases: SUCCESS, FAILE (or FAILED), PENDING
            if ($apiStatus === 'SUCCESS') {
                $updateData['status'] = 1;
                $this->processSuccessActions($transaction, $transactionId);
            } elseif (in_array($apiStatus, ['FAILED', 'FAILE']) || ($apiStatus === 'PENDING' && $attempts >= $this->maxAttempts)) {
                $updateData['status'] = 2; // Mark as failed
            }

            // Update transaction if needed
            if (isset($updateData['status'])) {
                if (!$txnModel->update($transaction['id'], $updateData)) {
                    throw new \RuntimeException("Database update failed");
                }
            }

            return [
                'success' => true,
                'transaction_id' => $transactionId,
                'status' => $apiStatus,
                'attempt' => $attempts,
                'database_updated' => isset($updateData['status'])
            ];

        } catch (\Exception $e) {
            log_message('error', "Transaction {$transactionId} check failed: " . $e->getMessage());

            // Calculate attempts for error response
            $attempts = 1;
            if (isset($transaction['created_at'])) {
                $createdAt = strtotime($transaction['created_at']);
                $now = time();
                $hoursSinceCreation = round(($now - $createdAt) / 3600, 1);
                $attempts = min(ceil($hoursSinceCreation / 4), $this->maxAttempts);
            }

            // Final failure after max attempts
            if ($attempts >= $this->maxAttempts) {
                $txnModel->update($transaction['id'] ?? null, [
                    'status' => 2,
                    'updated_at' => date('Y-m-d H:i:s')
                ]);
                return [
                    'success' => false,
                    'transaction_id' => $transactionId,
                    'error' => $e->getMessage(),
                    'attempt' => $attempts,
                    'message' => 'Max attempts reached - marked as failed'
                ];
            }

            return [
                'success' => false,
                'transaction_id' => $transactionId,
                'error' => $e->getMessage(),
                'attempt' => $attempts,
                'message' => 'Will retry'
            ];
        }
    }

    /**
     * Process success actions (tips and subscriptions)
     */
    private function processSuccessActions(array $transaction, string $transactionId): void
    {
        $txnModel = new \App\Models\TransactionModel();

        // Handle tip unlock
        if (!empty($transaction['tip_id'])) {
            $unlockModel = new \App\Models\TipsUnlockModel();
            if (!$unlockModel->where('transaction_id', $transactionId)->first()) {
                $unlockData = [
                    'msisdn' => $transaction['msisdn'],
                    'tip_id' => $transaction['tip_id'],
                    'transaction_id' => $transactionId,
                    'unlock_status' => 1,
                    'created_at' => date('Y-m-d H:i:s')
                ];
                if (!$unlockModel->insert($unlockData)) {
                    log_message('error', "Tip unlock failed for {$transactionId}");
                }
            }
            // --- Reconciliation: Ensure tip_sales entry exists ---
            $tipSalesModel = new \App\Models\TipSaleModel();
            $existingSale = $tipSalesModel->where([
                'msisdn' => $transaction['msisdn'],
                'tip_id' => $transaction['tip_id'],
            ])->first();
            if (!$existingSale) {
                $tipModel = new \App\Models\TipModel();
                $tip = $tipModel->find($transaction['tip_id']);
                $author = $tip['author'] ?? null;
                $tag = !empty($transaction['points_used']) ? 'Used Pts' : 'Used Cash';
                if ($author) {
                    $tipSalesModel->insert([
                        'tip_id' => $transaction['tip_id'],
                        'author' => $author,
                        'msisdn' => $transaction['msisdn'],
                        'amount' => $transaction['amount'],
                        'created_at' => $transaction['created_at'] ?? date('Y-m-d H:i:s'),
                        'tag' => $tag,
                    ]);
                    log_message('info', "Reconciled tip sale for msisdn {$transaction['msisdn']} tip_id {$transaction['tip_id']} with tag {$tag}");
                }
            }
            // --- End reconciliation ---
        }

        // Handle subscription
        if (!empty($transaction['plan_id'])) {
            $subModel = new \App\Models\SubscriptionModel();
            
            if (!$subModel->where('transaction_id', $transactionId)->first()) {
                $planModel = new \App\Models\PlanModel();
                $plan = $planModel->find($transaction['plan_id']);
                
                if ($plan) {
                    $now = date('Y-m-d H:i:s');
                    $subModel->insert([
                        'msisdn' => $transaction['msisdn'],
                        'plan_id' => $transaction['plan_id'],
                        'transaction_id' => $transactionId,
                        'start_date' => $now,
                        'end_date' => date('Y-m-d H:i:s', strtotime("+{$plan['duration_days']} days", strtotime($now))),
                        'status' => 'active',
                        'created_at' => $now,
                        'updated_at' => $now,
                    ]);
                }
            }
        }

        // Handle bet-builder ticket fulfillment
        if (!empty($transaction['builder_ticket_id'])) {
            $ticketModel = new \App\Models\BuilderTicketModel();
            $ticket = $ticketModel->find((int) $transaction['builder_ticket_id']);
            if ($ticket && ($ticket['status'] ?? '') !== 'paid') {
                $ticketModel->markPaid(
                    (int) $transaction['builder_ticket_id'],
                    $transactionId
                );
            }
        }
    }

    /**
     * Convert status code to text
     */
    private function getStatusText(int $status): string
    {
        if (in_array($status, [1, '1', 'success'], true)) {
            return 'SUCCESS';
        }
        if (in_array($status, [2, '2', 'failed'], true)) {
            return 'FAILED';
        }
        return 'PENDING';
    }

    /**
     * Cron job entry point
     */
    public function cronCheckPendingTransactions()
    {
        try {
            log_message('info', 'Cron job started');
            set_time_limit(300);
            $result = $this->processPendingTransactions();
            log_message('info', 'Cron job completed');
            return $result;
        } catch (\Exception $e) {
            log_message('error', 'Cron job failed: ' . $e->getMessage());
            return $this->respond([
                'success' => false,
                'error' => $e->getMessage(),
                'timestamp' => date('Y-m-d H:i:s')
            ]);
        }
    }

    /**
     * Reconcile all past successful transactions missing tip_sales entries
     */
    public function reconcileMissingSales()
    {
        $txnModel = new \App\Models\TransactionModel();
        $tipSalesModel = new \App\Models\TipSaleModel();
        $tipModel = new \App\Models\TipModel();
        $db = \Config\Database::connect();

        // Find all successful tip transactions with no tip_sales entry
        $transactions = $db->table('transactions')
            ->where('status', 1)
            ->where('tip_id IS NOT NULL', null, false)
            ->get()->getResultArray();

        $created = 0;
        foreach ($transactions as $transaction) {
            $exists = $tipSalesModel->where([
                'msisdn' => $transaction['msisdn'],
                'tip_id' => $transaction['tip_id'],
            ])->first();
            if (!$exists) {
                $tip = $tipModel->find($transaction['tip_id']);
                $author = $tip['author'] ?? null;
                $tag = !empty($transaction['points_used']) ? 'Used Pts' : 'Used Cash';
                if ($author) {
                    $tipSalesModel->insert([
                        'tip_id' => $transaction['tip_id'],
                        'author' => $author,
                        'msisdn' => $transaction['msisdn'],
                        'amount' => $transaction['amount'],
                        'created_at' => $transaction['created_at'] ?? date('Y-m-d H:i:s'),
                        'tag' => $tag,
                    ]);
                    $created++;
                }
            }
        }
        return $this->respond([
            'success' => true,
            'created' => $created,
            'message' => "Reconciled $created missing sales entries."
        ]);
    }
}