<?php
declare(strict_types=1);

namespace App\Modules\Reports\Services;

use App\Core\App;

final class ReportEngine
{
    public static function generate(string $reportCode, array $filters): array
    {
        $db = App::getInstance()->db();

        switch ($reportCode) {
            case 'RPT_MEMBERSHIP_REGISTER':
                return self::membershipRegister($db, $filters);
            case 'RPT_NEW_MEMBERSHIPS':
                return self::newMemberships($db, $filters);
            case 'RPT_CHILDREN':
                return self::childrenReport($db, $filters);
            case 'RPT_CHILDREN_18':
                return self::childrenApproaching($db, $filters, 18);
            case 'RPT_CHILDREN_25':
                return self::childrenApproaching($db, $filters, 25);
            case 'RPT_SPOUSES':
                return self::spousesReport($db, $filters);
            case 'RPT_REVENUE':
                return self::revenueReport($db, $filters);
            case 'RPT_OUTSTANDING':
                return self::outstandingReport($db, $filters);
            case 'RPT_INSTALLMENTS':
                return self::installmentsReport($db, $filters);
            case 'RPT_SUBSCRIPTIONS':
                return self::subscriptionsReport($db, $filters);
            case 'RPT_OVERDUE_SUBSCRIPTIONS':
                return self::overdueSubscriptionsReport($db, $filters);
            case 'RPT_FINES':
                return self::finesReport($db, $filters);
            case 'RPT_DAILY_CASH':
                return self::dailyCashReport($db, $filters);
            case 'RPT_VOIDED_RECEIPTS':
                return self::voidedReceiptsReport($db, $filters);
            case 'RPT_EMPLOYEE_ACTIVITY':
                return self::employeeActivityReport($db, $filters);
            case 'RPT_CARNET_PRINTS':
                return self::carnetPrintsReport($db, $filters);
            case 'RPT_TRANSFERS':
                return self::transfersReport($db, $filters);
            case 'RPT_TEMPORARY':
                return self::temporaryReport($db, $filters);
            default:
                return self::genericReport($db, $reportCode, $filters);
        }
    }

    private static function applyDateFilter(string &$where, array &$params, array $filters, string $dateCol): void
    {
        if (!empty($filters['date_from'])) { $where .= " AND {$dateCol} >= ?"; $params[] = $filters['date_from']; }
        if (!empty($filters['date_to'])) { $where .= " AND {$dateCol} <= ?"; $params[] = $filters['date_to'] . ' 23:59:59'; }
    }

    private static function applyBranchFilter(string &$where, array &$params, array $filters, string $col = 'm.branch_id'): void
    {
        if (!empty($filters['branch_id'])) { $where .= " AND {$col} = ?"; $params[] = (int) $filters['branch_id']; }
    }

    private static function membershipRegister($db, array $f): array
    {
        $w = 'm.is_archived = 0'; $p = [];
        self::applyDateFilter($w, $p, $f, 'm.created_at');
        self::applyBranchFilter($w, $p, $f);
        if (!empty($f['status'])) { $w .= ' AND m.status = ?'; $p[] = $f['status']; }
        $rows = $db->select("SELECT m.*, b.name_ar as branch_name FROM members m LEFT JOIN branches b ON b.id = m.branch_id WHERE {$w} ORDER BY m.membership_number ASC", $p);
        return ['columns' => ['membership_number' => 'رقم العضوية', 'full_name_ar' => 'الاسم', 'national_id' => 'الرقم القومي', 'phone_mobile' => 'المحمول', 'status' => 'الحالة', 'branch_name' => 'الفرع', 'membership_type' => 'النوع', 'created_at' => 'تاريخ الإنشاء'], 'rows' => $rows, 'total' => count($rows)];
    }

    private static function newMemberships($db, array $f): array
    {
        $w = "m.is_archived = 0"; $p = [];
        self::applyDateFilter($w, $p, $f, 'm.created_at');
        self::applyBranchFilter($w, $p, $f);
        $rows = $db->select("SELECT m.full_name_ar, m.membership_number, m.national_id, m.membership_type, m.membership_value, m.status, m.created_at, b.name_ar as branch_name FROM members m LEFT JOIN branches b ON b.id = m.branch_id WHERE {$w} ORDER BY m.created_at DESC", $p);
        return ['columns' => ['full_name_ar' => 'الاسم', 'membership_number' => 'رقم العضوية', 'membership_type' => 'النوع', 'membership_value' => 'القيمة', 'status' => 'الحالة', 'branch_name' => 'الفرع', 'created_at' => 'التاريخ'], 'rows' => $rows, 'total' => count($rows)];
    }

