<?php
declare(strict_types=1);

namespace App\Modules\Subscriptions\Controllers;

use App\Core\Controller;
use App\Core\Request;
use App\Core\Response;
use App\Core\App;
use App\Core\EventBus;
use App\Modules\Subscriptions\Models\Subscription;
use App\Modules\Subscriptions\Services\SubscriptionGenerator;
use App\Modules\Subscriptions\Services\SubscriptionCalculator;
use App\Modules\Subscriptions\Services\OverdueFineApplicator;
use App\Modules\Payments\Services\PaymentService;

class SubscriptionController extends Controller
{
    public function index(Request $request): Response
    {
        $filters = [
            'search'         => trim((string) $request->get('q', '')),
            'financial_year' => $request->get('financial_year', ''),
            'status'         => $request->get('status', ''),
            'person_type'    => $request->get('person_type', ''),
        ];
        $page = max(1, (int) $request->get('page', 1));
        $result = Subscription::search($filters, 30, $page);

        $currentYear = self::currentFinancialYear();

        return $this->view('Subscriptions.Views.index', [
            'rows'        => $result['data'],
            'pagination'  => $result['pagination'],
            'filters'     => $filters,
            'currentYear' => $currentYear,
        ]);
    }

    public function memberSubscriptions(Request $request, string $memberId): Response
    {
        $db = App::getInstance()->db();
        $member = $db->selectOne("SELECT * FROM members WHERE id = ?", [(int) $memberId]);
        if (!$member) return $this->redirect('/members')->withError('العضو غير موجود');

        $lateFine = OverdueFineApplicator::applyForMember((int) $memberId);

        $subscriptions = Subscription::getForMember((int) $memberId);
        $unpaidYears = Subscription::getUnpaidYears((int) $memberId);

        $grouped = [];
        foreach ($subscriptions as $s) {
            $grouped[$s['financial_year']][] = $s;
        }

        $yearTotals = [];
        foreach ($grouped as $fy => $rows) {
            $totalAmount = '0.00';
            $totalPaid = '0.00';
            $totalFine = '0.00';
            $devFee = '0.00';
            $allPaid = true;
            $anyPaid = false;
            foreach ($rows as $r) {
                $totalAmount = bcadd($totalAmount, $r['total_amount'], 2);
                $totalPaid = bcadd($totalPaid, $r['paid_amount'] ?? '0', 2);
                $totalFine = bcadd($totalFine, $r['fine_amount'] ?? '0', 2);
                if (bccomp($r['development_fee'] ?? '0', '0', 2) > 0 && bccomp($devFee, '0', 2) === 0) {
                    $devFee = $r['development_fee'];
                }
                if ($r['status'] !== 'paid' && $r['status'] !== 'exempt') $allPaid = false;
                if ($r['status'] === 'paid') $anyPaid = true;
            }
            $yearTotals[$fy] = [
                'total_amount' => $totalAmount,
                'total_paid'   => $totalPaid,
                'total_fine'   => $totalFine,
                'total_dev'    => $devFee,
                'year_status'  => $allPaid ? 'paid' : ($anyPaid ? 'partial' : 'unpaid'),
                'count'        => count($rows),
            ];
        }

        $oldestUnpaidYear = null;
        foreach ($unpaidYears as $uy) {
            $oldestUnpaidYear = $uy['financial_year'];
            break;
        }

        return $this->view('Subscriptions.Views.member-subscriptions', [
            'member'           => $member,
            'grouped'          => $grouped,
            'yearTotals'       => $yearTotals,
            'lateFine'         => $lateFine,
            'oldestUnpaidYear' => $oldestUnpaidYear,
        ]);
    }

