<?php
declare(strict_types=1);

namespace App\Modules\Rentals\Controllers;

use App\Core\Controller;
use App\Core\Request;
use App\Core\Response;
use App\Core\App;
use App\Modules\Rentals\Models\RentalEntity;
use App\Modules\Rentals\Models\RentalContract;
use App\Modules\Rentals\Models\RentalBooking;
use App\Modules\Rentals\Services\RentalContractService;
use App\Modules\Rentals\Services\RentalDepositService;
use App\Modules\Rentals\Services\RentalInvoiceService;
use App\Modules\Rentals\Models\RentalInvoice;
use App\Modules\Facilities\Models\Facility;
use App\Modules\Rules\Services\RuleEngine;

class RentalController extends Controller
{
    // ─── Contracts ───────────────────────────────────────────────

    /**
     * List contracts with status tabs, entity filter, facility filter, search, pagination.
     */
    public function index(Request $request): Response
    {
        $filters = [
            'q'           => trim((string) $request->get('q', '')),
            'status'      => trim((string) $request->get('status', '')),
            'entity_id'   => trim((string) $request->get('entity_id', '')),
            'facility_id' => trim((string) $request->get('facility_id', '')),
        ];

        $page   = max(1, (int) $request->get('page', 1));
        $result = RentalContract::search($filters, 25, $page);

        // Load entities and facilities for filter dropdowns
        $entities   = RentalEntity::search([], 1000, 1)['data'] ?? [];
        $facilities = Facility::allActive();

        // Load entity names map for display
        $entityMap = [];
        foreach ($entities as $e) {
            $entityMap[(int) $e['id']] = $e['name_ar'];
        }

        // Load facility names map for display
        $facilityMap = [];
        foreach ($facilities as $f) {
            $facilityMap[(int) $f['id']] = $f['name_ar'];
        }

        return $this->view('Rentals.Views.index', [
            'contracts'   => $result['data'],
            'pagination'  => $result['pagination'],
            'filters'     => $filters,
            'statuses'    => RentalContract::getStatuses(),
            'entities'    => $entities,
            'facilities'  => $facilities,
            'entityMap'   => $entityMap,
            'facilityMap' => $facilityMap,
        ]);
    }

    // ─── Entities ────────────────────────────────────────────────

    /**
     * List rental entities.
     */
    public function entities(Request $request): Response
    {
        $filters = [
            'q'           => trim((string) $request->get('q', '')),
            'entity_type' => trim((string) $request->get('entity_type', '')),
            'status'      => trim((string) $request->get('status', '')),
        ];

        $page   = max(1, (int) $request->get('page', 1));
        $result = RentalEntity::search($filters, 25, $page);

        return $this->view('Rentals.Views.entities', [
            'entities'    => $result['data'],
            'pagination'  => $result['pagination'],
            'filters'     => $filters,
            'entityTypes' => RentalEntity::getEntityTypes(),
            'statuses'    => RentalEntity::getStatuses(),
        ]);
    }

    /**
     * Show the create entity form.
     */
    public function createEntity(Request $request): Response
    {
        return $this->view('Rentals.Views.entity_form', [
            'entity'      => null,
            'entityTypes' => RentalEntity::getEntityTypes(),
            'statuses'    => RentalEntity::getStatuses(),
        ]);
    }

    /**
     * Validate and store a new rental entity.
     */
    public function storeEntity(Request $request): Response
    {
        $data = $this->extractEntityData($request);
        $errors = $this->validateEntityData($data);

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

        $entity = RentalEntity::create($data);

        return $this->redirect('/rentals/entities/' . $entity->id)->withSuccess('تم إضافة الجهة المستأجرة بنجاح');
    }

    /**
     * Show entity detail page.
     */
    public function showEntity(Request $request, string $id): Response
    {
        $entity = RentalEntity::find((int) $id);
        if (!$entity) {
            return $this->redirect('/rentals/entities')->withError('الجهة المستأجرة غير موجودة');
        }

        $contracts = RentalContract::getForEntity((int) $id);

        return $this->view('Rentals.Views.entity_show', [
            'entity'    => $entity,
            'contracts' => $contracts,
        ]);
    }