    private static function childrenReport($db, array $f): array
    {
        $w = 'c.is_archived = 0'; $p = [];
        self::applyDateFilter($w, $p, $f, 'c.created_at');
        $rows = $db->select("SELECT c.full_name_ar, c.date_of_birth, c.age_years, c.gender, c.classification, c.status, c.is_frozen, m.full_name_ar as member_name, m.membership_number FROM children c JOIN members m ON m.id = c.member_id WHERE {$w} ORDER BY c.age_years DESC", $p);
        return ['columns' => ['full_name_ar' => 'اسم الابن', 'member_name' => 'صاحب العضوية', 'membership_number' => 'رقم العضوية', 'date_of_birth' => 'تاريخ الميلاد', 'age_years' => 'السن', 'gender' => 'النوع', 'classification' => 'التصنيف', 'status' => 'الحالة'], 'rows' => $rows, 'total' => count($rows)];
    }

    private static function childrenApproaching($db, array $f, int $age): array
    {
        $monthsAhead = 3;
        $targetDate = date('Y-m-d', strtotime("-{$age} years +{$monthsAhead} months"));
        $targetDateNow = date('Y-m-d', strtotime("-{$age} years"));
        $rows = $db->select("SELECT c.full_name_ar, c.date_of_birth, c.age_years, c.gender, c.classification, m.full_name_ar as member_name, m.membership_number, m.phone_mobile FROM children c JOIN members m ON m.id = c.member_id WHERE c.is_archived = 0 AND c.date_of_birth BETWEEN ? AND ? ORDER BY c.date_of_birth ASC", [$targetDateNow, $targetDate]);
        return ['columns' => ['full_name_ar' => 'اسم الابن', 'member_name' => 'صاحب العضوية', 'membership_number' => 'رقم العضوية', 'date_of_birth' => 'تاريخ الميلاد', 'age_years' => 'السن', 'phone_mobile' => 'المحمول'], 'rows' => $rows, 'total' => count($rows)];
    }

    private static function spousesReport($db, array $f): array
    {
        $w = 's.is_archived = 0'; $p = [];
        $rows = $db->select("SELECT s.full_name_ar, s.spouse_order, s.classification, s.addition_fee, s.status, m.full_name_ar as member_name, m.membership_number FROM spouses s JOIN members m ON m.id = s.member_id WHERE {$w} ORDER BY m.membership_number, s.spouse_order", $p);
        return ['columns' => ['member_name' => 'العضو', 'membership_number' => 'رقم العضوية', 'full_name_ar' => 'اسم الزوج/ة', 'spouse_order' => 'الترتيب', 'classification' => 'التصنيف', 'addition_fee' => 'الرسوم', 'status' => 'الحالة'], 'rows' => $rows, 'total' => count($rows)];
    }

    private static function revenueReport($db, array $f): array
    {
        $w = 'p.is_voided = 0'; $p = [];
        self::applyDateFilter($w, $p, $f, 'p.payment_date');
        $rows = $db->select("SELECT p.payment_type, COUNT(*) as count, SUM(p.amount) as total_amount FROM payments p WHERE {$w} GROUP BY p.payment_type ORDER BY total_amount DESC", $p);
        return ['columns' => ['payment_type' => 'نوع الدفع', 'count' => 'العدد', 'total_amount' => 'الإجمالي'], 'rows' => $rows, 'total' => count($rows)];
    }

    private static function outstandingReport($db, array $f): array
    {
        $rows = $db->select("SELECT m.full_name_ar, m.membership_number, m.phone_mobile, COALESCE((SELECT SUM(s.total_amount - s.paid_amount) FROM subscriptions s WHERE s.member_id = m.id AND s.status IN ('pending','overdue')), 0) as sub_outstanding, COALESCE((SELECT SUM(f.amount - f.paid_amount) FROM fines f WHERE f.member_id = m.id AND f.status IN ('imposed','appeal_upheld')), 0) as fine_outstanding FROM members m WHERE m.is_archived = 0 AND m.status = 'active' HAVING (sub_outstanding + fine_outstanding) > 0 ORDER BY (sub_outstanding + fine_outstanding) DESC");
        return ['columns' => ['full_name_ar' => 'الاسم', 'membership_number' => 'رقم العضوية', 'phone_mobile' => 'المحمول', 'sub_outstanding' => 'اشتراكات مستحقة', 'fine_outstanding' => 'غرامات مستحقة'], 'rows' => $rows, 'total' => count($rows)];
    }

