<?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\Modules\HR\Models\HrLeaveType;

class LeaveTypeController extends Controller
{
    public function index(Request $request): Response
    {
        $this->authorize('hr.leave.manage');

        $db = App::getInstance()->db();
        $types = $db->select(
            "SELECT * FROM hr_leave_types WHERE is_archived = 0 ORDER BY sort_order ASC, id ASC"
        );

        $categories = HrLeaveType::getCategories();

        return $this->view('HR.Views.leave_types.index', [
            'types'      => $types,
            'categories' => $categories,
        ]);
    }

    public function create(Request $request): Response
    {
        $this->authorize('hr.leave.manage');

        return $this->view('HR.Views.leave_types.form', [
            'leaveType'  => null,
            'categories' => HrLeaveType::getCategories(),
        ]);
    }

    public function store(Request $request): Response
    {
        $this->authorize('hr.leave.manage');

        $data = $this->extractData($request);
        $errors = $this->validateInput($data);

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

        $db = App::getInstance()->db();
        $existing = $db->selectOne(
            "SELECT id FROM hr_leave_types WHERE code = ? AND is_archived = 0",
            [$data['code']]
        );
        if ($existing) {
            return $this->flashErrorsAndRedirect(
                ['كود النوع مستخدم بالفعل'],
                $request,
                '/hr/leave-types/create'
            );
        }

        $leaveType = HrLeaveType::create($data);

        return $this->redirect('/hr/leave-types/' . $leaveType->id)->withSuccess('تم إنشاء نوع الإجازة بنجاح');
    }

    public function show(Request $request, string $id): Response
    {
        $this->authorize('hr.leave.manage');

        $db = App::getInstance()->db();
        $leaveType = $db->selectOne(
            "SELECT * FROM hr_leave_types WHERE id = ? AND is_archived = 0",
            [(int) $id]
        );
        if (!$leaveType) {
            return $this->redirect('/hr/leave-types')->withError('نوع الإجازة غير موجود');
        }

        $usageCount = $db->selectOne(
            "SELECT COUNT(*) as cnt FROM hr_leave_requests WHERE leave_type_id = ? AND is_archived = 0",
            [(int) $id]
        );

        return $this->view('HR.Views.leave_types.show', [
            'leaveType'  => $leaveType,
            'usageCount' => (int) ($usageCount['cnt'] ?? 0),
            'categories' => HrLeaveType::getCategories(),
        ]);
    }

    public function edit(Request $request, string $id): Response
    {
        $this->authorize('hr.leave.manage');

        $db = App::getInstance()->db();
        $leaveType = $db->selectOne(
            "SELECT * FROM hr_leave_types WHERE id = ? AND is_archived = 0",
            [(int) $id]
        );
        if (!$leaveType) {
            return $this->redirect('/hr/leave-types')->withError('نوع الإجازة غير موجود');
        }

        return $this->view('HR.Views.leave_types.form', [
            'leaveType'  => $leaveType,
            'categories' => HrLeaveType::getCategories(),
        ]);
    }

    public function update(Request $request, string $id): Response
    {
        $this->authorize('hr.leave.manage');

        $db = App::getInstance()->db();
        $leaveType = $db->selectOne(
            "SELECT id FROM hr_leave_types WHERE id = ? AND is_archived = 0",
            [(int) $id]
        );
        if (!$leaveType) {
            return $this->redirect('/hr/leave-types')->withError('نوع الإجازة غير موجود');
        }

        $data = $this->extractData($request);
        $errors = $this->validateInput($data, (int) $id);

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

        $existing = $db->selectOne(
            "SELECT id FROM hr_leave_types WHERE code = ? AND id != ? AND is_archived = 0",
            [$data['code'], (int) $id]
        );
        if ($existing) {
            return $this->flashErrorsAndRedirect(
                ['كود النوع مستخدم بالفعل بواسطة نوع إجازة آخر'],
                $request,
                '/hr/leave-types/' . $id . '/edit'
            );
        }

        $data['updated_at'] = date('Y-m-d H:i:s');
        $db->update('hr_leave_types', $data, '`id` = ?', [(int) $id]);

        return $this->redirect('/hr/leave-types/' . $id)->withSuccess('تم تحديث نوع الإجازة بنجاح');
    }

