<?php
declare(strict_types=1);

namespace App\Modules\Installments\Controllers;

use App\Core\Controller;
use App\Core\Request;
use App\Core\Response;
use App\Core\App;
use App\Modules\Installments\Services\ChequeService;

class ChequeController extends Controller
{
    public function index(Request $request, string $planId): Response
    {
        $this->authorize('installment.view');

        $db = App::getInstance()->db();

        $plan = $db->selectOne(
            "SELECT ip.*, m.full_name_ar as member_name, m.membership_number, m.status as member_status, m.form_number
             FROM installment_plans ip
             JOIN members m ON m.id = ip.member_id
             WHERE ip.id = ? AND ip.status = 'active'",
            [(int) $planId]
        );

        if (!$plan) {
            return $this->redirect('/installments')->withError('خطة التقسيط غير موجودة أو غير نشطة');
        }

        $cheques = $db->select(
            "SELECT ic.*, e.full_name_ar as uploaded_by_name
             FROM installment_cheques ic
             LEFT JOIN employees e ON e.id = ic.uploaded_by
             WHERE ic.installment_plan_id = ?
             ORDER BY ic.cheque_date ASC",
            [(int) $planId]
        );

        $requiredCount = (int) $plan['number_of_months'];
        $uploadedCount = count($cheques);
        $remainingCount = max(0, $requiredCount - $uploadedCount);
        $allSubmitted = $remainingCount === 0;

        // Total amount that cheques must cover = total_with_interest (remaining after down payment)
        $uploadedTotal = array_sum(array_column($cheques, 'cheque_amount'));
        $planTotal     = (float) ($plan['total_with_interest'] ?? 0);

        return $this->view('Installments.Views.cheques', [
            'plan'                => $plan,
            'cheques'             => $cheques,
            'requiredCount'       => $requiredCount,
            'uploadedCount'       => $uploadedCount,
            'remainingCount'      => $remainingCount,
            'allSubmitted'        => $allSubmitted,
            'uploadedTotal'       => $uploadedTotal,
            'planTotal'           => $planTotal,
            'nextChequeNum'       => ChequeService::nextChequeNumber(),
            'nextChequeNumForPlan'=> ChequeService::nextChequeNumberForPlan((int) $planId),
        ]);
    }