    private static function installmentsReport($db, array $f): array
    {
        $w = '1=1'; $p = [];
        if (!empty($f['status'])) { $w .= ' AND p.status = ?'; $p[] = $f['status']; }
        $rows = $db->select("SELECT p.*, m.full_name_ar as member_name, m.membership_number, (SELECT COUNT(*) FROM installment_schedule s WHERE s.installment_plan_id = p.id AND s.status = 'pending' AND s.due_date < CURDATE()) as overdue_count FROM installment_plans p JOIN members m ON m.id = p.member_id WHERE {$w} ORDER BY p.created_at DESC", $p);
        return ['columns' => ['member_name' => 'العضو', 'membership_number' => 'رقم العضوية', 'total_amount' => 'الإجمالي', 'down_payment' => 'المقدم', 'monthly_payment' => 'القسط الشهري', 'number_of_months' => 'الأشهر', 'status' => 'الحالة', 'overdue_count' => 'أقساط متأخرة'], 'rows' => $rows, 'total' => count($rows)];
    }

    private static function subscriptionsReport($db, array $f): array
    {
        $w = '1=1'; $p = [];
        if (!empty($f['status'])) { $w .= ' AND s.status = ?'; $p[] = $f['status']; }
        $rows = $db->select("SELECT s.financial_year, s.person_type, s.person_name, s.total_amount, s.paid_amount, s.fine_amount, s.status, m.full_name_ar as member_name, m.membership_number FROM subscriptions s JOIN members m ON m.id = s.member_id WHERE {$w} ORDER BY s.financial_year DESC, m.membership_number", $p);
        return ['columns' => ['member_name' => 'العضو', 'membership_number' => 'رقم العضوية', 'financial_year' => 'السنة المالية', 'person_type' => 'النوع', 'total_amount' => 'المبلغ', 'paid_amount' => 'المدفوع', 'fine_amount' => 'الغرامة', 'status' => 'الحالة'], 'rows' => $rows, 'total' => count($rows)];
    }

    private static function overdueSubscriptionsReport($db, array $f): array
    {
        $rows = $db->select("SELECT s.financial_year, s.total_amount, s.fine_amount, m.full_name_ar as member_name, m.membership_number, m.phone_mobile FROM subscriptions s JOIN members m ON m.id = s.member_id WHERE s.status IN ('pending','overdue') AND m.is_archived = 0 ORDER BY s.financial_year ASC, m.membership_number");
        return ['columns' => ['member_name' => 'العضو', 'membership_number' => 'رقم العضوية', 'phone_mobile' => 'المحمول', 'financial_year' => 'السنة المالية', 'total_amount' => 'المبلغ', 'fine_amount' => 'الغرامة'], 'rows' => $rows, 'total' => count($rows)];
    }

    private static function finesReport($db, array $f): array
    {
        $w = '1=1'; $p = [];
        self::applyDateFilter($w, $p, $f, 'f.created_at');
        $rows = $db->select("SELECT f.*, m.full_name_ar as member_name, m.membership_number FROM fines f JOIN members m ON m.id = f.member_id WHERE {$w} ORDER BY f.created_at DESC", $p);
        return ['columns' => ['member_name' => 'العضو', 'membership_number' => 'رقم العضوية', 'penalty_type' => 'نوع العقوبة', 'amount' => 'المبلغ', 'status' => 'الحالة', 'created_at' => 'التاريخ'], 'rows' => $rows, 'total' => count($rows)];
    }

    private static function dailyCashReport($db, array $f): array
    {
        $date = $f['date_from'] ?: date('Y-m-d');
        $rows = $db->select("SELECT r.receipt_number, r.amount, r.description_ar, r.receipt_type, r.is_voided, r.issued_at, m.full_name_ar as member_name, e.full_name_ar as cashier FROM receipts r JOIN members m ON m.id = r.member_id LEFT JOIN employees e ON e.id = r.issued_by_employee_id WHERE DATE(r.issued_at) = ? ORDER BY r.issued_at ASC", [$date]);
        $totalValid = array_sum(array_map(fn($r) => $r['is_voided'] ? 0 : (float) $r['amount'], $rows));
        return ['columns' => ['receipt_number' => 'رقم الإيصال', 'member_name' => 'العضو', 'amount' => 'المبلغ', 'description_ar' => 'الوصف', 'cashier' => 'أمين الخزينة', 'is_voided' => 'ملغى', 'issued_at' => 'الوقت'], 'rows' => $rows, 'total' => count($rows), 'summary' => ['total_valid' => $totalValid, 'date' => $date]];
    }

