<?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\HrLeaveRequest;
use App\Modules\HR\Models\HrLeaveType;
use App\Modules\HR\Models\HrLeaveBalance;
use App\Modules\HR\Models\HrEmployeeProfile;
use App\Modules\HR\Models\HrDepartment;
use App\Modules\HR\Services\LeaveService;
use App\Modules\Workflow\Services\WorkflowEngine;
use App\Core\Logger;

class LeaveController extends Controller
{
    public function index(Request $request): Response
    {
        $filters = [
            'q'              => trim((string) $request->get('q', '')),
            'status'         => trim((string) $request->get('status', '')),
            'leave_type_id'  => trim((string) $request->get('leave_type_id', '')),
            'department_id'  => trim((string) $request->get('department_id', '')),
            'date_from'      => trim((string) $request->get('date_from', '')),
            'date_to'        => trim((string) $request->get('date_to', '')),
        ];

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

        $db = App::getInstance()->db();
        $leaveTypes = $db->select("SELECT id, name_ar FROM hr_leave_types WHERE is_active = 1 AND is_archived = 0 ORDER BY name_ar");

        return $this->view('HR.Views.leaves.index', [
            'requests'    => $result['data'],
            'pagination'  => $result['pagination'],
            'filters'     => $filters,
            'leaveTypes'  => $leaveTypes,
            'departments' => HrDepartment::allActive(),
        ]);
    }

    public function requestForm(Request $request): Response
    {
        $db = App::getInstance()->db();
        $leaveTypes = $db->select(
            "SELECT id, name_ar, code, max_days_per_year, requires_attachment
             FROM hr_leave_types WHERE is_active = 1 AND is_archived = 0 ORDER BY sort_order, name_ar"
        );

        $employees = HrEmployeeProfile::getActiveIds();

        return $this->view('HR.Views.leaves.request_form', [
            'leaveTypes' => $leaveTypes,
            'employees'  => $employees,
        ]);
    }

    public function storeRequest(Request $request): Response
    {
        $data = [
            'employee_profile_id' => (int) $request->post('employee_profile_id', 0),
            'leave_type_id'       => (int) $request->post('leave_type_id', 0),
            'start_date'          => trim((string) $request->post('start_date', '')),
            'end_date'            => trim((string) $request->post('end_date', '')),
            'half_day'            => (int) ($request->post('half_day', 0)),
            'reason'              => trim((string) $request->post('reason', '')) ?: null,
        ];

        $result = LeaveService::submitRequest($data);

        if (!$result['success']) {
            $session = App::getInstance()->session();
            $session->flash('_alerts', [['type' => 'error', 'message' => $result['error']]]);
            $session->flash('_old_input', $request->all());
            return $this->redirect('/hr/leaves/request');
        }

        return $this->redirect('/hr/leaves/' . $result['request_id'])->withSuccess('تم تقديم طلب الإجازة بنجاح');
    }

    public function show(Request $request, string $id): Response
    {
        $leaveRequest = HrLeaveRequest::find((int) $id);
        if (!$leaveRequest) {
            return $this->redirect('/hr/leaves')->withError('طلب الإجازة غير موجود');
        }

        $profile = HrEmployeeProfile::find((int) $leaveRequest->employee_profile_id);

        $db = App::getInstance()->db();
        $leaveType = $db->selectOne("SELECT * FROM hr_leave_types WHERE id = ?", [(int) $leaveRequest->leave_type_id]);

        // Current balance for this leave type
        $balance = HrLeaveBalance::getBalance(
            (int) $leaveRequest->employee_profile_id,
            (int) $leaveRequest->leave_type_id,
            (int) date('Y')
        );

        $approver = $leaveRequest->approved_by
            ? $db->selectOne("SELECT name FROM employees WHERE id = ?", [(int) $leaveRequest->approved_by])
            : null;

        return $this->view('HR.Views.leaves.show', [
            'leaveRequest' => $leaveRequest,
            'profile'      => $profile,
            'leaveType'    => $leaveType,
            'balance'      => $balance,
            'approver'     => $approver,
        ]);
    }

