<?php
declare(strict_types=1);

namespace App\Modules\Fines\Controllers;

use App\Core\Controller;
use App\Core\Request;
use App\Core\Response;
use App\Core\App;
use App\Core\EventBus;
use App\Core\Logger;
use App\Modules\Fines\Models\Fine;
use App\Modules\Fines\Models\Violation;
use App\Modules\Rules\Services\RuleEngine;
use App\Modules\Payments\Services\PaymentService;
use App\Modules\Workflow\Services\WorkflowEngine;

class FineController extends Controller
{
    public function index(Request $request): Response
    {
        $filters = [
            'search'       => trim((string) $request->get('q', '')),
            'status'       => $request->get('status', ''),
            'penalty_type' => $request->get('penalty_type', ''),
        ];
        $page = max(1, (int) $request->get('page', 1));
        $result = Fine::search($filters, 25, $page);

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

    public function impose(Request $request, string $violationId): Response
    {
        $db = App::getInstance()->db();
        $violation = $db->selectOne("SELECT * FROM violations WHERE id = ?", [(int) $violationId]);
        if (!$violation) return $this->redirect('/violations')->withError('المخالفة غير موجودة');

        $penaltyType = trim($request->post('penalty_type', ''));
        $amount = trim($request->post('amount', '0'));
        $suspensionFrom = $request->post('suspension_from') ?: null;
        $suspensionTo = $request->post('suspension_to') ?: null;
        $notes = trim($request->post('notes', ''));

        $errors = [];
        $validTypes = ['warning', 'caution', 'fine', 'suspension', 'ban', 'termination'];
        if (!in_array($penaltyType, $validTypes)) $errors[] = 'نوع العقوبة غير صالح';

        if ($penaltyType === 'fine') {
            $minData = RuleEngine::get('VIOLATION_FINE_MIN');
            $maxData = RuleEngine::get('VIOLATION_FINE_MAX');
            $min = $minData['amount'] ?? '200.00';
            $max = $maxData['amount'] ?? '10000.00';
            if (bccomp($amount, $min, 2) < 0 || bccomp($amount, $max, 2) > 0) {
                $errors[] = "مبلغ الغرامة يجب أن يكون بين {$min} و {$max} ج.م";
            }
        }

        if (in_array($penaltyType, ['suspension', 'ban']) && (!$suspensionFrom || !$suspensionTo)) {
            $errors[] = 'تاريخ البداية والنهاية مطلوبان للإيقاف/المنع';
        }

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

        $employee = App::getInstance()->currentEmployee();
        $fineAmount = in_array($penaltyType, ['fine']) ? $amount : '0.00';

        $fine = Fine::create([
            'member_id'       => (int) $violation['member_id'],
            'violation_id'    => (int) $violationId,
            'fine_type'       => 'violation_fine',
            'amount'          => $fineAmount,
            'penalty_type'    => $penaltyType,
            'suspension_from' => $suspensionFrom,
            'suspension_to'   => $suspensionTo,
            'status'          => 'imposed',
            'notes'           => $notes ?: null,
        ]);

        $db->update('violations', ['status' => 'penalty_imposed', 'updated_at' => date('Y-m-d H:i:s')], '`id` = ?', [(int) $violationId]);

        // Update member status for suspension/ban/termination
        if (in_array($penaltyType, ['suspension', 'ban'])) {
            $db->update('members', ['status' => 'suspended', 'updated_at' => date('Y-m-d H:i:s')], '`id` = ? AND `status` = \'active\'', [(int) $violation['member_id']]);
        } elseif ($penaltyType === 'termination') {
            $db->update('members', ['status' => 'terminated', 'updated_at' => date('Y-m-d H:i:s')], '`id` = ? AND `status` IN (\'active\',\'suspended\')', [(int) $violation['member_id']]);
        }

        if (WorkflowEngine::hasDefinition('violation_penalty')) {
            WorkflowEngine::createInstance('violation_penalty', 'fines', (int) $fine->id);
        }

        try {
            WorkflowEngine::transitionByEntity('violations', (int) $violationId, 'impose_penalty');
        } catch (\Throwable $e) {
            Logger::warning("Workflow transition failed for violation impose_penalty", ['violation_id' => (int) $violationId, 'error' => $e->getMessage()]);
        }

        EventBus::dispatch('fine.imposed', [
            'fine_id'      => (int) $fine->id,
            'member_id'    => (int) $violation['member_id'],
            'penalty_type' => $penaltyType,
            'amount'       => $fineAmount,
        ]);

        return $this->redirect('/fines')->withSuccess('تم فرض العقوبة: ' . Fine::getPenaltyTypeLabel($penaltyType));
    }

    public function pay(Request $request, string $id): Response
    {
        $db = App::getInstance()->db();
        $fine = $db->selectOne("SELECT * FROM fines WHERE id = ? FOR UPDATE", [(int) $id]);
        if (!$fine) return $this->redirect('/fines')->withError('الغرامة غير موجودة');
        if ($fine['penalty_type'] !== 'fine') return $this->redirect('/fines')->withError('هذه العقوبة ليست غرامة مالية');
        if ($fine['status'] === 'paid') return $this->redirect('/fines')->withError('الغرامة مسددة بالكامل بالفعل');

        $remaining = bcsub($fine['amount'], $fine['paid_amount'], 2);
        if (bccomp($remaining, '0', 2) <= 0) return $this->redirect('/fines')->withError('الغرامة مسددة بالكامل');

        // Prevent double payment — check if a payment already exists for this fine
        $existingPayment = $db->selectOne(
            "SELECT id FROM payments WHERE related_entity_type = 'fines' AND related_entity_id = ? AND is_voided = 0 LIMIT 1",
            [(int) $id]
        );
        if ($existingPayment) return $this->redirect('/fines')->withError('تم سداد هذه الغرامة بالفعل — لا يمكن الدفع مرتين');

        $data = $request->all();
        $data['member_id'] = (int) $fine['member_id'];
        $data['amount'] = $remaining;
        $data['payment_type'] = 'fine';
        $data['payment_method'] = $data['payment_method'] ?? 'cash';
        $data['related_entity_type'] = 'fines';
        $data['related_entity_id'] = (int) $id;
        $data['description'] = 'غرامة مخالفة #' . ($fine['violation_id'] ?? $id);

        $result = PaymentService::processPayment($data);
        if (!$result['success']) return $this->redirect('/fines')->withError($result['error']);

        $db->update('fines', [
            'paid_amount' => $fine['amount'],
            'payment_id'  => $result['payment_id'],
            'paid_at'     => date('Y-m-d H:i:s'),
            'status'      => 'paid',
            'updated_at'  => date('Y-m-d H:i:s'),
        ], '`id` = ? AND `status` != \'paid\'', [(int) $id]);

        try {
            WorkflowEngine::transitionByEntity('fines', (int) $id, 'pay');
        } catch (\Throwable $e) {
            Logger::warning("Workflow transition failed for fine pay", ['fine_id' => (int) $id, 'error' => $e->getMessage()]);
        }

        EventBus::dispatch('fine.paid', ['fine_id' => (int) $id, 'member_id' => (int) $fine['member_id'], 'amount' => $remaining]);

        return $this->redirect('/fines')->withSuccess('تم تسجيل دفع الغرامة — إيصال: ' . $result['receipt_number']);
    }

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

        $appealWindowData = RuleEngine::get('APPEAL_WINDOW');
        $appealDays = $appealWindowData['days'] ?? 15;
        $imposedAt = strtotime($fine['created_at']);
        $daysSinceImposed = (time() - $imposedAt) / 86400;

        if ($daysSinceImposed > $appealDays) {
            return $this->redirect('/fines')->withError("انتهت مهلة التظلم ({$appealDays} يوم)");
        }

        $appealNotes = trim((string) $request->post('appeal_notes', ''));
        if ($appealNotes === '') return $this->redirect('/fines')->withError('يجب إدخال أسباب التظلم');

        $db->update('fines', [
            'appeal_submitted' => 1,
            'appeal_date'      => date('Y-m-d'),
            'appeal_notes'     => $appealNotes,
            'status'           => 'appealed',
            'updated_at'       => date('Y-m-d H:i:s'),
        ], '`id` = ?', [(int) $id]);

        try {
            WorkflowEngine::transitionByEntity('fines', (int) $id, 'appeal');
        } catch (\Throwable $e) {
            Logger::warning("Workflow transition failed for fine appeal", ['fine_id' => (int) $id, 'error' => $e->getMessage()]);
        }

        EventBus::dispatch('fine.appealed', ['fine_id' => (int) $id, 'member_id' => (int) $fine['member_id']]);

        return $this->redirect('/fines')->withSuccess('تم تقديم التظلم');
    }

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

