<?php
declare(strict_types=1);

namespace App\Modules\Audit\Services;

use App\Core\App;
use App\Core\Logger;

final class AuditService
{
    /**
     * Tables we NEVER audit (they'd cause infinite loops or are just noise).
     */
    private const IGNORE_TABLES = [
        'audit_trail',
        'login_attempts',
        'active_sessions',
        'migrations',
        'seeds',
        'async_event_queue',
        'cron_job_log',
        'password_history',
        'sms_log',
        'notification_queue',
    ];

    /**
     * Sensitive fields — values are masked in audit logs.
     */
    private const SENSITIVE_FIELDS = [
        'password_hash',
        'password',
    ];

    /**
     * Fields to skip in change tracking (they change on every save and add noise).
     */
    private const NOISE_FIELDS = [
        'updated_at',
    ];

    /**
     * Log an audit entry directly.
     */
    public static function log(
        string $action,
        ?string $entityType = null,
        ?int $entityId = null,
        ?string $entityLabel = null,
        ?array $before = null,
        ?array $after = null,
        ?string $notes = null
    ): void {
        try {
            $app = App::getInstance();
            $db = $app->db();
            $employee = $app->currentEmployee();
            $session = $app->session();

            // Calculate changed fields
            $changedFields = null;
            if ($before !== null && $after !== null) {
                $changed = self::computeChangedFields($before, $after);
                if (!empty($changed)) {
                    $changedFields = $changed;
                }
            }

            // Mask sensitive fields
            if ($before !== null) $before = self::maskSensitive($before);
            if ($after !== null) $after = self::maskSensitive($after);

            $data = [
                'employee_id'         => $employee ? ($employee->id ?? null) : null,
                'employee_name'       => $employee ? ($employee->full_name_ar ?? null) : null,
                'action'              => $action,
                'entity_type'         => $entityType,
                'entity_id'           => $entityId,
                'entity_label'        => $entityLabel ? mb_substr($entityLabel, 0, 255) : null,
                'before_data_json'    => $before !== null ? json_encode($before, JSON_UNESCAPED_UNICODE | JSON_PARTIAL_OUTPUT_ON_ERROR) : null,
                'after_data_json'     => $after !== null ? json_encode($after, JSON_UNESCAPED_UNICODE | JSON_PARTIAL_OUTPUT_ON_ERROR) : null,
                'changed_fields_json' => $changedFields !== null ? json_encode($changedFields, JSON_UNESCAPED_UNICODE) : null,
                'ip_address'          => $_SERVER['REMOTE_ADDR'] ?? null,
                'user_agent'          => isset($_SERVER['HTTP_USER_AGENT']) ? mb_substr($_SERVER['HTTP_USER_AGENT'], 0, 500) : null,
                'session_id'          => $session ? ($session->id() ?: null) : null,
                'route'               => $_SERVER['REQUEST_URI'] ?? null,
                'notes'               => $notes,
                'created_at'          => date('Y-m-d H:i:s'),
            ];

            // Direct insert — bypasses hooks to avoid infinite loop
            $columns = array_keys($data);
            $placeholders = array_fill(0, count($columns), '?');
            $sql = sprintf(
                'INSERT INTO `audit_trail` (%s) VALUES (%s)',
                implode(', ', array_map(fn($c) => "`{$c}`", $columns)),
                implode(', ', $placeholders)
            );
            $db->query($sql, array_values($data));

        } catch (\Throwable $e) {
            // Audit must NEVER crash the application
            try {
                Logger::warning('Audit log failed: ' . $e->getMessage());
            } catch (\Throwable $e2) {}
        }
    }

    /**
     * Compute which fields actually changed between two records.
     * Returns array of field names with old/new values.
     */
    private static function computeChangedFields(array $before, array $after): array
    {
        $changed = [];
        $allKeys = array_unique(array_merge(array_keys($before), array_keys($after)));

        foreach ($allKeys as $key) {
            // Skip noise fields
            if (in_array($key, self::NOISE_FIELDS, true)) {
                continue;
            }

            $oldVal = $before[$key] ?? null;
            $newVal = $after[$key] ?? null;

            // Normalize for comparison
            $oldStr = ($oldVal === null) ? '' : (string) $oldVal;
            $newStr = ($newVal === null) ? '' : (string) $newVal;

            if ($oldStr !== $newStr) {
                // Mask sensitive
                if (in_array($key, self::SENSITIVE_FIELDS, true)) {
                    $changed[$key] = ['from' => '***', 'to' => '***'];
                } else {
                    $changed[$key] = [
                        'from' => $oldVal,
                        'to'   => $newVal,
                    ];
                }
            }
        }

        return $changed;
    }