    public function payYear(Request $request, string $memberId, string $year): Response
    {
        $db = App::getInstance()->db();
        $financialYear = str_replace('-', '/', $year);

        $member = $db->selectOne("SELECT id, full_name_ar FROM members WHERE id = ? AND is_archived = 0", [(int) $memberId]);
        if (!$member) return $this->redirect('/members')->withError('العضو غير موجود');

        $olderUnpaid = $db->selectOne(
            "SELECT financial_year FROM subscriptions WHERE member_id = ? AND financial_year < ? AND status IN ('pending','overdue') LIMIT 1",
            [(int) $memberId, $financialYear]
        );
        if ($olderUnpaid) {
            return $this->redirect("/members/{$memberId}/subscriptions")
                ->withError('يجب سداد اشتراكات السنة الأقدم أولاً (' . $olderUnpaid['financial_year'] . ')');
        }

        $rows = $db->select(
            "SELECT id, total_amount, fine_amount, paid_amount, development_fee, person_name, person_type
             FROM subscriptions WHERE member_id = ? AND financial_year = ? AND status IN ('pending','overdue')",
            [(int) $memberId, $financialYear]
        );
        if (empty($rows)) {
            return $this->redirect("/members/{$memberId}/subscriptions")->withError('لا توجد اشتراكات مستحقة لهذه السنة');
        }

        $subscriptionTotal = '0.00';
        $devFee = '0.00';
        foreach ($rows as $r) {
            $remaining = bcsub(bcadd($r['total_amount'], $r['fine_amount'], 2), $r['paid_amount'], 2);
            $subscriptionTotal = bcadd($subscriptionTotal, $remaining, 2);
            if (bccomp($r['development_fee'], '0', 2) > 0) {
                $devFee = $r['development_fee'];
            }
        }
        $totalAmount = bcadd($subscriptionTotal, $devFee, 2);

        $data = $request->all();
        $data['member_id'] = (int) $memberId;
        $data['amount'] = $totalAmount;
        $data['payment_type'] = 'annual_subscription';
        $data['payment_method'] = $data['payment_method'] ?? 'cash';
        $data['related_entity_type'] = 'subscriptions';
        $data['related_entity_id'] = (int) $rows[0]['id'];
        $data['description'] = 'اشتراك سنوي ' . $financialYear . ' — ' . $member['full_name_ar'] . ' (عائلة كاملة' . (bccomp($devFee, '0', 2) > 0 ? ' + مصاريف تنمية' : '') . ')';

        $result = PaymentService::processPayment($data);
        if (!$result['success']) {
            return $this->redirect("/members/{$memberId}/subscriptions")->withError($result['error']);
        }

        $ts = date('Y-m-d H:i:s');
        $employee = App::getInstance()->currentEmployee();
        $paidBy = $employee ? (int) $employee->id : null;
        foreach ($rows as $r) {
            $remaining = bcsub(bcadd($r['total_amount'], $r['fine_amount'], 2), $r['paid_amount'], 2);
            // Dev fee is added to the member row's paid_amount (single flat charge per family)
            $rowDevFee = (bccomp($r['development_fee'] ?? '0', '0', 2) > 0) ? $r['development_fee'] : '0.00';
            $rowPaid = bcadd(bcadd($r['paid_amount'], $remaining, 2), $rowDevFee, 2);
            $db->update('subscriptions', [
                'paid_amount'    => $rowPaid,
                'payment_id'     => $result['payment_id'],
                'receipt_number' => $result['receipt_number'],
                'paid_by'        => $paidBy,
                'status'         => 'paid',
                'paid_at'        => $ts,
                'updated_at'     => $ts,
            ], '`id` = ?', [(int) $r['id']]);
        }

        EventBus::dispatch('subscription.paid', [
            'member_id' => (int) $memberId,
            'year'      => $financialYear,
            'amount'    => $totalAmount,
            'count'     => count($rows),
        ]);

        return $this->redirect("/members/{$memberId}/subscriptions")
            ->withSuccess('تم سداد اشتراك ' . $financialYear . ' بالكامل (' . count($rows) . ' فرد) — إيصال: ' . $result['receipt_number']);
    }