    /**
     * Show edit form for an entity.
     */
    public function editEntity(Request $request, string $id): Response
    {
        $entity = RentalEntity::find((int) $id);
        if (!$entity) {
            return $this->redirect('/rentals/entities')->withError('الجهة المستأجرة غير موجودة');
        }

        return $this->view('Rentals.Views.entity_form', [
            'entity'      => $entity,
            'entityTypes' => RentalEntity::getEntityTypes(),
            'statuses'    => RentalEntity::getStatuses(),
        ]);
    }

    /**
     * Validate and update an existing entity.
     */
    public function updateEntity(Request $request, string $id): Response
    {
        $entity = RentalEntity::find((int) $id);
        if (!$entity) {
            return $this->redirect('/rentals/entities')->withError('الجهة المستأجرة غير موجودة');
        }

        $data = $this->extractEntityData($request);
        $errors = $this->validateEntityData($data);

        if (!empty($errors)) {
            $session = App::getInstance()->session();
            $session->flash('_alerts', array_map(fn($e) => ['type' => 'error', 'message' => $e], $errors));
            $session->flash('_old_input', $request->all());
            return $this->redirect('/rentals/entities/' . $id . '/edit');
        }

        $entity->update($data);

        return $this->redirect('/rentals/entities/' . $id)->withSuccess('تم تحديث الجهة المستأجرة بنجاح');
    }

    // ─── Contracts CRUD ──────────────────────────────────────────

    /**
     * Show the create contract form.
     */
    public function createContract(Request $request): Response
    {
        $entities   = RentalEntity::search(['status' => 'active'], 1000, 1)['data'] ?? [];
        $facilities = Facility::allActive();
        $defaultVat = (float) (RuleEngine::getValue('RENTAL_VAT_PCT') ?? 1.00);
        $bankRate   = (float) (RuleEngine::getValue('RENTAL_LATE_FEE_BANK_RATE', 'annual_rate') ?? 27.25);

        return $this->view('Rentals.Views.contract_form', [
            'contract'          => null,
            'entities'          => $entities,
            'facilities'        => $facilities,
            'statuses'          => RentalContract::getStatuses(),
            'defaultVat'        => $defaultVat,
            'bankRate'          => $bankRate,
            'escalationTypes'   => RentalContract::getEscalationTypes(),
            'utilitiesModes'    => RentalContract::getUtilitiesModes(),
        ]);
    }