    /**
     * Batch-save multiple cheques at once (no scan files required at this stage).
     * Validates that the total cheque amount >= plan's total_with_interest.
     */
    public function storeBatch(Request $request, string $planId): Response
    {
        $this->authorize('installment.create_plan');

        $db = App::getInstance()->db();

        $plan = $db->selectOne(
            "SELECT id, member_id, number_of_months, total_with_interest, monthly_payment
             FROM installment_plans WHERE id = ? AND status = 'active'",
            [(int) $planId]
        );
        if (!$plan) {
            return $this->redirect('/installments')->withError('خطة التقسيط غير موجودة');
        }

        $cheques    = $request->post('cheques', []);
        $planTotal  = (float) $plan['total_with_interest'];

        if (!is_array($cheques) || empty($cheques)) {
            return $this->redirect("/installments/{$planId}/cheques")->withError('لم يتم إدخال أي شيكات');
        }

        // Load existing cheques for this plan (to prevent duplicates and compute remaining)
        $existingCheques = $db->select(
            "SELECT cheque_number, cheque_amount FROM installment_cheques WHERE installment_plan_id = ?",
            [(int) $planId]
        );
        $existingNums  = array_map(fn($c) => trim((string) $c['cheque_number']), $existingCheques);
        $existingTotal = array_sum(array_column($existingCheques, 'cheque_amount'));
        $existingCount = count($existingCheques);
        $requiredCount = (int) $plan['number_of_months'];

        // Guard: no more cheques needed
        if ($existingCount >= $requiredCount) {
            return $this->redirect("/installments/{$planId}/cheques")
                ->withError('تم تسليم جميع الشيكات المطلوبة بالفعل — لا يمكن إضافة المزيد');
        }

        // Guard: batch would exceed required count
        if ($existingCount + count($cheques) > $requiredCount) {
            $allowed = $requiredCount - $existingCount;
            return $this->redirect("/installments/{$planId}/cheques")
                ->withError("عدد الشيكات المدخلة ({$allowed} مسموح به) يتجاوز العدد المتبقي — المسموح: {$allowed} شيك");
        }

        // Validate each row and compute batch total
        $errors = [];
        $totalEntered = 0.0;
        foreach ($cheques as $idx => $chq) {
            $n   = $idx + 1;
            $num = trim((string) ($chq['cheque_number'] ?? ''));
            $bnk = trim((string) ($chq['bank_name']     ?? ''));
            $amt = (float) ($chq['cheque_amount'] ?? 0);
            $dt  = trim((string) ($chq['cheque_date']   ?? ''));

            if ($num === '')    $errors[] = "الشيك #{$n}: رقم الشيك مطلوب";
            if ($bnk === '')    $errors[] = "الشيك #{$n}: اسم البنك مطلوب";
            if ($amt <= 0)      $errors[] = "الشيك #{$n}: المبلغ يجب أن يكون أكبر من صفر";
            if ($dt  === '')    $errors[] = "الشيك #{$n}: التاريخ مطلوب";

            // Check against existing cheque numbers in DB
            if ($num !== '' && in_array($num, $existingNums, true)) {
                $errors[] = "الشيك #{$n}: رقم الشيك \"{$num}\" موجود مسبقاً في هذه الخطة";
            }

            $totalEntered += $amt;
        }

        // Only enforce coverage check if this batch completes all required cheques
        $isLastBatch = ($existingCount + count($cheques)) >= $requiredCount;
        $grandTotal  = $existingTotal + $totalEntered;
        if (empty($errors) && $isLastBatch && $grandTotal < $planTotal - 0.009) {
            $errors[] = sprintf(
                'إجمالي قيمة جميع الشيكات (%.2f ج.م = موجود %.2f + جديد %.2f) أقل من إجمالي التقسيط (%.2f ج.م)',
                $grandTotal,
                $existingTotal,
                $totalEntered,
                $planTotal
            );
        }

        if (!empty($errors)) {
            $session = App::getInstance()->session();
            $session->flash('_alerts', array_map(fn($m) => ['type' => 'error', 'message' => $m], $errors));
            return $this->redirect("/installments/{$planId}/cheques");
        }

        // Check for duplicate cheque numbers within the batch itself
        $batchNums = array_filter(array_map(fn($c) => trim((string) ($c['cheque_number'] ?? '')), $cheques));
        if (count(array_unique($batchNums)) !== count($batchNums)) {
            return $this->redirect("/installments/{$planId}/cheques")
                ->withError('توجد أرقام شيكات مكررة في القائمة — يجب أن يكون كل رقم فريداً');
        }

        $employeeId = App::getInstance()->session()->get('employee_id');
        $ts         = date('Y-m-d H:i:s');

        $db->beginTransaction();
        try {
            foreach ($cheques as $chq) {
                $db->insert('installment_cheques', [
                    'installment_plan_id' => (int) $planId,
                    'cheque_number'       => trim((string) ($chq['cheque_number'] ?? '')),
                    'bank_name'           => trim((string) ($chq['bank_name']     ?? '')),
                    'cheque_date'         => trim((string) ($chq['cheque_date']   ?? '')),
                    'cheque_amount'       => (float) ($chq['cheque_amount'] ?? 0),
                    'scan_path'           => '',
                    'scan_original_name'  => null,
                    'uploaded_by'         => $employeeId ? (int) $employeeId : null,
                    'notes'               => trim((string) ($chq['notes'] ?? '')) ?: null,
                    'created_at'          => $ts,
                    'updated_at'          => $ts,
                ]);
            }
            $db->commit();
        } catch (\Throwable $e) {
            $db->rollBack();
            return $this->redirect("/installments/{$planId}/cheques")->withError('خطأ أثناء الحفظ: ' . $e->getMessage());
        }

        // Check if count threshold reached → activate member
        if (ChequeService::allChequesSubmitted((int) $planId)) {
            $result = ChequeService::activateMemberAfterCheques((int) $planId);
            if ($result['success'] && !($result['already_active'] ?? false)) {
                return $this->redirect("/installments/{$planId}/cheques")
                    ->withSuccess('تم حفظ الشيكات وتفعيل العضوية — رقم العضوية: ' . ($result['membership_number'] ?? ''));
            }
        }

        return $this->redirect("/installments/{$planId}/cheques")
            ->withSuccess('تم حفظ ' . count($cheques) . ' شيكات بنجاح — الإجمالي: ' . number_format($totalEntered, 2) . ' ج.م');
    }