    /**
     * Mask sensitive field values.
     */
    private static function maskSensitive(array $data): array
    {
        foreach (self::SENSITIVE_FIELDS as $field) {
            if (array_key_exists($field, $data)) {
                $data[$field] = '***MASKED***';
            }
        }
        return $data;
    }

    /**
     * Get a human-readable label for a record.
     */
    private static function extractLabel(string $table, array $data): ?string
    {
        // Try common label fields in priority order
        $labelFields = [
            'full_name_ar', 'name_ar', 'username', 'membership_number',
            'form_number', 'branch_code', 'role_code', 'carnet_number',
            'receipt_number', 'rule_code', 'template_code', 'service_code',
            'form_code', 'workflow_code', 'report_code', 'discount_code',
        ];

        foreach ($labelFields as $field) {
            if (!empty($data[$field])) {
                return mb_substr((string) $data[$field], 0, 255);
            }
        }

        return null;
    }

    /**
     * Log login events.
     */
    public static function logLogin(string $username, bool $success, ?string $reason = null): void
    {
        self::log(
            $success ? 'login' : 'login_failed',
            'employee',
            null,
            $username,
            null,
            null,
            $success ? "تسجيل دخول ناجح: {$username}" : "فشل تسجيل الدخول: {$username} - {$reason}"
        );
    }

    /**
     * Log logout events.
     */
    public static function logLogout(): void
    {
        $employee = App::getInstance()->currentEmployee();
        $name = $employee ? ($employee->full_name_ar ?? 'unknown') : 'unknown';
        self::log('logout', 'employee', $employee ? (int) $employee->id : null, $name);
    }

    /**
     * Register database hooks for AUTOMATIC audit logging of ALL operations.
     * This is the nuclear option — every INSERT, UPDATE, DELETE gets logged.
     */
    public static function registerDatabaseHooks(): void
    {
        try {
            $db = App::getInstance()->db();

            if (!$db->tableExists('audit_trail')) {
                return;
            }

            // ══════════════════════════════════════
            // HOOK: After INSERT — log creation
            // ══════════════════════════════════════
            $db->onAfterInsert(function (string $table, array $data, ?int $id) {
                if (in_array($table, self::IGNORE_TABLES, true)) {
                    return;
                }

                $label = self::extractLabel($table, $data);

                self::log(
                    'create',
                    $table,
                    $id,
                    $label,
                    null,   // no before (it's a creation)
                    $data,  // after = the inserted data
                    null
                );
            });

            // ══════════════════════════════════════
            // HOOK: After UPDATE — log changes with before/after
            // ══════════════════════════════════════
            $db->onAfterUpdate(function (string $table, array $changedData, ?int $entityId, ?array $oldRecord, ?array $newRecord) {
                if (in_array($table, self::IGNORE_TABLES, true)) {
                    return;
                }

                // Don't log if nothing actually changed
                if ($oldRecord && $newRecord) {
                    $changed = self::computeChangedFields($oldRecord, $newRecord);
                    if (empty($changed)) {
                        return; // No real changes
                    }
                }

                $label = null;
                if ($newRecord) {
                    $label = self::extractLabel($table, $newRecord);
                } elseif ($oldRecord) {
                    $label = self::extractLabel($table, $oldRecord);
                }

                self::log(
                    'update',
                    $table,
                    $entityId,
                    $label,
                    $oldRecord,  // full record before
                    $newRecord,  // full record after
                    null
                );
            });

            // ══════════════════════════════════════
            // HOOK: After DELETE — log deletion with old data
            // ══════════════════════════════════════
            $db->onAfterDelete(function (string $table, array $oldRecord, ?int $entityId) {
                if (in_array($table, self::IGNORE_TABLES, true)) {
                    return;
                }

                $label = !empty($oldRecord) ? self::extractLabel($table, $oldRecord) : null;

                self::log(
                    'delete',
                    $table,
                    $entityId,
                    $label,
                    $oldRecord,  // what was deleted
                    null,        // nothing after (it's gone)
                    "Deleted from {$table}" . ($entityId ? " ID: {$entityId}" : '')
                );
            });

        } catch (\Throwable $e) {
            try {
                Logger::warning('Failed to register audit hooks: ' . $e->getMessage());
            } catch (\Throwable $e2) {}
        }
    }