    /**
     * Validate and store a new rental contract.
     */
    public function storeContract(Request $request): Response
    {
        $entityId            = (int) $request->post('entity_id', 0);
        $facilityId          = (int) $request->post('facility_id', 0);
        $activityType        = trim((string) $request->post('activity_type', ''));
        $startDate           = trim((string) $request->post('start_date', ''));
        $endDate             = trim((string) $request->post('end_date', ''));
        $totalUnits          = (int) $request->post('total_units', 0);
        $unitRate            = (float) $request->post('unit_rate', 0);
        $timeTier            = trim((string) $request->post('time_tier', 'AM'));
        $depositPercentage   = (float) $request->post('deposit_percentage', 0);
        $vatPercentage       = (float) $request->post('vat_percentage', 1.00);
        $utilitiesPercentage = (float) $request->post('utilities_percentage', 0.00);
        $lateFeeType         = trim((string) $request->post('late_fee_type', 'none'));
        $lateFeeRate         = (float) $request->post('late_fee_rate', 0.00);
        $notes               = trim((string) $request->post('notes', ''));

        // New escalation / utilities / terms fields
        $escalationType        = trim((string) $request->post('escalation_type', 'none'));
        $escalationRate        = (float) $request->post('escalation_rate', 0.00);
        $escalationTiersJson   = trim((string) $request->post('escalation_tiers_json', ''));
        $utilitiesMode         = trim((string) $request->post('utilities_mode', 'rent_pct'));
        $utilitiesRentPct      = (float) $request->post('utilities_rent_pct', 0.00);
        $utilitiesFacilityPct  = (float) $request->post('utilities_facility_pct', 0.00);
        $facilityMonthlyCost   = $request->post('facility_monthly_cost', '') !== '' ? (float) $request->post('facility_monthly_cost', 0) : null;
        $paymentDueDay         = (int) $request->post('payment_due_day', 5);
        $lateFeeBankRate       = (float) $request->post('late_fee_bank_rate', 0.00);
        $gracePeriodMonths     = (int) $request->post('grace_period_months', 0);
        $earlyTerminationMonths = (int) $request->post('early_termination_months', 0);

        // Validation
        $errors = [];

        $entity = RentalEntity::find($entityId);
        if (!$entity) {
            $errors[] = 'يجب اختيار جهة مستأجرة صالحة';
        }

        $facility = Facility::find($facilityId);
        if (!$facility) {
            $errors[] = 'يجب اختيار مرفق صالح';
        }

        if ($startDate === '' || $endDate === '') {
            $errors[] = 'تاريخ البدء والانتهاء مطلوبان';
        } elseif ($startDate >= $endDate) {
            $errors[] = 'تاريخ الانتهاء يجب أن يكون بعد تاريخ البدء';
        }

        if ($totalUnits <= 0) {
            $errors[] = 'عدد الوحدات يجب أن يكون أكبر من صفر';
        }

        if ($unitRate <= 0) {
            $errors[] = 'سعر الوحدة يجب أن يكون أكبر من صفر';
        }

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

        $contract = RentalContractService::createContract([
            'entity_id'               => $entityId,
            'facility_id'             => $facilityId,
            'activity_type'           => $activityType ?: null,
            'start_date'              => $startDate,
            'end_date'                => $endDate,
            'total_units'             => $totalUnits,
            'unit_rate'               => $unitRate,
            'time_tier'               => $timeTier,
            'vat_percentage'          => $vatPercentage,
            'utilities_percentage'    => $utilitiesPercentage,
            'late_fee_type'           => $lateFeeType,
            'late_fee_rate'           => $lateFeeRate,
            'escalation_type'         => $escalationType,
            'escalation_rate'         => $escalationRate,
            'escalation_tiers_json'   => $escalationTiersJson ?: null,
            'utilities_mode'          => $utilitiesMode,
            'utilities_rent_pct'      => $utilitiesRentPct,
            'utilities_facility_pct'  => $utilitiesFacilityPct,
            'facility_monthly_cost'   => $facilityMonthlyCost,
            'payment_due_day'         => $paymentDueDay,
            'late_fee_bank_rate'      => $lateFeeBankRate,
            'grace_period_months'     => $gracePeriodMonths,
            'early_termination_months'=> $earlyTerminationMonths,
            'deposit_percentage'      => $depositPercentage,
            'notes'                   => $notes ?: null,
        ]);

        return $this->redirect('/rentals/contracts/' . $contract->id)->withSuccess('تم إنشاء العقد بنجاح');
    }

    /**
     * Show contract detail page with bookings and deposit status.
     */
    public function showContract(Request $request, string $id): Response
    {
        $contract = RentalContract::find((int) $id);
        if (!$contract) {
            return $this->redirect('/rentals')->withError('العقد غير موجود');
        }

        $entityId   = (int) $contract->entity_id;
        $facilityId = (int) $contract->facility_id;

        $entity   = RentalEntity::find($entityId);
        $facility = Facility::find($facilityId);
        $bookings = RentalBooking::getForContract((int) $id);
        $invoices = RentalInvoice::getForContract((int) $id);

        return $this->view('Rentals.Views.contract_show', [
            'contract' => $contract,
            'entity'   => $entity,
            'facility' => $facility,
            'bookings' => $bookings,
            'invoices' => $invoices,
        ]);
    }