    public function approve(Request $request, string $id): Response
    {
        $result = LeaveService::approveRequest((int) $id);

        if (!$result['success']) {
            return $this->redirect('/hr/leaves/' . $id)->withError($result['error']);
        }

        return $this->redirect('/hr/leaves/' . $id)->withSuccess('تم اعتماد طلب الإجازة');
    }

    public function reject(Request $request, string $id): Response
    {
        $reason = trim((string) $request->post('rejection_reason', ''));

        $result = LeaveService::rejectRequest((int) $id, $reason);

        if (!$result['success']) {
            return $this->redirect('/hr/leaves/' . $id)->withError($result['error']);
        }

        return $this->redirect('/hr/leaves/' . $id)->withSuccess('تم رفض طلب الإجازة');
    }

    public function cancel(Request $request, string $id): Response
    {
        $leaveRequest = HrLeaveRequest::find((int) $id);
        if (!$leaveRequest) {
            return $this->redirect('/hr/leaves')->withError('طلب الإجازة غير موجود');
        }

        if (!in_array($leaveRequest->status, ['pending', 'approved'], true)) {
            return $this->redirect('/hr/leaves/' . $id)->withError('لا يمكن إلغاء هذا الطلب');
        }

        $db = App::getInstance()->db();
        $employee = $this->currentEmployee();

        // Restore balance
        $year = (int) date('Y', strtotime($leaveRequest->start_date));
        $balance = HrLeaveBalance::getBalance(
            (int) $leaveRequest->employee_profile_id,
            (int) $leaveRequest->leave_type_id,
            $year
        );
        if ($balance) {
            if ($leaveRequest->status === 'approved') {
                // Approved leave: days are in used_days, reduce them
                $db->update('hr_leave_balances', [
                    'used_days'  => new \App\Core\RawExpression("GREATEST(used_days - {$leaveRequest->total_days}, 0)"),
                    'updated_at' => date('Y-m-d H:i:s'),
                ], '`id` = ?', [(int) $balance['id']]);
            } else {
                // Pending leave: days are in pending_days, release them
                HrLeaveBalance::releasePendingDays((int) $balance['id'], (string) $leaveRequest->total_days);
            }
        }

        $db->update('hr_leave_requests', [
            'status'     => 'cancelled',
            'updated_at' => date('Y-m-d H:i:s'),
            'updated_by' => $employee ? (int) $employee->id : null,
        ], '`id` = ?', [(int) $id]);

        try {
            WorkflowEngine::transitionByEntity('hr_leave_requests', (int) $id, 'cancel');
        } catch (\Throwable $e) {
            Logger::warning("Workflow transition failed for leave cancel", ['request_id' => (int) $id, 'error' => $e->getMessage()]);
        }

        return $this->redirect('/hr/leaves/' . $id)->withSuccess('تم إلغاء طلب الإجازة');
    }

    public function balances(Request $request): Response
    {
        $year = (int) $request->get('year', (int) date('Y'));
        $departmentId = (int) $request->get('department_id', 0);

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

        $query = "SELECT hp.id, hp.employee_number, hp.first_name_ar, hp.last_name_ar,
                         d.name_ar as department_name
                  FROM hr_employee_profiles hp
                  LEFT JOIN hr_departments d ON d.id = hp.department_id
                  WHERE hp.employment_status = 'active' AND hp.is_archived = 0";
        $params = [];

        if ($departmentId > 0) {
            $query .= " AND hp.department_id = ?";
            $params[] = $departmentId;
        }

        $query .= " ORDER BY hp.first_name_ar ASC";
        $employees = $db->select($query, $params);

        // Get all leave types
        $leaveTypes = $db->select("SELECT id, name_ar, code FROM hr_leave_types WHERE is_active = 1 AND is_archived = 0 ORDER BY sort_order");

        // Build balances matrix
        $balancesMatrix = [];
        foreach ($employees as $emp) {
            $empBalances = [];
            foreach ($leaveTypes as $lt) {
                $bal = HrLeaveBalance::getBalance((int) $emp['id'], (int) $lt['id'], $year);
                $empBalances[$lt['id']] = $bal;
            }
            $balancesMatrix[$emp['id']] = $empBalances;
        }

        return $this->view('HR.Views.leaves.balances', [
            'employees'      => $employees,
            'leaveTypes'     => $leaveTypes,
            'balancesMatrix' => $balancesMatrix,
            'year'           => $year,
            'departmentId'   => $departmentId,
            'departments'    => HrDepartment::allActive(),
        ]);
    }

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