        $decision = trim($request->post('appeal_decision', ''));
        $validDecisions = ['upheld', 'modified', 'cancelled'];
        if (!in_array($decision, $validDecisions)) return $this->redirect('/fines')->withError('قرار غير صالح');

        $employee = App::getInstance()->currentEmployee();
        $newStatus = match ($decision) {
            'upheld'    => 'appeal_upheld',
            'modified'  => 'appeal_modified',
            'cancelled' => 'cancelled',
        };

        $updateData = [
            'appeal_decision'   => $decision,
            'appeal_decided_by' => $employee ? (int) $employee->id : null,
            'appeal_decided_at' => date('Y-m-d H:i:s'),
            'status'            => $newStatus,
            'updated_at'        => date('Y-m-d H:i:s'),
        ];

        if ($decision === 'modified') {
            $newAmount = trim($request->post('new_amount', $fine['amount']));
            if (is_numeric($newAmount)) $updateData['amount'] = $newAmount;
        }

        $db->update('fines', $updateData, '`id` = ?', [(int) $id]);

        $wfTransition = match ($decision) {
            'upheld'    => 'uphold_appeal',
            'modified'  => 'modify_appeal',
            'cancelled' => 'cancel_appeal',
        };
        try {
            WorkflowEngine::transitionByEntity('fines', (int) $id, $wfTransition);
        } catch (\Throwable $e) {
            Logger::warning("Workflow transition failed for fine {$wfTransition}", ['fine_id' => (int) $id, 'error' => $e->getMessage()]);
        }

        return $this->redirect('/fines')->withSuccess('تم البت في التظلم: ' . match($decision) { 'upheld' => 'تأييد العقوبة', 'modified' => 'تعديل العقوبة', 'cancelled' => 'إلغاء العقوبة' });
    }

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

        $reason = trim((string) $request->post('waive_reason', ''));
        if ($reason === '') return $this->redirect('/fines')->withError('يجب إدخال سبب الإعفاء');

        $db->update('fines', [
            'status'     => 'waived',
            'notes'      => ($fine['notes'] ? $fine['notes'] . "\n" : '') . 'إعفاء: ' . $reason,
            'updated_at' => date('Y-m-d H:i:s'),
        ], '`id` = ?', [(int) $id]);

        try {
            WorkflowEngine::transitionByEntity('fines', (int) $id, 'waive');
        } catch (\Throwable $e) {
            Logger::warning("Workflow transition failed for fine waive", ['fine_id' => (int) $id, 'error' => $e->getMessage()]);
        }

        return $this->redirect('/fines')->withSuccess('تم الإعفاء من الغرامة');
    }
}