    /**
     * Approve a rental contract.
     */
    public function approveContract(Request $request, string $id): Response
    {
        $contract = RentalContract::find((int) $id);
        if (!$contract) {
            return $this->redirect('/rentals')->withError('العقد غير موجود');
        }

        $employee = App::getInstance()->currentEmployee();
        $contract->update([
            'status'      => 'approved',
            'approved_by' => $employee ? (int) $employee->id : null,
            'approved_at' => date('Y-m-d H:i:s'),
        ]);

        return $this->redirect('/rentals/contracts/' . $id)->withSuccess('تم اعتماد العقد بنجاح');
    }

    /**
     * Collect deposit for a contract.
     */
    public function collectDeposit(Request $request, string $id): Response
    {
        $paymentId = (int) $request->post('payment_id', 0);
        if ($paymentId <= 0) {
            return $this->redirect('/rentals/contracts/' . $id)->withError('رقم الدفعة مطلوب');
        }

        try {
            RentalDepositService::collectDeposit((int) $id, $paymentId);
            return $this->redirect('/rentals/contracts/' . $id)->withSuccess('تم تحصيل التأمين بنجاح');
        } catch (\RuntimeException $e) {
            return $this->redirect('/rentals/contracts/' . $id)->withError($e->getMessage());
        }
    }

    /**
     * Refund deposit for a contract.
     */
    public function refundDeposit(Request $request, string $id): Response
    {
        $paymentId = (int) $request->post('payment_id', 0);

        try {
            if ($paymentId > 0) {
                RentalDepositService::processRefund((int) $id, $paymentId);
            } else {
                RentalDepositService::requestRefund((int) $id);
            }
            return $this->redirect('/rentals/contracts/' . $id)->withSuccess('تم معالجة استرداد التأمين بنجاح');
        } catch (\RuntimeException $e) {
            return $this->redirect('/rentals/contracts/' . $id)->withError($e->getMessage());
        }
    }

    // ─── Invoices ────────────────────────────────────────────────

    /**
     * Show form to generate a new invoice for a contract.
     */
    public function createInvoice(Request $request, string $id): Response
    {
        $contract = RentalContract::find((int) $id);
        if (!$contract) {
            return $this->redirect('/rentals')->withError('العقد غير موجود');
        }

        $monthly = RentalInvoiceService::calcBase($contract);
        $utils   = RentalInvoiceService::calcUtilities($contract, $monthly);
        $vat     = RentalInvoiceService::calcVat($contract, $monthly + $utils);

        return $this->view('Rentals.Views.invoice_form', [
            'contract'       => $contract,
            'monthly_base'   => $monthly,
            'monthly_utils'  => $utils,
            'monthly_vat'    => $vat,
            'monthly_total'  => round($monthly + $utils + $vat, 2),
        ]);
    }

    /**
     * Bulk-generate all monthly invoices from contract start to end, skipping existing ones.
     */
    public function bulkGenerateInvoices(Request $request, string $id): Response
    {
        $contract = RentalContract::find((int) $id);
        if (!$contract) {
            return $this->redirect('/rentals')->withError('العقد غير موجود');
        }

        try {
            $count = RentalInvoiceService::bulkGenerateInvoices((int) $id);
            if ($count === 0) {
                return $this->redirect('/rentals/contracts/' . $id)->withWarning('جميع الفواتير الشهرية موجودة مسبقاً');
            }
            return $this->redirect('/rentals/contracts/' . $id)->withSuccess('تم توليد ' . $count . ' فاتورة بنجاح');
        } catch (\RuntimeException $e) {
            return $this->redirect('/rentals/contracts/' . $id)->withError($e->getMessage());
        }
    }