    public static function getActionLabel(string $action): string
    {
        return match ($action) {
            'create'       => 'إنشاء',
            'update'       => 'تعديل',
            'delete'       => 'حذف',
            'archive'      => 'أرشفة',
            'login'        => 'تسجيل دخول',
            'logout'       => 'تسجيل خروج',
            'login_failed' => 'فشل تسجيل دخول',
            default        => $action,
        };
    }

    public static function getActionIcon(string $action): string
    {
        return match ($action) {
            'create'       => '➕',
            'update'       => '✏️',
            'delete'       => '🗑️',
            'archive'      => '📦',
            'login'        => '🔑',
            'logout'       => '🚪',
            'login_failed' => '🚫',
            default        => '📋',
        };
    }

    public static function getActionColor(string $action): string
    {
        return match ($action) {
            'create'       => '#059669',
            'update'       => '#0284C7',
            'delete'       => '#DC2626',
            'archive'      => '#D97706',
            'login'        => '#6366F1',
            'logout'       => '#6B7280',
            'login_failed' => '#DC2626',
            default        => '#6B7280',
        };
    }

    public static function getEntityTypeLabel(string $type): string
    {
        return match ($type) {
            'members'              => 'عضو',
            'employees'            => 'موظف',
            'spouses'              => 'زوج/ة',
            'children'             => 'ابن/ابنة',
            'temporary_members'    => 'عضو مؤقت',
            'seasonal_memberships' => 'عضوية موسمية',
            'payments'             => 'دفعة',
            'payment_requests'     => 'طلب دفع',
            'receipts'             => 'إيصال',
            'installment_plans'    => 'خطة تقسيط',
            'installment_schedule' => 'قسط',
            'subscriptions'        => 'اشتراك',
            'fines'                => 'غرامة',
            'divorce_cases'        => 'حالة طلاق',
            'death_cases'          => 'حالة وفاة',
            'waiver_requests'      => 'طلب تنازل',
            'transfer_requests'    => 'طلب نقل/فصل',
            'branches'             => 'فرع',
            'roles'                => 'دور وظيفي',
            'qualifications'       => 'مؤهل',
            'business_rules'       => 'قاعدة عمل',
            'service_catalog'      => 'خدمة',
            'form_submissions'     => 'استمارة',
            'workflow_instances'   => 'مسار عمل',
            'journal_entries'      => 'قيد محاسبي',
            'sports_members'       => 'عضو رياضي',
            'carnets'              => 'كارنيه',
            'employee'             => 'موظف',
            default                => $type,
        };
    }