    private static function voidedReceiptsReport($db, array $f): array
    {
        $w = 'r.is_voided = 1'; $p = [];
        self::applyDateFilter($w, $p, $f, 'r.voided_at');
        $rows = $db->select("SELECT r.receipt_number, r.amount, r.void_reason, r.voided_at, m.full_name_ar as member_name, e.full_name_ar as voided_by_name FROM receipts r JOIN members m ON m.id = r.member_id LEFT JOIN employees e ON e.id = r.voided_by WHERE {$w} ORDER BY r.voided_at DESC", $p);
        return ['columns' => ['receipt_number' => 'رقم الإيصال', 'member_name' => 'العضو', 'amount' => 'المبلغ', 'void_reason' => 'السبب', 'voided_by_name' => 'بواسطة', 'voided_at' => 'تاريخ الإلغاء'], 'rows' => $rows, 'total' => count($rows)];
    }

    private static function employeeActivityReport($db, array $f): array
    {
        $w = '1=1'; $p = [];
        self::applyDateFilter($w, $p, $f, 'a.created_at');
        $rows = $db->select("SELECT a.employee_name, a.action, COUNT(*) as action_count, MAX(a.created_at) as last_action FROM audit_trail a WHERE {$w} GROUP BY a.employee_id, a.employee_name, a.action ORDER BY action_count DESC", $p);
        return ['columns' => ['employee_name' => 'الموظف', 'action' => 'الإجراء', 'action_count' => 'العدد', 'last_action' => 'آخر نشاط'], 'rows' => $rows, 'total' => count($rows)];
    }

    private static function carnetPrintsReport($db, array $f): array
    {
        $w = 'c.print_count > 0'; $p = [];
        self::applyDateFilter($w, $p, $f, 'c.last_printed_at');
        $rows = $db->select("SELECT c.carnet_number, c.print_count, c.last_printed_at, m.full_name_ar as member_name, m.membership_number, e.full_name_ar as printed_by FROM carnets c JOIN members m ON m.id = c.member_id LEFT JOIN employees e ON e.id = c.last_printed_by WHERE {$w} ORDER BY c.last_printed_at DESC", $p);
        return ['columns' => ['carnet_number' => 'رقم الكارنيه', 'member_name' => 'العضو', 'membership_number' => 'رقم العضوية', 'print_count' => 'مرات الطباعة', 'printed_by' => 'طُبع بواسطة', 'last_printed_at' => 'آخر طباعة'], 'rows' => $rows, 'total' => count($rows)];
    }

    private static function transfersReport($db, array $f): array
    {
        $w = '1=1'; $p = [];
        self::applyDateFilter($w, $p, $f, 't.created_at');
        $rows = $db->select("SELECT t.transfer_type, t.status, t.total_fee, t.source_membership_number, t.new_membership_number, t.created_at, m.full_name_ar as source_name FROM transfer_requests t JOIN members m ON m.id = t.source_member_id WHERE {$w} ORDER BY t.created_at DESC", $p);
        return ['columns' => ['source_name' => 'العضو المصدر', 'source_membership_number' => 'رقم العضوية القديم', 'new_membership_number' => 'رقم العضوية الجديد', 'transfer_type' => 'النوع', 'total_fee' => 'الرسوم', 'status' => 'الحالة', 'created_at' => 'التاريخ'], 'rows' => $rows, 'total' => count($rows)];
    }

    private static function temporaryReport($db, array $f): array
    {
        $rows = $db->select("SELECT t.full_name_ar, t.category, t.age_years, t.status, t.addition_fee, m.full_name_ar as member_name, m.membership_number FROM temporary_members t JOIN members m ON m.id = t.member_id WHERE t.is_archived = 0 ORDER BY t.category, m.membership_number");
        return ['columns' => ['member_name' => 'العضو', 'membership_number' => 'رقم العضوية', 'full_name_ar' => 'اسم العضو المؤقت', 'category' => 'الفئة', 'age_years' => 'السن', 'addition_fee' => 'الرسوم', 'status' => 'الحالة'], 'rows' => $rows, 'total' => count($rows)];
    }

    private static function genericReport($db, string $code, array $f): array
    {
        return ['columns' => [], 'rows' => [], 'total' => 0, 'message' => 'التقرير غير مُعرّف بعد: ' . $code];
    }
}