    /**
     * Generate and store a new invoice.
     */
    public function storeInvoice(Request $request, string $id): Response
    {
        $contract = RentalContract::find((int) $id);
        if (!$contract) {
            return $this->redirect('/rentals')->withError('العقد غير موجود');
        }

        $periodStart = trim((string) $request->post('period_start', ''));
        $periodEnd   = trim((string) $request->post('period_end', ''));
        $dueDate     = trim((string) $request->post('due_date', ''));
        $notes       = trim((string) $request->post('notes', '')) ?: null;

        $errors = [];
        if (!$periodStart || !$periodEnd || !$dueDate) {
            $errors[] = 'تاريخ الفترة وتاريخ الاستحقاق مطلوبة';
        }

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

        try {
            $invoice = RentalInvoiceService::generateInvoice((int) $id, $periodStart, $periodEnd, $dueDate, $notes);
            return $this->redirect('/rentals/invoices/' . $invoice->id)->withSuccess('تم إنشاء الفاتورة بنجاح');
        } catch (\RuntimeException $e) {
            return $this->redirect('/rentals/contracts/' . $id)->withError($e->getMessage());
        }
    }

    /**
     * Show invoice detail.
     */
    public function showInvoice(Request $request, string $id): Response
    {
        $invoice = RentalInvoice::find((int) $id);
        if (!$invoice) {
            return $this->redirect('/rentals')->withError('الفاتورة غير موجودة');
        }

        $contractId = (int) $invoice->contract_id;
        $contract   = RentalContract::find($contractId);
        $entity     = $contract ? RentalEntity::find((int) $contract->entity_id) : null;

        return $this->view('Rentals.Views.invoice_show', [
            'invoice'  => $invoice,
            'contract' => $contract,
            'entity'   => $entity,
        ]);
    }

    /**
     * Mark invoice as paid.
     */
    public function payInvoice(Request $request, string $id): Response
    {
        $paymentId = (int) $request->post('payment_id', 0);
        $paidAt    = trim((string) $request->post('paid_at', date('Y-m-d H:i:s')));

        if ($paymentId <= 0) {
            return $this->redirect('/rentals/invoices/' . $id)->withError('رقم الدفعة مطلوب');
        }

        try {
            RentalInvoiceService::markPaid((int) $id, $paymentId, $paidAt);
            return $this->redirect('/rentals/invoices/' . $id)->withSuccess('تم تسجيل الدفع بنجاح');
        } catch (\RuntimeException $e) {
            return $this->redirect('/rentals/invoices/' . $id)->withError($e->getMessage());
        }
    }

    // ─── Private helpers ─────────────────────────────────────────

    /**
     * Extract entity data from request.
     */
    private function extractEntityData(Request $request): array
    {
        return [
            'entity_type'              => trim((string) $request->post('entity_type', '')),
            'name_ar'                  => trim((string) $request->post('name_ar', '')),
            'name_en'                  => trim((string) $request->post('name_en', '')) ?: null,
            'contact_person'           => trim((string) $request->post('contact_person', '')) ?: null,
            'contact_phone'            => trim((string) $request->post('contact_phone', '')) ?: null,
            'contact_email'            => trim((string) $request->post('contact_email', '')) ?: null,
            'address'                  => trim((string) $request->post('address', '')) ?: null,
            'tax_number'               => trim((string) $request->post('tax_number', '')) ?: null,
            'official_letter_received' => (int) $request->post('official_letter_received', 0),
            'board_approved'           => (int) $request->post('board_approved', 0),
            'approval_date'            => trim((string) $request->post('approval_date', '')) ?: null,
            'status'                   => trim((string) $request->post('status', 'pending')),
            'notes'                    => trim((string) $request->post('notes', '')) ?: null,
        ];
    }

    /**
     * Validate entity data, return errors array.
     */
    private function validateEntityData(array $data): array
    {
        $errors = [];

        if (empty($data['name_ar']) || mb_strlen($data['name_ar']) < 2) {
            $errors[] = 'اسم الجهة بالعربي مطلوب (حرفان على الأقل)';
        }

        if (!array_key_exists($data['entity_type'], RentalEntity::getEntityTypes())) {
            $errors[] = 'نوع الجهة غير صالح';
        }

        if (!array_key_exists($data['status'], RentalEntity::getStatuses())) {
            $errors[] = 'الحالة غير صالحة';
        }

        return $errors;
    }
}
