<?php
declare(strict_types=1);

namespace App\Modules\Transfers\Services;

use App\Core\App;
use App\Core\EventBus;
use App\Core\Logger;
use App\Modules\Archive\Services\ArchiveService;
use App\Modules\Members\Services\FormNumberGenerator;
use App\Modules\Members\Services\MemberNumberGenerator;
use App\Modules\Members\Services\NationalIdParser;
use App\Modules\Transfers\Models\TransferRequest;

final class TransferProcessor
{
    /**
     * Execute a complete transfer/separation.
     * 1. Take archive snapshot
     * 2. Archive source member FIRST (releases membership_number)
     * 3. Create new member record with SAME number
     * 4. Record number chain
     * 5. Transfer dependents / mark separated
     * 6. Mark transfer complete
     */
    public static function execute(int $transferRequestId): array
    {
        $db = App::getInstance()->db();
        $employee = App::getInstance()->currentEmployee();

        $request = $db->selectOne("SELECT * FROM transfer_requests WHERE id = ?", [$transferRequestId]);
        if (!$request) {
            return ['success' => false, 'error' => 'طلب التحويل غير موجود'];
        }

        if (!in_array($request['status'], ['approved', 'fee_paid', 'pending_form_fill', 'form_filled'], true)) {
            return ['success' => false, 'error' => 'طلب التحويل غير مُعتمد أو لم يتم السداد'];
        }

        $sourceMember = $db->selectOne("SELECT * FROM members WHERE id = ?", [(int) $request['source_member_id']]);
        if (!$sourceMember) {
            return ['success' => false, 'error' => 'العضو المصدر غير موجود'];
        }

        $debtCheck = \App\Shared\Services\DebtCheckService::check((int) $request['source_member_id']);
        if (!$debtCheck['clear']) {
            return ['success' => false, 'error' => 'لا يمكن إتمام التحويل — مديونيات على العضو المصدر بإجمالي ' . $debtCheck['total'] . ' ج.م'];
        }

        $db->beginTransaction();
        try {
            // 1. Take archive snapshot of source member (skip for child_separation — parent keeps their membership)
            $snapshotId = null;
            if ($request['transfer_type'] !== 'child_separation') {
                $snapshotId = ArchiveService::takeSnapshot(
                    'members',
                    (int) $sourceMember['id'],
                    $request['transfer_type'],
                    'لقطة أرشيفية قبل التحويل/الفصل — طلب #' . $transferRequestId,
                    null,
                    $sourceMember['membership_number']
                );
            }

            // 2. Determine the subject of transfer
            $subjectName = $sourceMember['full_name_ar'];
            $subjectData = $sourceMember;
            $isFullTransfer = !$request['child_id'] && !$request['spouse_id'];

            if ($request['child_id']) {
                $child = $db->selectOne("SELECT * FROM children WHERE id = ?", [(int) $request['child_id']]);
                if ($child) {
                    $subjectName = $child['full_name_ar'];
                    $subjectData = $child;
                }
            } elseif ($request['spouse_id']) {
                $spouse = $db->selectOne("SELECT * FROM spouses WHERE id = ?", [(int) $request['spouse_id']]);
                if ($spouse) {
                    $subjectName = $spouse['full_name_ar'];
                    $subjectData = $spouse;
                }
            }

            // Use recipient/form data stored in notes
            $recipientData = [];
            $formData = [];
            if (!empty($request['notes'])) {
                $notesDecoded = json_decode($request['notes'], true);
                if ($request['transfer_type'] === 'full_transfer') {
                    $recipientData = $notesDecoded['recipient_data'] ?? [];
                }
                $formData = $notesDecoded['form_data'] ?? [];
            }

            // Find the separation_fee payment that was made for this transfer
            $transferPayment = $db->selectOne(
                "SELECT id FROM payments WHERE member_id = ? AND payment_type = 'separation_fee' AND related_entity_type = 'transfer_requests' AND related_entity_id = ? AND is_voided = 0 ORDER BY id DESC LIMIT 1",
                [(int) $request['source_member_id'], $transferRequestId]
            );
            $transferPaymentId = $transferPayment ? (int) $transferPayment['id'] : null;

            // 3. Determine membership number for new member
            $isChildSeparation = ($request['transfer_type'] === 'child_separation');
            $newNumber = null;

            if ($isChildSeparation) {
                // Child separation: parent KEEPS their number, child gets a NEW number
                $newNumber = (string) MemberNumberGenerator::getNextMembershipNumber();
            } else {
                // Full transfer / death: archive source, new member inherits SAME number
                $newNumber = $sourceMember['membership_number'];
                $db->update('members', [
                    'membership_number' => null,
                    'status'            => 'transferred',
                    'is_archived'       => 1,
                    'archived_at'       => date('Y-m-d H:i:s'),
                    'updated_at'        => date('Y-m-d H:i:s'),
                ], '`id` = ?', [(int) $sourceMember['id']]);

                // Verify number is actually released
                $numberConflict = $db->selectOne(
                    "SELECT id FROM members WHERE membership_number = ? AND id != ?",
                    [$newNumber, (int) $sourceMember['id']]
                );
                if ($numberConflict) {
                    $newNumber = MemberNumberGenerator::generateNext();
                }
            }

            // 4. Create new member record
            // Derive date_of_birth and gender from national_id if not directly available
            $dob = $recipientData['date_of_birth'] ?? $subjectData['date_of_birth'] ?? null;
            $gender = $recipientData['gender'] ?? $subjectData['gender'] ?? null;
            $nid = $recipientData['national_id'] ?? $subjectData['national_id'] ?? null;

            // Sanitize: empty string or invalid placeholder → try NID parsing
            if (!$dob || $dob === '0000-00-00' || $dob === '1900-01-01') {
                $dob = null;
            }
            if (!$gender || $gender === '') {
                $gender = null;
            }

            if ((!$dob || !$gender) && $nid && strlen((string) $nid) === 14) {
                $parsed = NationalIdParser::parse((string) $nid);
                if ($parsed['is_valid']) {
                    $dob = $dob ?: $parsed['dob'];
                    $gender = $gender ?: $parsed['gender'];
                }
            }

            // phone_mobile: children/spouses may not have this — fall through to source member
            $phoneMobile = $recipientData['phone_mobile']
                ?? $subjectData['phone_mobile']
                ?? $subjectData['mobile']
                ?? $sourceMember['phone_mobile']
                ?? '00000000000';

            // Use manually entered form_number, or auto-generate as fallback
            $formNumber = !empty($request['form_number'])
                ? $request['form_number']
                : FormNumberGenerator::next();

            $newMemberData = [
                'full_name_ar'           => $formData['full_name_ar'] ?? $recipientData['full_name_ar'] ?? $subjectData['full_name_ar'] ?? $subjectName,
                'full_name_en'           => $formData['full_name_en'] ?? $recipientData['full_name_en'] ?? $subjectData['full_name_en'] ?? null,
                'national_id'            => $formData['national_id'] ?? $nid,
                'passport_number'        => $subjectData['passport_number'] ?? null,
                'id_type'                => $subjectData['id_type'] ?? 'national_id',
                'date_of_birth'          => $formData['date_of_birth'] ?? $dob ?: '1900-01-01',
                'gender'                 => $formData['gender'] ?? $gender ?: 'male',
                'nationality'            => $formData['nationality'] ?? $recipientData['nationality'] ?? $subjectData['nationality'] ?? 'مصري',
                'branch_id'              => (int) $sourceMember['branch_id'],
                'membership_type'        => 'working',
                'member_category'        => 'working_member',
                'status'                 => 'active',
                'activated_at'           => date('Y-m-d H:i:s'),
                'activated_by_payment_id'=> $transferPaymentId,
                'qualification_id'       => $formData['qualification_id'] ?? $recipientData['qualification_id'] ?? $subjectData['qualification_id'] ?? $sourceMember['qualification_id'],
                'phone_mobile'           => $formData['phone_mobile'] ?? $phoneMobile,
                'membership_value'       => $request['new_membership_value'],
                'membership_number'      => $newNumber,
                'form_number'            => $formNumber ? (string) $formNumber : null,
                'created_at'             => date('Y-m-d H:i:s'),
                'updated_at'             => date('Y-m-d H:i:s'),
                'created_by'             => $employee ? (int) $employee->id : null,
            ];

            // Add optional fields from form_data or recipient_data
            $optionalFields = ['marital_status', 'religion', 'phone_home', 'email', 'emergency_name', 'emergency_phone', 'residence_address', 'area', 'governorate', 'occupation', 'job_title'];
            foreach ($optionalFields as $field) {
                if (!empty($formData[$field])) {
                    $newMemberData[$field] = $formData[$field];
                } elseif (!empty($recipientData[$field])) {
                    $newMemberData[$field] = $recipientData[$field];
                }
            }

            $newMemberData['transferred_from_transfer_id'] = $transferRequestId;

            // Archive any conflicting potential/inactive member with the same national_id
            $newNid = $newMemberData['national_id'] ?? null;
            if ($newNid) {
                $conflict = $db->selectOne(
                    "SELECT id FROM members WHERE national_id = ? AND status IN ('potential','inactive','pending') AND is_archived = 0",
                    [$newNid]
                );
                if ($conflict) {
                    $db->update('members', [
                        'is_archived' => 1,
                        'archived_at' => date('Y-m-d H:i:s'),
                        'national_id' => null,
                    ], '`id` = ?', [(int) $conflict['id']]);
                }
            }

            $newMemberId = $db->insert('members', $newMemberData);

            // 5. Record number chain
            ArchiveService::recordNumberTransfer(
                $newNumber,
                $request['transfer_type'],
                'members',
                $newMemberId,
                null
            );

            // 6. Update source dependents — archive them from parent + remove unpaid subscriptions
            if ($request['child_id']) {
                $db->update('children', [
                    'status'        => 'separated',
                    'classification'=> 'separated',
                    'is_archived'   => 1,
                    'archived_at'   => date('Y-m-d H:i:s'),
                    'updated_at'    => date('Y-m-d H:i:s'),
                ], '`id` = ?', [(int) $request['child_id']]);
                $db->query(
                    "DELETE FROM subscriptions WHERE member_id = ? AND person_type = 'child' AND person_id = ? AND status IN ('pending','overdue')",
                    [(int) $request['source_member_id'], (int) $request['child_id']]
                );
            } elseif ($request['spouse_id']) {
                $db->update('spouses', [
                    'status'     => 'separated',
                    'is_archived' => 1,
                    'archived_at' => date('Y-m-d H:i:s'),
                    'updated_at' => date('Y-m-d H:i:s'),
                ], '`id` = ?', [(int) $request['spouse_id']]);
                $db->query(
                    "DELETE FROM subscriptions WHERE member_id = ? AND person_type = 'spouse' AND person_id = ? AND status IN ('pending','overdue')",
                    [(int) $request['source_member_id'], (int) $request['spouse_id']]
                );
            }

            // Child separation: create dependents specified on the form
            if ($request['transfer_type'] === 'child_separation' && !empty($request['notes'])) {
                $notesDecoded = json_decode($request['notes'], true) ?: [];
                $depsData = $notesDecoded['dependents_data'] ?? [];
                $today = date('Y-m-d');
                $ts = date('Y-m-d H:i:s');

                if (!empty($depsData['spouses'])) {
                    $spOrder = 0;
                    foreach ($depsData['spouses'] as $sp) {
                        if (empty($sp['full_name_ar'])) continue;
                        $spOrder++;
                        $spNid = !empty($sp['national_id']) ? (string) $sp['national_id'] : null;
                        $spDob = !empty($sp['date_of_birth']) ? $sp['date_of_birth'] : null;
                        $spGender = 'female';
                        if ($spNid && strlen($spNid) === 14) {
                            $spParsed = NationalIdParser::parse($spNid);
                            if ($spParsed['is_valid']) {
                                if (!$spDob) $spDob = $spParsed['dob'];
                                $spGender = $spParsed['gender'] ?? 'female';
                            }
                        }
                        $spNationality = trim($sp['nationality'] ?? 'مصري');
                        $spJoinDate = !empty($sp['join_date']) ? $sp['join_date'] : $today;

                        // Calculate addition_fee for non-Egyptian spouses (SPOUSE_FOREIGN_FEE % of current plan price)
                        $spAdditionFee = '0.00';
                        $isForeign = ($spNationality !== 'مصري' && $spNationality !== '' && $spNationality !== 'Egyptian');
                        if ($isForeign) {
                            try {
                                $foreignRule = \App\Modules\Rules\Services\RuleEngine::get('SPOUSE_FOREIGN_FEE');
                                $foreignPct = $foreignRule['percentage'] ?? '15.00';
                                $newMemberRow = $db->selectOne("SELECT branch_id, qualification_id, membership_type FROM members WHERE id = ?", [$newMemberId]);
                                $brId = (int) ($newMemberRow['branch_id'] ?? 1);
                                $qId = !empty($newMemberRow['qualification_id']) ? (int) $newMemberRow['qualification_id'] : null;
                                $mType = $newMemberRow['membership_type'] ?? 'working';
                                $planPrice = '0.00';
                                if ($qId) {
                                    $pr = $db->selectOne("SELECT price FROM pricing_configs WHERE branch_id = ? AND qualification_id = ? AND membership_type = ? AND is_active = 1 AND effective_from <= CURDATE() AND (effective_to IS NULL OR effective_to >= CURDATE()) ORDER BY effective_from DESC LIMIT 1", [$brId, $qId, $mType]);
                                    $planPrice = $pr['price'] ?? '0.00';
                                }
                                if (bccomp($planPrice, '0.01', 2) < 0) {
                                    $pr = $db->selectOne("SELECT price FROM pricing_configs WHERE branch_id = ? AND membership_type = ? AND is_active = 1 AND effective_from <= CURDATE() AND (effective_to IS NULL OR effective_to >= CURDATE()) ORDER BY price DESC LIMIT 1", [$brId, $mType]);
                                    $planPrice = $pr['price'] ?? '0.00';
                                }
                                if (bccomp($planPrice, '0.01', 2) >= 0) {
                                    $spAdditionFee = bcdiv(bcmul($planPrice, $foreignPct, 4), '100', 2);
                                }
                            } catch (\Throwable $e) {}
                        }

                        $db->insert('spouses', [
                            'member_id'    => $newMemberId,
                            'spouse_order' => $spOrder,
                            'full_name_ar' => $sp['full_name_ar'],
                            'national_id'  => $spNid,
                            'date_of_birth'=> $spDob ?: '1900-01-01',
                            'gender'       => $spGender,
                            'nationality'  => $spNationality ?: 'مصري',
                            'marriage_date'=> $sp['marriage_date'] ?? $today,
                            'join_date'    => $spJoinDate,
                            'addition_fee' => $spAdditionFee,
                            'status'       => 'active',
                            'is_archived'  => 0,
                            'created_at'   => $ts,
                            'updated_at'   => $ts,
                        ]);
                    }
                }

                if (!empty($depsData['children'])) {
                    $chOrder = 0;
                    foreach ($depsData['children'] as $ch) {
                        if (empty($ch['full_name_ar'])) continue;
                        $chOrder++;
                        $chNid = !empty($ch['national_id']) ? (string) $ch['national_id'] : null;
                        $chDob = !empty($ch['date_of_birth']) ? $ch['date_of_birth'] : null;
                        $chGender = $ch['gender'] ?? 'male';
                        if (!$chDob && $chNid && strlen($chNid) === 14) {
                            $chParsed = NationalIdParser::parse($chNid);
                            if ($chParsed['is_valid']) {
                                $chDob = $chParsed['dob'];
                                $chGender = $chParsed['gender'] ?? $chGender;
                            }
                        }
                        $chJoinDate = !empty($ch['join_date']) ? $ch['join_date'] : null;
                        $db->insert('children', [
                            'member_id'      => $newMemberId,
                            'child_order'    => $chOrder,
                            'full_name_ar'   => $ch['full_name_ar'],
                            'national_id'    => $chNid,
                            'date_of_birth'  => $chDob ?: '1900-01-01',
                            'gender'         => $chGender,
                            'join_date'      => $chJoinDate,
                            'classification' => 'active',
                            'status'         => 'active',
                            'is_archived'    => 0,
                            'created_at'     => $ts,
                            'updated_at'     => $ts,
                        ]);
                    }
                }

                $tempsArr = $depsData['temps'] ?? $depsData['temporary_members'] ?? [];
                if (!empty($tempsArr)) {
                    foreach ($tempsArr as $tm) {
                        if (empty($tm['full_name_ar'])) continue;
                        $tmNid = !empty($tm['national_id']) ? (string) $tm['national_id'] : null;
                        $tmDob = !empty($tm['date_of_birth']) ? $tm['date_of_birth'] : null;
                        $tmGender = $tm['gender'] ?? 'male';
                        if ($tmNid && strlen($tmNid) === 14) {
                            $tmParsed = NationalIdParser::parse($tmNid);
                            if ($tmParsed['is_valid']) {
                                if (!$tmDob) $tmDob = $tmParsed['dob'];
                                $tmGender = $tmParsed['gender'] ?? $tmGender;
                            }
                        }
                        $db->insert('temporary_members', [
                            'member_id'    => $newMemberId,
                            'category'     => $tm['category'] ?? 'relative',
                            'full_name_ar' => $tm['full_name_ar'],
                            'national_id'  => $tmNid,
                            'date_of_birth'=> $tmDob ?: '1900-01-01',
                            'gender'       => $tmGender,
                            'status'       => 'active',
                            'is_archived'  => 0,
                            'created_at'   => $ts,
                            'updated_at'   => $ts,
                        ]);
                    }
                }
            }

            // Full membership transfer: move all dependents to new member
            if ($isFullTransfer) {
                $db->query("UPDATE spouses SET member_id = ?, updated_at = NOW() WHERE member_id = ? AND is_archived = 0", [$newMemberId, (int) $sourceMember['id']]);
                $db->query("UPDATE children SET member_id = ?, updated_at = NOW() WHERE member_id = ? AND is_archived = 0", [$newMemberId, (int) $sourceMember['id']]);
                $db->query("UPDATE temporary_members SET member_id = ?, updated_at = NOW() WHERE member_id = ? AND is_archived = 0", [$newMemberId, (int) $sourceMember['id']]);

                // Also transfer archived dependents (preserve full family history)
                $db->query("UPDATE spouses SET member_id = ?, updated_at = NOW() WHERE member_id = ? AND is_archived = 1", [$newMemberId, (int) $sourceMember['id']]);
                $db->query("UPDATE children SET member_id = ?, updated_at = NOW() WHERE member_id = ? AND is_archived = 1", [$newMemberId, (int) $sourceMember['id']]);
                $db->query("UPDATE temporary_members SET member_id = ?, updated_at = NOW() WHERE member_id = ? AND is_archived = 1", [$newMemberId, (int) $sourceMember['id']]);

                // Transfer archive history (full membership history follows the number)
                $db->query(
                    "UPDATE archive_snapshots SET entity_id = ?, updated_at = NOW() WHERE entity_type = 'members' AND entity_id = ?",
                    [$newMemberId, (int) $sourceMember['id']]
                );
            }

            // 7. Mark transfer request complete
            $db->update('transfer_requests', [
                'target_member_id'     => $newMemberId,
                'new_membership_number'=> $newNumber,
                'archive_snapshot_id'  => $snapshotId,
                'completed_at'         => date('Y-m-d H:i:s'),
                'status'               => 'completed',
                'updated_at'           => date('Y-m-d H:i:s'),
                'updated_by'           => $employee ? (int) $employee->id : null,
            ], '`id` = ?', [$transferRequestId]);

            $db->commit();

            EventBus::dispatch('transfer.completed', [
                'transfer_id'      => $transferRequestId,
                'source_member_id' => (int) $request['source_member_id'],
                'target_member_id' => $newMemberId,
                'new_number'       => $newNumber,
                'transfer_type'    => $request['transfer_type'],
                'fee_amount'       => $request['total_fee'] ?? $request['separation_fee'] ?? '0.00',
                'old_number'       => $sourceMember['membership_number'] ?? null,
            ]);

            Logger::info("Transfer completed", [
                'transfer_id' => $transferRequestId,
                'new_member'  => $newMemberId,
                'new_number'  => $newNumber,
            ]);

            return [
                'success'          => true,
                'new_member_id'    => $newMemberId,
                'new_number'       => $newNumber,
                'snapshot_id'      => $snapshotId,
                'transfer_id'      => $transferRequestId,
            ];

        } catch (\Throwable $e) {
            $db->rollBack();
            Logger::error("Transfer failed: " . $e->getMessage(), ['transfer_id' => $transferRequestId]);
            return ['success' => false, 'error' => 'فشل في إتمام التحويل: ' . $e->getMessage()];
        }
    }
}