    public static function getFieldLabel(string $field): string
    {
        return match ($field) {
            'full_name_ar'                  => 'الاسم بالعربية',
            'full_name_en'                  => 'الاسم بالإنجليزية',
            'national_id'                   => 'الرقم القومي',
            'status'                        => 'الحالة',
            'membership_number'             => 'رقم العضوية',
            'form_number'                   => 'رقم الاستمارة',
            'branch_id'                     => 'الفرع',
            'qualification_id'              => 'المؤهل',
            'membership_value'              => 'قيمة العضوية',
            'membership_type'               => 'نوع العضوية',
            'member_category'               => 'فئة العضو',
            'membership_origin_date'        => 'تاريخ أصل العضوية',
            'date_of_birth'                 => 'تاريخ الميلاد',
            'gender'                        => 'النوع',
            'phone'                         => 'الهاتف',
            'phone_mobile'                  => 'الهاتف المحمول',
            'phone_home'                    => 'هاتف المنزل',
            'email'                         => 'البريد الإلكتروني',
            'marital_status'                => 'الحالة الاجتماعية',
            'nationality'                   => 'الجنسية',
            'religion'                      => 'الديانة',
            'occupation'                    => 'المهنة',
            'job_title'                     => 'المسمى الوظيفي',
            'residence_address'             => 'عنوان السكن',
            'area'                          => 'المنطقة',
            'governorate'                   => 'المحافظة',
            'emergency_name'                => 'اسم الطوارئ',
            'emergency_phone'               => 'هاتف الطوارئ',
            'is_archived'                   => 'مؤرشف',
            'archived_at'                   => 'تاريخ الأرشفة',
            'archived_by'                   => 'أرشف بواسطة',
            'created_at'                    => 'تاريخ الإنشاء',
            'updated_at'                    => 'تاريخ التعديل',
            'created_by'                    => 'أنشئ بواسطة',
            'updated_by'                    => 'عُدّل بواسطة',
            'activated_at'                  => 'تاريخ التفعيل',
            'activated_by_payment_id'       => 'دفعة التفعيل',
            'amount'                        => 'المبلغ',
            'total_amount'                  => 'المبلغ الإجمالي',
            'paid_amount'                   => 'المبلغ المدفوع',
            'remaining_balance'             => 'المبلغ المتبقي',
            'down_payment'                  => 'المقدم',
            'monthly_payment'               => 'القسط الشهري',
            'payment_type'                  => 'نوع الدفعة',
            'payment_method'                => 'طريقة الدفع',
            'payment_date'                  => 'تاريخ الدفع',
            'receipt_number'                => 'رقم الإيصال',
            'receipt_id'                    => 'رقم الإيصال',
            'request_number'                => 'رقم الطلب',
            'addition_fee'                  => 'رسوم الإضافة',
            'spouse_order'                  => 'ترتيب الزوجة',
            'child_order'                   => 'ترتيب الابن',
            'classification'                => 'التصنيف',
            'join_date'                     => 'تاريخ الالتحاق',
            'marriage_date'                 => 'تاريخ الزواج',
            'age_years'                     => 'السن (سنوات)',
            'age_months'                    => 'السن (شهور)',
            'category'                      => 'الفئة',
            'notes'                         => 'ملاحظات',
            'description'                   => 'الوصف',
            'is_voided'                     => 'ملغى',
            'voided_at'                     => 'تاريخ الإلغاء',
            'void_reason'                   => 'سبب الإلغاء',
            'voided_by'                     => 'ألغي بواسطة',
            'description_ar'                => 'الوصف',
            'current_value_json'            => 'القيمة الحالية',
            'rule_code'                     => 'كود القاعدة',
            'fee_amount'                    => 'مبلغ الرسوم',
            'fine_amount'                   => 'مبلغ الغرامة',
            'start_date'                    => 'تاريخ البداية',
            'end_date'                      => 'تاريخ النهاية',
            'due_date'                      => 'تاريخ الاستحقاق',
            'discount_amount'               => 'مبلغ الخصم',
            'special_discount_id'           => 'خصم خاص',
            'special_discount_document'     => 'مستند الخصم',
            'has_championship'              => 'بطولات رياضية',
            'disability_documentation'      => 'وثائق إعاقة',
            'relationship_to_member'        => 'صلة القرابة',
            'passport_number'               => 'رقم جواز السفر',
            'id_type'                       => 'نوع الهوية',
            'can_separate'                  => 'يمكن الفصل',
            'can_get_independent'           => 'يمكن الاستقلال',
            'processed_by'                  => 'معالج بواسطة',
            'processed_at'                  => 'تاريخ المعالجة',
            'requested_by'                  => 'مقدم الطلب',
            'related_entity_type'           => 'نوع الكيان المرتبط',
            'related_entity_id'             => 'رقم الكيان المرتبط',
            'member_id'                     => 'رقم العضو',
            'transferred_from_death_id'     => 'محوّل من حالة وفاة',
            'transferred_from_waiver_id'    => 'محوّل من تنازل',
            'transferred_from_divorce_id'   => 'محوّل من طلاق',
            'transferred_from_transfer_id'  => 'محوّل من فصل',
            'ownership_transfer_date'       => 'تاريخ نقل الملكية',
            'financial_year'                => 'السنة المالية',
            'number_of_months'              => 'عدد الأشهر',
            'installment_amount'            => 'مبلغ القسط',
            'interest_rate'                 => 'نسبة الفائدة',
            'check_number'                  => 'رقم الشيك',
            'check_bank'                    => 'بنك الشيك',
            'check_date'                    => 'تاريخ الشيك',
            'fee_receipt_number'            => 'رقم إيصال الرسوم',
            'form_date'                     => 'تاريخ الاستمارة',
            'source_member_id'              => 'العضو المصدر',
            'target_member_id'              => 'العضو المستهدف',
            'transfer_type'                 => 'نوع التحويل',
            'separation_fee'                => 'رسوم الفصل',
            'total_fee'                     => 'إجمالي الرسوم',
            'companion_surcharge'           => 'رسوم ملحقين إضافيين',
            'fee_percentage'                => 'نسبة الرسوم',
            'years_since_acquisition'       => 'سنوات منذ الاكتساب',
            'effective_transfer_date'       => 'تاريخ الفصل الفعلي',
            'completed_at'                  => 'تاريخ الإتمام',
            'approved_at'                   => 'تاريخ الاعتماد',
            'approved_by'                   => 'اعتمد بواسطة',
            'board_decision_notes'          => 'ملاحظات مجلس الإدارة',
            default                         => $field,
        };
    }