        $year = (int) $request->get('year', (int) date('Y'));
        $balances = HrLeaveBalance::getForEmployee((int) $employeeId, $year);

        $db = App::getInstance()->db();
        $leaveTypes = $db->select("SELECT id, name_ar, code FROM hr_leave_types WHERE is_active = 1 AND is_archived = 0 ORDER BY sort_order");

        // Recent requests
        $recentRequests = $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 YEAR(lr.start_date) = ? AND lr.is_archived = 0
             ORDER BY lr.start_date DESC LIMIT 20",
            [(int) $employeeId, $year]
        );

        return $this->view('HR.Views.leaves.employee_balance', [
            'profile'        => $profile,
            'balances'       => $balances,
            'leaveTypes'     => $leaveTypes,
            'recentRequests' => $recentRequests,
            'year'           => $year,
        ]);
    }

    public function adjustBalance(Request $request): Response
    {
        $profileId = (int) $request->post('employee_profile_id', 0);
        $leaveTypeId = (int) $request->post('leave_type_id', 0);
        $year = (int) $request->post('year', (int) date('Y'));
        $adjustment = (float) $request->post('adjustment_days', 0);
        $reason = trim((string) $request->post('reason', ''));

        if ($profileId <= 0 || $leaveTypeId <= 0) {
            return $this->back()->withError('بيانات غير مكتملة');
        }
        if ($adjustment == 0) {
            return $this->back()->withError('قيمة التعديل يجب أن تكون غير صفرية');
        }

        $db = App::getInstance()->db();
        $employee = $this->currentEmployee();

        $balance = $db->selectOne(
            "SELECT * FROM hr_leave_balances WHERE employee_profile_id = ? AND leave_type_id = ? AND year = ?",
            [$profileId, $leaveTypeId, $year]
        );

        if (!$balance) {
            return $this->back()->withError('رصيد الإجازة غير موجود لهذا العام');
        }

        $newAdjustment = bcadd((string) ($balance['adjustment_days'] ?? '0'), (string) $adjustment, 2);
        $db->update('hr_leave_balances', [
            'adjustment_days'  => $newAdjustment,
            'adjustment_reason'=> $reason ?: null,
            'updated_at'       => date('Y-m-d H:i:s'),
            'updated_by'       => $employee ? (int) $employee->id : null,
        ], '`id` = ?', [(int) $balance['id']]);

        return $this->redirect('/hr/leaves/balances/' . $profileId . '?year=' . $year)->withSuccess('تم تعديل رصيد الإجازة بنجاح');
    }

    public function calendar(Request $request): Response
    {
        $year = (int) $request->get('year', (int) date('Y'));
        $month = (int) $request->get('month', (int) date('m'));

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

        $startDate = sprintf('%04d-%02d-01', $year, $month);
        $endDate = date('Y-m-t', strtotime($startDate));

        $leaves = $db->select(
            "SELECT lr.*, lt.name_ar as leave_type_name, lt.code as leave_type_code,
                    hp.first_name_ar, hp.last_name_ar, hp.employee_number
             FROM hr_leave_requests lr
             JOIN hr_leave_types lt ON lt.id = lr.leave_type_id
             JOIN hr_employee_profiles hp ON hp.id = lr.employee_profile_id
             WHERE lr.status IN ('approved','taken')
               AND lr.start_date <= ? AND lr.end_date >= ?
               AND lr.is_archived = 0
             ORDER BY lr.start_date ASC",
            [$endDate, $startDate]
        );

        return $this->view('HR.Views.leaves.calendar', [
            'leaves' => $leaves,
            'year'   => $year,
            'month'  => $month,
        ]);
    }
}