    public function archive(Request $request, string $id): Response
    {
        $this->authorize('hr.leave.manage');

        $db = App::getInstance()->db();
        $leaveType = $db->selectOne(
            "SELECT id FROM hr_leave_types WHERE id = ? AND is_archived = 0",
            [(int) $id]
        );
        if (!$leaveType) {
            return $this->redirect('/hr/leave-types')->withError('نوع الإجازة غير موجود');
        }

        $activeRequests = $db->selectOne(
            "SELECT COUNT(*) as cnt FROM hr_leave_requests WHERE leave_type_id = ? AND status IN ('pending','approved') AND is_archived = 0",
            [(int) $id]
        );

        if ((int) ($activeRequests['cnt'] ?? 0) > 0) {
            return $this->redirect('/hr/leave-types/' . $id)->withError('لا يمكن حذف نوع إجازة له طلبات نشطة (' . $activeRequests['cnt'] . ' طلب)');
        }

        $db->update('hr_leave_types', [
            'is_archived' => 1,
            'archived_at' => date('Y-m-d H:i:s'),
            'archived_by' => App::getInstance()->session()->get('employee_id'),
        ], '`id` = ?', [(int) $id]);

        return $this->redirect('/hr/leave-types')->withSuccess('تم حذف نوع الإجازة بنجاح');
    }

    private function extractData(Request $request): array
    {
        return [
            'code'                    => strtolower(trim((string) $request->post('code', ''))),
            'name_ar'                 => trim((string) $request->post('name_ar', '')),
            'name_en'                 => trim((string) $request->post('name_en', '')) ?: null,
            'category'                => trim((string) $request->post('category', '')) ?: null,
            'default_days_per_year'   => $this->nullableDecimal($request->post('default_days_per_year')),
            'max_days_per_year'       => $this->nullableDecimal($request->post('max_days_per_year')),
            'is_paid'                 => (int) ($request->post('is_paid', 1)),
            'pay_percentage'          => (float) ($request->post('pay_percentage', 100)),
            'requires_approval'       => (int) ($request->post('requires_approval', 1)),
            'requires_attachment'     => (int) ($request->post('requires_attachment', 0)),
            'min_days_per_request'    => $this->nullableDecimal($request->post('min_days_per_request')),
            'max_days_per_request'    => $this->nullableDecimal($request->post('max_days_per_request')),
            'min_consecutive_annual'  => $this->nullableDecimal($request->post('min_consecutive_annual')),
            'max_consecutive_days'    => $this->nullableInt($request->post('max_consecutive_days')),
            'max_per_occurrence'      => $this->nullableInt($request->post('max_per_occurrence')),
            'max_times_in_career'     => $this->nullableInt($request->post('max_times_in_career')),
            'min_service_months'      => $this->nullableInt($request->post('min_service_months')),
            'advance_notice_days'     => (int) ($request->post('advance_notice_days', 0)),
            'gender_restriction'      => trim((string) $request->post('gender_restriction', '')) ?: null,
            'carry_over_allowed'      => (int) ($request->post('carry_over_allowed', 0)),
            'max_carry_over_days'     => $this->nullableDecimal($request->post('max_carry_over_days')),
            'carry_over_expiry_months' => $this->nullableInt($request->post('carry_over_expiry_months')),
            'is_accumulative'         => (int) ($request->post('is_accumulative', 0)),
            'accumulation_max_years'  => $this->nullableInt($request->post('accumulation_max_years')),
            'career_max_times'        => $this->nullableInt($request->post('career_max_times')),
            'career_max_days'         => $this->nullableDecimal($request->post('career_max_days')),
            'deduct_from_salary'      => (int) ($request->post('deduct_from_salary', 0)),
            'legal_reference'         => trim((string) $request->post('legal_reference', '')) ?: null,
            'is_active'               => (int) ($request->post('is_active', 1)),
            'sort_order'              => (int) ($request->post('sort_order', 0)),
        ];
    }

    private function validateInput(array $data, ?int $excludeId = null): array
    {
        $errors = [];

        if ($data['code'] === '' || mb_strlen($data['code']) < 2) {
            $errors[] = 'كود النوع مطلوب (حرفان على الأقل)';
        }
        if (!preg_match('/^[a-z][a-z0-9_]*$/', $data['code'])) {
            $errors[] = 'كود النوع يجب أن يكون بالإنجليزية (حروف صغيرة وأرقام و _ فقط)';
        }
        if ($data['name_ar'] === '' || mb_strlen($data['name_ar']) < 2) {
            $errors[] = 'اسم النوع بالعربي مطلوب (حرفان على الأقل)';
        }
        if ($data['pay_percentage'] < 0 || $data['pay_percentage'] > 100) {
            $errors[] = 'نسبة الراتب يجب أن تكون بين 0 و 100';
        }

        return $errors;
    }

    private function nullableDecimal(mixed $value): ?float
    {
        $v = trim((string) ($value ?? ''));
        return $v !== '' ? (float) $v : null;
    }

    private function nullableInt(mixed $value): ?int
    {
        $v = trim((string) ($value ?? ''));
        return $v !== '' && $v !== '0' ? (int) $v : ($v === '0' ? 0 : null);
    }

    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);
    }
}