    public static function getStatusLabel(string $status): string
    {
        return match ($status) {
            'active'              => 'نشط',
            'inactive'            => 'غير نشط',
            'potential'           => 'محتمل',
            'under_review'        => 'قيد المراجعة',
            'interview_scheduled' => 'مقابلة محددة',
            'accepted'            => 'مقبول',
            'rejected'            => 'مرفوض',
            'payment_pending'     => 'في انتظار الدفع',
            'pending_payment'     => 'في انتظار الدفع',
            'pending'             => 'معلق',
            'pending_cheques'     => 'في انتظار الشيكات',
            'pending_form_fill'   => 'في انتظار ملء الاستمارة',
            'processing'          => 'قيد المعالجة',
            'completed'           => 'مكتمل',
            'cancelled'           => 'ملغى',
            'frozen'              => 'مجمد',
            'suspended'           => 'موقوف',
            'dropped'             => 'منسحب',
            'expired'             => 'منتهي',
            'transferred'         => 'محوّل',
            'separated'           => 'مفصول',
            'deceased'            => 'متوفي',
            'requested'           => 'في انتظار الدفع',
            'approved'            => 'معتمد',
            'fee_paid'            => 'تم الدفع',
            'paid'                => 'مدفوع',
            'overdue'             => 'متأخر',
            'partial'             => 'مدفوع جزئياً',
            'voided'              => 'ملغى',
            default               => $status,
        };
    }

    public static function formatFieldValue(string $field, mixed $value): string
    {
        if ($value === null || $value === '') return '—';

        if (in_array($field, ['amount', 'membership_value', 'addition_fee', 'fee_amount', 'fine_amount', 'discount_amount', 'total_amount', 'down_payment', 'remaining_balance', 'monthly_payment', 'paid_amount', 'separation_fee', 'total_fee', 'companion_surcharge', 'installment_amount'], true)) {
            return number_format((float) $value, 2) . ' ج.م';
        }
        if ($field === 'status') return self::getStatusLabel((string) $value);
        if ($field === 'gender') return match ((string) $value) { 'male' => 'ذكر', 'female' => 'أنثى', default => (string) $value };
        if (in_array($field, ['is_archived', 'is_voided', 'has_championship', 'disability_documentation', 'can_separate', 'can_get_independent'], true)) {
            return $value ? 'نعم' : 'لا';
        }
        if ($field === 'payment_type') {
            return \App\Modules\Cashier\Services\PaymentRequestService::getPaymentTypeLabel((string) $value);
        }
        if ($field === 'payment_method') {
            return match ((string) $value) { 'cash' => 'نقدي', 'visa' => 'فيزا', 'bank_transfer' => 'تحويل بنكي', 'check' => 'شيك', 'instalment' => 'تقسيط', default => (string) $value };
        }
        if ($field === 'membership_type') {
            return match ((string) $value) { 'working' => 'عامل', 'seasonal' => 'موسمي', 'honorary' => 'شرفي', 'sports' => 'رياضي', 'foreign' => 'أجنبي', default => (string) $value };
        }
        if ($field === 'member_category') {
            return match ((string) $value) { 'working_member' => 'عضو عامل', 'seasonal_member' => 'عضو موسمي', 'honorary_member' => 'عضو شرفي', 'sports_member' => 'عضو رياضي', default => (string) $value };
        }
        if ($field === 'transfer_type') {
            return match ((string) $value) { 'child_separation' => 'فصل أبناء', 'child_mandatory_25' => 'تحويل وجوبي (25 سنة)', 'sports_conversion' => 'تحويل رياضي', 'cross_branch' => 'تحويل بين فروع', 'full_transfer' => 'تحويل عضوية كامل', default => (string) $value };
        }
        if ($field === 'classification') {
            return match ((string) $value) { 'active' => 'نشط', 'separated' => 'مفصول', 'graduated' => 'متخرج', 'employed' => 'موظف', 'married' => 'متزوجة', default => (string) $value };
        }
        if ($field === 'id_type') {
            return match ((string) $value) { 'national_id' => 'بطاقة رقم قومي', 'passport' => 'جواز سفر', 'birth_certificate' => 'شهادة ميلاد', default => (string) $value };
        }
        if ($field === 'marital_status') {
            return match ((string) $value) { 'single' => 'أعزب', 'married' => 'متزوج', 'divorced' => 'مطلق', 'widowed' => 'أرمل', default => (string) $value };
        }
        if ($field === 'category') {
            return match ((string) $value) { 'relative' => 'قريب', 'friend' => 'صديق', 'nanny' => 'مربية', 'driver' => 'سائق', default => (string) $value };
        }

        return mb_substr((string) $value, 0, 200);
    }
}