<?php
declare(strict_types=1);

namespace App\Modules\HR\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\HR\Models\HrEmployeeProfile;
use App\Modules\Members\Services\NationalIdParser;
use App\Modules\HR\Models\HrDepartment;
use App\Modules\HR\Models\HrJobTitle;
use App\Modules\HR\Models\HrSalaryStructure;
use App\Modules\HR\Models\HrSalaryComponent;
use App\Modules\HR\Models\HrEmployeeSalaryDetail;
use App\Modules\HR\Services\HrNumberGenerator;

class EmployeeProfileController extends Controller
{
    public function index(Request $request): Response
    {
        $filters = [
            'q'                 => trim((string) $request->get('q', '')),
            'department_id'     => trim((string) $request->get('department_id', '')),
            'employment_status' => trim((string) $request->get('employment_status', '')),
            'employment_type'   => trim((string) $request->get('employment_type', '')),
        ];

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

        return $this->view('HR.Views.employees.index', [
            'employees'  => $result['data'],
            'pagination' => $result['pagination'],
            'filters'    => $filters,
            'departments' => HrDepartment::allActive(),
            'statuses'    => HrEmployeeProfile::getStatuses(),
            'types'       => HrEmployeeProfile::getEmploymentTypes(),
        ]);
    }

    public function create(Request $request): Response
    {
        $db = App::getInstance()->db();

        // Get employees without HR profiles yet
        $unlinked = $db->select(
            "SELECT e.id, e.username, e.name
             FROM employees e
             LEFT JOIN hr_employee_profiles hp ON hp.employee_id = e.id AND hp.is_archived = 0
             WHERE hp.id IS NULL AND e.is_archived = 0
             ORDER BY e.name ASC"
        );

        return $this->view('HR.Views.employees.form', [
            'profile'       => null,
            'unlinked'      => $unlinked,
            'departments'   => HrDepartment::allActive(),
            'jobTitles'     => HrJobTitle::allActive(),
            'structures'    => HrSalaryStructure::allActive(),
            'statuses'      => HrEmployeeProfile::getStatuses(),
            'types'         => HrEmployeeProfile::getEmploymentTypes(),
            'genders'       => HrEmployeeProfile::getGenders(),
            'maritalStatuses' => HrEmployeeProfile::getMaritalStatuses(),
            'religions'     => HrEmployeeProfile::getReligions(),
            'militaryStatuses' => HrEmployeeProfile::getMilitaryStatuses(),
        ]);
    }

    public function store(Request $request): Response
    {
        $data = $this->extractData($request);
        $errors = $this->validateInput($data);

        // Verify employee_id is valid and not already linked
        if ($data['employee_id']) {
            $db = App::getInstance()->db();
            $existing = $db->selectOne(
                "SELECT id FROM hr_employee_profiles WHERE employee_id = ? AND is_archived = 0",
                [$data['employee_id']]
            );
            if ($existing) {
                $errors[] = 'هذا الموظف لديه ملف HR بالفعل';
            }
        }

        // Generate employee number
        $data['employee_number'] = HrNumberGenerator::generateEmployeeNumber();

        if (!empty($errors)) {
            return $this->flashErrorsAndRedirect($errors, $request, '/hr/employees/create');
        }

        $profile = HrEmployeeProfile::create($data);

        Logger::info("HR Profile created", ['profile_id' => $profile->id, 'employee_id' => $data['employee_id']]);

        return $this->redirect('/hr/employees/' . $profile->id)->withSuccess('تم إنشاء ملف الموظف بنجاح');
    }