    public function store(Request $request, string $planId): Response
    {
        $this->authorize('installment.create_plan');

        $db = App::getInstance()->db();

        $plan = $db->selectOne(
            "SELECT id, member_id, number_of_months, total_with_interest FROM installment_plans WHERE id = ? AND status = 'active'",
            [(int) $planId]
        );

        if (!$plan) {
            return $this->redirect('/installments')->withError('خطة التقسيط غير موجودة');
        }

        $chequeNumber = trim((string) $request->post('cheque_number', ''));
        $bankName = trim((string) $request->post('bank_name', ''));
        $chequeDate = trim((string) $request->post('cheque_date', ''));
        $chequeAmount = trim((string) $request->post('cheque_amount', ''));
        $notes = trim((string) $request->post('notes', ''));

        $errors = [];
        if ($chequeNumber === '') {
            $errors[] = 'رقم الشيك مطلوب';
        } elseif (mb_strlen($chequeNumber) > 50) {
            $errors[] = 'رقم الشيك يجب ألا يتجاوز 50 حرف';
        }
        if ($bankName === '') {
            $errors[] = 'اسم البنك مطلوب';
        } elseif (mb_strlen($bankName) > 200) {
            $errors[] = 'اسم البنك يجب ألا يتجاوز 200 حرف';
        }
        if ($chequeDate === '') {
            $errors[] = 'تاريخ الشيك مطلوب';
        }
        if ($chequeAmount === '' || (float) $chequeAmount <= 0) {
            $errors[] = 'مبلغ الشيك مطلوب ويجب أن يكون أكبر من صفر';
        }

        // Check uploaded count
        $currentCount = (int) $db->selectOne(
            "SELECT COUNT(*) as cnt FROM installment_cheques WHERE installment_plan_id = ?",
            [(int) $planId]
        )['cnt'];

        if ($currentCount >= (int) $plan['number_of_months']) {
            return $this->redirect("/installments/{$planId}/cheques")->withError('تم رفع جميع الشيكات المطلوبة بالفعل');
        }

        // File upload validation
        $file = $_FILES['scan_file'] ?? null;
        if (!$file || $file['error'] !== UPLOAD_ERR_OK) {
            $errors[] = 'صورة/مسح الشيك مطلوبة';
        } else {
            $allowedMimes = ['image/jpeg', 'image/png', 'image/webp', 'application/pdf'];
            $finfo = new \finfo(FILEINFO_MIME_TYPE);
            $detectedMime = $finfo->file($file['tmp_name']);
            if (!in_array($detectedMime, $allowedMimes, true)) {
                $errors[] = 'نوع الملف غير مسموح (يُقبل: JPG, PNG, WebP, PDF)';
            }
            if ($file['size'] > 10 * 1024 * 1024) {
                $errors[] = 'حجم الملف يتجاوز الحد الأقصى (10 ميجابايت)';
            }
        }

        // Total-coverage validation: sum of existing + this cheque must cover plan total
        if (empty($errors)) {
            $existingTotal = (float) ($db->selectOne(
                "SELECT COALESCE(SUM(cheque_amount), 0) as s FROM installment_cheques WHERE installment_plan_id = ?",
                [(int) $planId]
            )['s'] ?? 0);
            $newTotal    = $existingTotal + (float) $chequeAmount;
            $isLastBatch = ($currentCount + 1) >= (int) $plan['number_of_months'];
            if ($isLastBatch && $newTotal < (float) $plan['total_with_interest'] - 0.009) {
                $errors[] = sprintf(
                    'إجمالي قيمة الشيكات بعد الإضافة (%.2f ج.م) أقل من إجمالي التقسيط (%.2f ج.م)',
                    $newTotal,
                    (float) $plan['total_with_interest']
                );
            }
        }

        if (!empty($errors)) {
            $session = App::getInstance()->session();
            $alerts = array_map(fn($msg) => ['type' => 'error', 'message' => $msg], $errors);
            $session->flash('_alerts', $alerts);
            $session->flash('_old_input', $request->all());
            return $this->redirect("/installments/{$planId}/cheques");
        }

        // Store the file
        $uploadDir = App::getInstance()->basePath() . '/storage/uploads/cheques/';
        if (!is_dir($uploadDir)) {
            mkdir($uploadDir, 0755, true);
        }

        $ext = pathinfo($file['name'], PATHINFO_EXTENSION) ?: 'jpg';
        $storedFilename = 'chq_' . $planId . '_' . time() . '_' . bin2hex(random_bytes(4)) . '.' . $ext;
        $destination = $uploadDir . $storedFilename;

        if (!move_uploaded_file($file['tmp_name'], $destination)) {
            return $this->redirect("/installments/{$planId}/cheques")->withError('فشل في حفظ الملف');
        }

        $employeeId = App::getInstance()->session()->get('employee_id');

        $db->insert('installment_cheques', [
            'installment_plan_id' => (int) $planId,
            'cheque_number'       => $chequeNumber,
            'bank_name'           => $bankName,
            'cheque_date'         => $chequeDate,
            'cheque_amount'       => (float) $chequeAmount,
            'scan_path'           => 'storage/uploads/cheques/' . $storedFilename,
            'scan_original_name'  => $file['name'],
            'uploaded_by'         => $employeeId ? (int) $employeeId : null,
            'notes'               => $notes ?: null,
            'created_at'          => date('Y-m-d H:i:s'),
            'updated_at'          => date('Y-m-d H:i:s'),
        ]);

        // Check if all cheques now submitted → activate member
        if (ChequeService::allChequesSubmitted((int) $planId)) {
            $result = ChequeService::activateMemberAfterCheques((int) $planId);
            if ($result['success'] && !($result['already_active'] ?? false)) {
                return $this->redirect("/installments/{$planId}/cheques")
                    ->withSuccess('تم رفع الشيك وتفعيل العضوية بنجاح — رقم العضوية: ' . ($result['membership_number'] ?? ''));
            }
        }

        $remaining = (int) $plan['number_of_months'] - ($currentCount + 1);
        $msg = 'تم رفع الشيك بنجاح';
        if ($remaining > 0) {
            $msg .= " — المتبقي: {$remaining} شيك";
        }

        return $this->redirect("/installments/{$planId}/cheques")->withSuccess($msg);
    }
}
