<?php
namespace App\Controllers;

use App\Core\Controller;
use App\Models\Client;
use App\Models\Deposit;
use App\Services\MpesaParser;
use App\Core\Model;
use PDO;

class MpesaController extends Controller
{
    /**
     * List all M‑Pesa uploads and manual transactions
     */
    public function index()
    {
        $this->requireAuth();
        $pdo = Model::getPDO();

        // Get upload batches
        $stmt = $pdo->query("
            SELECT u.*, us.full_name as uploader_name 
            FROM mpesa_uploads u
            JOIN users us ON u.uploaded_by = us.id
            ORDER BY u.upload_date DESC
        ");
        $uploads = $stmt->fetchAll();

        // Get recent manual transactions (source = 'manual')
        $stmt = $pdo->query("
            SELECT mt.*, c.full_name as client_name
            FROM mpesa_transactions mt
            LEFT JOIN clients c ON mt.matched_client_id = c.id
            WHERE mt.source = 'manual'
            ORDER BY mt.completion_date DESC
            LIMIT 50
        ");
        $manualTransactions = $stmt->fetchAll();

        $this->render('mpesa/index', [
            'uploads' => $uploads,
            'manualTransactions' => $manualTransactions
        ]);
    }

    /**
     * Show upload form
     */
    public function uploadForm()
    {
        $this->requireAuth();
        $this->render('mpesa/upload');
    }

    /**
     * Handle CSV upload
     */
    public function upload()
    {
        $this->requireAuth();

        if (!isset($_FILES['csv_file']) || $_FILES['csv_file']['error'] !== UPLOAD_ERR_OK) {
            $_SESSION['error'] = 'File upload failed.';
            $this->redirect('/mpesa');
            return;
        }

        $file = $_FILES['csv_file'];
        $ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
        if ($ext !== 'csv') {
            $_SESSION['error'] = 'Only CSV files are allowed.';
            $this->redirect('/mpesa');
            return;
        }

        $uploadDir = __DIR__ . '/../../storage/uploads/';
        if (!is_dir($uploadDir)) mkdir($uploadDir, 0777, true);
        $filename = uniqid() . '_' . preg_replace('/[^a-zA-Z0-9\._-]/', '', $file['name']);
        $filePath = $uploadDir . $filename;

        if (!move_uploaded_file($file['tmp_name'], $filePath)) {
            $_SESSION['error'] = 'Failed to save file.';
            $this->redirect('/mpesa');
            return;
        }

        $parser = new MpesaParser();
        $transactions = $parser->parse($filePath);

        if (empty($transactions)) {
            $_SESSION['error'] = 'No valid transactions found.';
            unlink($filePath);
            $this->redirect('/mpesa');
            return;
        }

        $pdo = Model::getPDO();
        $pdo->beginTransaction();
        try {
            $stmt = $pdo->prepare("
                INSERT INTO mpesa_uploads (filename, upload_date, total_records, uploaded_by)
                VALUES (?, NOW(), ?, ?)
            ");
            $stmt->execute([$filename, count($transactions), $_SESSION['user_id']]);
            $uploadId = $pdo->lastInsertId();

            $stmt = $pdo->prepare("
                INSERT INTO mpesa_transactions 
                (upload_id, receipt_number, completion_date, details, transaction_status, 
                 paid_in, withdrawn, balance, transaction_type, other_party, source)
                VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'upload')
            ");

            foreach ($transactions as $t) {
                $stmt->execute([
                    $uploadId,
                    $t['receipt_number'],
                    $t['completion_date'],
                    $t['details'],
                    $t['transaction_status'],
                    $t['paid_in'],
                    $t['withdrawn'],
                    $t['balance'],
                    $t['transaction_type'],
                    $t['other_party']
                ]);
            }

            $pdo->commit();
            $_SESSION['success'] = count($transactions) . ' transactions imported.';
        } catch (\Exception $e) {
            $pdo->rollBack();
            unlink($filePath);
            $_SESSION['error'] = 'Database error: ' . $e->getMessage();
        }

        $this->redirect('/mpesa');
    }

    /**
     * Auto-match uploaded transactions
     */
    public function match($uploadId)
    {
        $this->requireAuth();
        $pdo = Model::getPDO();

        $stmt = $pdo->prepare("
            SELECT * FROM mpesa_transactions 
            WHERE upload_id = ? AND paid_in > 0 AND matched_client_id IS NULL
        ");
        $stmt->execute([$uploadId]);
        $unmatched = $stmt->fetchAll();

        $matchedCount = 0;
        foreach ($unmatched as $tx) {
            $clientId = $this->findMatchingClient($tx);
            if ($clientId) {
                $deposit = new Deposit();
                $depositData = [
                    'client_id' => $clientId,
                    'amount' => $tx['paid_in'],
                    'reference' => $tx['receipt_number'],
                    'payment_method' => 'mpesa',
                    'deposit_date' => $tx['completion_date'] ?? date('Y-m-d H:i:s'),
                    'recorded_by' => $_SESSION['user_id'],
                    'notes' => 'Auto-matched from M‑Pesa statement: ' . substr($tx['details'], 0, 200)
                ];

                $pdo->beginTransaction();
                try {
                    $depositId = $deposit->create($depositData);
                    $updateStmt = $pdo->prepare("
                        UPDATE mpesa_transactions 
                        SET matched_client_id = ?, deposit_id = ?, match_confidence = 'exact'
                        WHERE id = ?
                    ");
                    $updateStmt->execute([$clientId, $depositId, $tx['id']]);
                    $pdo->commit();
                    $matchedCount++;
                } catch (\Exception $e) {
                    $pdo->rollBack();
                }
            }
        }

        $pdo->prepare("UPDATE mpesa_uploads SET matched_records = matched_records + ?, status = 'processed' WHERE id = ?")
            ->execute([$matchedCount, $uploadId]);

        $_SESSION['success'] = "{$matchedCount} deposit(s) created.";
        $this->redirect('/mpesa');
    }

    /**
     * Show form for manual M‑Pesa transaction entry
     */
    public function createForm()
    {
        $this->requireAuth();
        $clientModel = new Client();
        $clients = $clientModel->getAll();
        $this->render('mpesa/create', ['clients' => $clients]);
    }

    /**
     * Store a manually entered M‑Pesa transaction
     */
    public function store()
    {
        $this->requireAuth();

        $pdo = Model::getPDO();
        $pdo->beginTransaction();
        try {
            // Insert into mpesa_transactions with source = 'manual'
            $stmt = $pdo->prepare("
                INSERT INTO mpesa_transactions 
                (upload_id, receipt_number, completion_date, details, transaction_status, 
                 paid_in, withdrawn, balance, transaction_type, other_party, source)
                VALUES (NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'manual')
            ");
            $stmt->execute([
                $_POST['receipt_number'] ?? null,
                $_POST['completion_date'] ?: date('Y-m-d H:i:s'),
                $_POST['details'] ?? null,
                $_POST['transaction_status'] ?? 'Completed',
                $_POST['paid_in'] ?: 0,
                $_POST['withdrawn'] ?: 0,
                $_POST['balance'] ?: 0,
                $_POST['transaction_type'] ?? 'Pay Bill',
                $_POST['other_party'] ?? null
            ]);
            $transactionId = $pdo->lastInsertId();

            // If a client is selected and it's an incoming payment, create a deposit
            $clientId = $_POST['client_id'] ?? null;
            $createDeposit = isset($_POST['create_deposit']) && $clientId && $_POST['paid_in'] > 0;

            if ($createDeposit) {
                $deposit = new Deposit();
                $depositData = [
                    'client_id' => $clientId,
                    'amount' => $_POST['paid_in'],
                    'reference' => $_POST['receipt_number'] ?? ('MANUAL_' . $transactionId),
                    'payment_method' => 'mpesa',
                    'deposit_date' => $_POST['completion_date'] ?: date('Y-m-d H:i:s'),
                    'recorded_by' => $_SESSION['user_id'],
                    'notes' => 'Manual M‑Pesa entry: ' . ($_POST['details'] ?? '')
                ];
                $depositId = $deposit->create($depositData);

                // Link transaction to client and deposit
                $stmt = $pdo->prepare("
                    UPDATE mpesa_transactions 
                    SET matched_client_id = ?, deposit_id = ?, match_confidence = 'exact'
                    WHERE id = ?
                ");
                $stmt->execute([$clientId, $depositId, $transactionId]);
            }

            $pdo->commit();
            $_SESSION['success'] = 'M‑Pesa transaction recorded successfully.';
        } catch (\Exception $e) {
            $pdo->rollBack();
            $_SESSION['error'] = 'Error: ' . $e->getMessage();
        }

        $this->redirect('/mpesa');
    }

    /**
     * Find a client that matches the M‑Pesa transaction
     */
    private function findMatchingClient($transaction)
    {
        $pdo = Model::getPDO();
        $phone = $this->extractPhoneNumber($transaction['other_party']);
        if (!$phone) {
            $phone = $this->extractPhoneNumber($transaction['details']);
        }

        if ($phone) {
            $phone = preg_replace('/[^0-9]/', '', $phone);
            if (strlen($phone) === 10 && substr($phone, 0, 1) === '0') {
                $phone = '254' . substr($phone, 1);
            } elseif (strlen($phone) === 9 && substr($phone, 0, 1) === '7') {
                $phone = '254' . $phone;
            }

            $stmt = $pdo->prepare("
                SELECT id FROM clients 
                WHERE REPLACE(REPLACE(REPLACE(phone, ' ', ''), '-', ''), '+', '') LIKE ?
            ");
            $stmt->execute(['%' . $phone . '%']);
            $client = $stmt->fetch();
            if ($client) return $client['id'];
        }

        $name = $this->extractName($transaction['other_party']);
        if ($name) {
            $stmt = $pdo->prepare("SELECT id FROM clients WHERE full_name LIKE ?");
            $stmt->execute(['%' . $name . '%']);
            $client = $stmt->fetch();
            if ($client) return $client['id'];
        }
        return null;
    }

    private function extractPhoneNumber($text)
    {
        $patterns = ['/\b(07\d{8})\b/', '/\b(2547\d{8})\b/', '/\b(\+2547\d{8})\b/', '/\b(7\d{8})\b/'];
        foreach ($patterns as $pattern) {
            if (preg_match($pattern, $text, $matches)) return $matches[1];
        }
        return null;
    }

    private function extractName($text)
    {
        $name = preg_replace('/^[\d\s\+-]+/', '', $text);
        $name = trim(str_replace('-', '', $name));
        return (strlen($name) >= 3) ? $name : null;
    }
}