    public function show(Request $request, string $id): Response
    {
        $profile = HrEmployeeProfile::find((int) $id);
        if (!$profile) {
            return $this->redirect('/hr/employees')->withError('ملف الموظف غير موجود');
        }

        $db = App::getInstance()->db();

        $department = $profile->department_id
            ? HrDepartment::find((int) $profile->department_id)
            : null;

        $jobTitle = $profile->job_title_id
            ? HrJobTitle::find((int) $profile->job_title_id)
            : null;

        $structure = $profile->salary_structure_id
            ? HrSalaryStructure::find((int) $profile->salary_structure_id)
            : null;

        // Active contract
        $activeContract = $db->selectOne(
            "SELECT * FROM hr_contracts WHERE employee_profile_id = ? AND status = 'active' AND is_archived = 0 ORDER BY id DESC LIMIT 1",
            [(int) $id]
        );

        // Active loans
        $activeLoans = $db->select(
            "SELECT * FROM hr_employee_loans WHERE employee_profile_id = ? AND status IN ('approved','disbursed','repaying') AND is_archived = 0",
            [(int) $id]
        );

        // Recent leave requests
        $recentLeaves = $db->select(
            "SELECT lr.*, lt.name_ar as leave_type_name
             FROM hr_leave_requests lr
             JOIN hr_leave_types lt ON lt.id = lr.leave_type_id
             WHERE lr.employee_profile_id = ? AND lr.is_archived = 0
             ORDER BY lr.created_at DESC LIMIT 5",
            [(int) $id]
        );

        $yearsOfService = HrEmployeeProfile::getYearsOfService($profile->hire_date ?? date('Y-m-d'));

        return $this->view('HR.Views.employees.show', [
            'profile'        => $profile,
            'department'     => $department,
            'jobTitle'       => $jobTitle,
            'structure'      => $structure,
            'activeContract' => $activeContract,
            'activeLoans'    => $activeLoans,
            'recentLeaves'   => $recentLeaves,
            'yearsOfService' => $yearsOfService,
            'statuses'       => HrEmployeeProfile::getStatuses(),
            'types'          => HrEmployeeProfile::getEmploymentTypes(),
            'genders'        => HrEmployeeProfile::getGenders(),
            'maritalStatuses' => HrEmployeeProfile::getMaritalStatuses(),
        ]);
    }

    public function edit(Request $request, string $id): Response
    {
        $profile = HrEmployeeProfile::find((int) $id);
        if (!$profile) {
            return $this->redirect('/hr/employees')->withError('ملف الموظف غير موجود');
        }

        return $this->view('HR.Views.employees.form', [
            'profile'       => $profile,
            'unlinked'      => [],
            'departments'   => HrDepartment::allActive(),
            'jobTitles'     => HrJobTitle::allActive(),
            'structures'    => HrSalaryStructure::allActive(),
            'statuses'      => HrEmployeeProfile::getStatuses(),
            'types'         => HrEmployeeProfile::getEmploymentTypes(),
            'genders'       => HrEmployeeProfile::getGenders(),
            'maritalStatuses' => HrEmployeeProfile::getMaritalStatuses(),
            'religions'     => HrEmployeeProfile::getReligions(),
            'militaryStatuses' => HrEmployeeProfile::getMilitaryStatuses(),
        ]);
    }

    public function update(Request $request, string $id): Response
    {
        $profile = HrEmployeeProfile::find((int) $id);
        if (!$profile) {
            return $this->redirect('/hr/employees')->withError('ملف الموظف غير موجود');
        }

        $data = $this->extractData($request);
        // Don't allow changing employee_id or employee_number on update
        unset($data['employee_id'], $data['employee_number']);

        $errors = $this->validateInput($data, true);

        if (!empty($errors)) {
            return $this->flashErrorsAndRedirect($errors, $request, '/hr/employees/' . $id . '/edit');
        }

        $profile->update($data);

        return $this->redirect('/hr/employees/' . $id)->withSuccess('تم تحديث ملف الموظف بنجاح');
    }

    public function archive(Request $request, string $id): Response
    {
        $profile = HrEmployeeProfile::find((int) $id);
        if (!$profile) {
            return $this->redirect('/hr/employees')->withError('ملف الموظف غير موجود');
        }

        if ($profile->employment_status === 'active') {
            return $this->redirect('/hr/employees/' . $id)->withError('لا يمكن حذف ملف موظف نشط. يجب إنهاء خدمته أولاً');
        }

        $profile->archive();

        return $this->redirect('/hr/employees')->withSuccess('تم حذف ملف الموظف بنجاح');
    }