    public function pay(Request $request, string $id): Response
    {
        $db = App::getInstance()->db();
        $sub = $db->selectOne("SELECT s.*, m.full_name_ar as member_name FROM subscriptions s JOIN members m ON m.id = s.member_id WHERE s.id = ?", [(int) $id]);
        if (!$sub) return $this->redirect('/subscriptions')->withError('الاشتراك غير موجود');
        if ($sub['status'] === 'paid') return $this->redirect('/subscriptions')->withError('الاشتراك مدفوع بالفعل');

        $olderUnpaid = $db->selectOne(
            "SELECT financial_year FROM subscriptions WHERE member_id = ? AND financial_year < ? AND status IN ('pending','overdue') LIMIT 1",
            [(int) $sub['member_id'], $sub['financial_year']]
        );
        if ($olderUnpaid) {
            return $this->redirect("/members/{$sub['member_id']}/subscriptions")
                ->withError('يجب سداد اشتراكات السنة الأقدم أولاً (' . $olderUnpaid['financial_year'] . ')');
        }

        $amount = bcsub(bcadd($sub['total_amount'], $sub['fine_amount'], 2), $sub['paid_amount'], 2);

        $data = $request->all();
        $data['member_id'] = (int) $sub['member_id'];
        $data['amount'] = $amount;
        $data['payment_type'] = 'annual_subscription';
        $data['payment_method'] = $data['payment_method'] ?? 'cash';
        $data['related_entity_type'] = 'subscriptions';
        $data['related_entity_id'] = (int) $id;
        $data['description'] = 'اشتراك سنوي ' . $sub['financial_year'] . ' — ' . ($sub['person_name'] ?? $sub['member_name']);

        $result = PaymentService::processPayment($data);
        if (!$result['success']) {
            return $this->redirect("/members/{$sub['member_id']}/subscriptions")->withError($result['error']);
        }

        $employee = App::getInstance()->currentEmployee();
        $db->update('subscriptions', [
            'paid_amount'    => bcadd($sub['paid_amount'], $amount, 2),
            'payment_id'     => $result['payment_id'],
            'receipt_number' => $result['receipt_number'],
            'paid_by'        => $employee ? (int) $employee->id : null,
            'status'         => 'paid',
            'paid_at'        => date('Y-m-d H:i:s'),
            'updated_at'     => date('Y-m-d H:i:s'),
        ], '`id` = ?', [(int) $id]);

        EventBus::dispatch('subscription.paid', [
            'subscription_id' => (int) $id,
            'member_id'       => (int) $sub['member_id'],
            'year'            => $sub['financial_year'],
            'amount'          => $amount,
        ]);

        return $this->redirect("/members/{$sub['member_id']}/subscriptions")->withSuccess('تم تسجيل الاشتراك — إيصال: ' . $result['receipt_number']);
    }

    public function exempt(Request $request, string $id): Response
    {
        $db = App::getInstance()->db();
        $sub = $db->selectOne("SELECT * FROM subscriptions WHERE id = ?", [(int) $id]);
        if (!$sub) return $this->redirect('/subscriptions')->withError('الاشتراك غير موجود');

        $reason = trim((string) $request->post('exemption_reason', ''));
        if ($reason === '') return $this->redirect("/members/{$sub['member_id']}/subscriptions")->withError('يجب إدخال سبب الإعفاء');

        $employee = App::getInstance()->currentEmployee();
        $db->update('subscriptions', [
            'status'           => 'exempt',
            'exempted_by'      => $employee ? (int) $employee->id : null,
            'exemption_reason' => $reason,
            'updated_at'       => date('Y-m-d H:i:s'),
        ], '`id` = ?', [(int) $id]);

        return $this->redirect("/members/{$sub['member_id']}/subscriptions")->withSuccess('تم إعفاء الاشتراك');
    }

    public function batchGenerateForm(Request $request): Response
    {
        $db = App::getInstance()->db();
        $branches = $db->select("SELECT id, name_ar FROM branches WHERE is_active = 1 ORDER BY name_ar");
        return $this->view('Subscriptions.Views.batch-generate', ['branches' => $branches, 'result' => null]);
    }

    public function batchGenerate(Request $request): Response
    {
        $year = trim((string) $request->post('financial_year', ''));
        $branchId = $request->post('branch_id', '') !== '' ? (int) $request->post('branch_id') : null;

        if ($year === '') return $this->redirect('/subscriptions/batch-generate')->withError('السنة المالية مطلوبة');

        $result = SubscriptionGenerator::generateForYear($year, $branchId);

        $db = App::getInstance()->db();
        $branches = $db->select("SELECT id, name_ar FROM branches WHERE is_active = 1 ORDER BY name_ar");

        return $this->view('Subscriptions.Views.batch-generate', ['branches' => $branches, 'result' => $result]);
    }

    private static function currentFinancialYear(): string
    {
        $month = (int) date('n');
        $year = (int) date('Y');
        if ($month >= 7) return $year . '/' . ($year + 1);
        return ($year - 1) . '/' . $year;
    }
}