    public function salary(Request $request, string $id): Response
    {
        $profile = HrEmployeeProfile::find((int) $id);
        if (!$profile) {
            return $this->redirect('/hr/employees')->withError('ملف الموظف غير موجود');
        }

        $salaryDetails = HrEmployeeSalaryDetail::getForEmployee((int) $id);

        $structure = $profile->salary_structure_id
            ? HrSalaryStructure::find((int) $profile->salary_structure_id)
            : null;

        $components = $structure
            ? HrSalaryStructure::getComponents((int) $structure->id)
            : [];

        return $this->view('HR.Views.employees.salary', [
            'profile'       => $profile,
            'salaryDetails' => $salaryDetails,
            'structure'     => $structure,
            'components'    => $components,
        ]);
    }

    public function updateSalary(Request $request, string $id): Response
    {
        $profile = HrEmployeeProfile::find((int) $id);
        if (!$profile) {
            return $this->redirect('/hr/employees')->withError('ملف الموظف غير موجود');
        }

        $db = App::getInstance()->db();
        $employee = $this->currentEmployee();
        $effectiveDate = trim((string) $request->post('effective_date', date('Y-m-d')));
        $componentAmounts = $request->post('components', []);

        if (!is_array($componentAmounts) || empty($componentAmounts)) {
            return $this->redirect('/hr/employees/' . $id . '/salary')->withError('لم يتم تحديد مكونات الراتب');
        }

        $db->beginTransaction();
        try {
            foreach ($componentAmounts as $componentId => $amount) {
                $amount = trim((string) $amount);
                if ($amount === '' || bccomp($amount, '0', 2) === 0) {
                    continue;
                }

                // Check if detail already exists for this component+date
                $existing = $db->selectOne(
                    "SELECT id FROM hr_employee_salary_details WHERE employee_profile_id = ? AND component_id = ? AND effective_from = ? AND is_active = 1",
                    [(int) $id, (int) $componentId, $effectiveDate]
                );

                if ($existing) {
                    $db->update('hr_employee_salary_details', [
                        'amount'     => $amount,
                        'updated_at' => date('Y-m-d H:i:s'),
                        'updated_by' => $employee ? (int) $employee->id : null,
                    ], '`id` = ?', [(int) $existing['id']]);
                } else {
                    $db->insert('hr_employee_salary_details', [
                        'employee_profile_id'  => (int) $id,
                        'component_id'  => (int) $componentId,
                        'amount'               => $amount,
                        'effective_from'        => $effectiveDate,
                        '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,
                    ]);
                }
            }

            $db->commit();

            Logger::info("Salary details updated", ['profile_id' => (int) $id, 'effective_date' => $effectiveDate]);

            return $this->redirect('/hr/employees/' . $id . '/salary')->withSuccess('تم تحديث تفاصيل الراتب بنجاح');

        } catch (\Throwable $e) {
            $db->rollBack();
            Logger::error("Salary update failed: " . $e->getMessage());
            return $this->redirect('/hr/employees/' . $id . '/salary')->withError('فشل تحديث تفاصيل الراتب: ' . $e->getMessage());
        }
    }

    public function searchJson(Request $request): Response
    {
        $q = trim((string) $request->get('q', ''));
        if (mb_strlen($q) < 2) {
            return $this->json(['results' => []]);
        }

        $db = App::getInstance()->db();
        $search = '%' . $q . '%';
        $rows = $db->select(
            "SELECT hp.id, hp.employee_number, hp.first_name_ar, hp.last_name_ar, hp.basic_salary,
                    d.name_ar as department_name, jt.name_ar as job_title_name
             FROM hr_employee_profiles hp
             LEFT JOIN hr_departments d ON d.id = hp.department_id
             LEFT JOIN hr_job_titles jt ON jt.id = hp.job_title_id
             WHERE (hp.first_name_ar LIKE ? OR hp.last_name_ar LIKE ? OR hp.employee_number LIKE ? OR hp.national_id LIKE ?)
               AND hp.employment_status = 'active' AND hp.is_archived = 0
             ORDER BY hp.first_name_ar ASC LIMIT 20",
            [$search, $search, $search, $search]
        );

        return $this->json(['results' => $rows]);
    }

    private function extractData(Request $request): array
    {
        $nationalId = trim((string) $request->post('national_id', ''));
        $dateOfBirth = trim((string) $request->post('date_of_birth', '')) ?: null;
        $gender = trim((string) $request->post('gender', 'male'));

        if ($nationalId !== '' && strlen($nationalId) === 14) {
            $parsed = NationalIdParser::parse($nationalId);
            if ($parsed['is_valid']) {
                $dateOfBirth = $parsed['dob'];
                $gender = $parsed['gender'];
            }
        }

        return [
            'employee_id'          => ((int) $request->post('employee_id', 0)) ?: null,
            'first_name_ar'        => trim((string) $request->post('first_name_ar', '')),
            'last_name_ar'         => trim((string) $request->post('last_name_ar', '')),
            'first_name_en'        => trim((string) $request->post('first_name_en', '')) ?: null,
            'last_name_en'         => trim((string) $request->post('last_name_en', '')) ?: null,
            'national_id'          => $nationalId,
            'date_of_birth'        => $dateOfBirth,
            'gender'               => $gender,
            'marital_status'       => trim((string) $request->post('marital_status', 'single')),
            'religion'             => trim((string) $request->post('religion', 'muslim')),
            'phone'                => trim((string) $request->post('phone', '')) ?: null,
            'email'                => trim((string) $request->post('email', '')) ?: null,
            'address'              => trim((string) $request->post('address', '')) ?: null,
            'department_id'        => ((int) $request->post('department_id', 0)) ?: null,
            'job_title_id'         => ((int) $request->post('job_title_id', 0)) ?: null,
            'salary_structure_id'  => ((int) $request->post('salary_structure_id', 0)) ?: null,
            'hire_date'            => trim((string) $request->post('hire_date', '')),
            'employment_type'      => trim((string) $request->post('employment_type', 'full_time')),
            'employment_status'    => trim((string) $request->post('employment_status', 'active')),
            'probation_end_date'   => trim((string) $request->post('probation_end_date', '')) ?: null,
            'basic_salary'         => trim((string) $request->post('basic_salary', '0.00')),
            'insurable_salary'     => trim((string) $request->post('insurable_salary', '0.00')) ?: '0.00',
            'insurance_number'     => trim((string) $request->post('insurance_number', '')) ?: null,
            'bank_name'            => trim((string) $request->post('bank_name', '')) ?: null,
            'bank_account_number'  => trim((string) $request->post('bank_account_number', '')) ?: null,
            'bank_iban'            => trim((string) $request->post('bank_iban', '')) ?: null,
            'military_status'      => trim((string) $request->post('military_status', '')) ?: null,
            'has_disability'       => (int) ($request->post('has_disability', 0)),
            'disability_details'   => trim((string) $request->post('disability_details', '')) ?: null,
            'emergency_contact_name'  => trim((string) $request->post('emergency_contact_name', '')) ?: null,
            'emergency_contact_phone' => trim((string) $request->post('emergency_contact_phone', '')) ?: null,
            'notes'                => trim((string) $request->post('notes', '')) ?: null,
        ];
    }

    private function validateInput(array $data, bool $isUpdate = false): array
    {
        $errors = [];
        if ($data['first_name_ar'] === '' || mb_strlen($data['first_name_ar']) < 2) {
            $errors[] = 'الاسم الأول بالعربي مطلوب (حرفان على الأقل)';
        }
        if ($data['last_name_ar'] === '' || mb_strlen($data['last_name_ar']) < 2) {
            $errors[] = 'اسم العائلة بالعربي مطلوب (حرفان على الأقل)';
        }
        if (!$isUpdate && empty($data['employee_id'])) {
            $errors[] = 'يجب ربط الملف بموظف من النظام';
        }
        if ($data['national_id'] === '' || mb_strlen($data['national_id']) !== 14) {
            $errors[] = 'الرقم القومي يجب أن يكون 14 رقم';
        }
        if ($data['hire_date'] === '') {
            $errors[] = 'تاريخ التعيين مطلوب';
        }
        if (bccomp($data['basic_salary'], '0.01', 2) < 0) {
            $errors[] = 'الراتب الأساسي يجب أن يكون أكبر من صفر';
        }
        if (!array_key_exists($data['gender'], HrEmployeeProfile::getGenders())) {
            $errors[] = 'الجنس غير صالح';
        }
        if (!array_key_exists($data['employment_type'], HrEmployeeProfile::getEmploymentTypes())) {
            $errors[] = 'نوع التوظيف غير صالح';
        }
        return $errors;
    }

    private function flashErrorsAndRedirect(array $errors, Request $request, string $url): Response
    {
        $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($url);
    }
}
