<?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\Logger;
use App\Modules\HR\Models\HrDisciplinaryAction;
use App\Modules\HR\Models\HrEmployeeProfile;
use App\Modules\HR\Services\DisciplinaryService;
use App\Modules\Workflow\Services\WorkflowEngine;

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

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

        return $this->view('HR.Views.disciplinary.index', [
            'actions'    => $result['data'],
            'pagination' => $result['pagination'],
            'filters'    => $filters,
        ]);
    }

    public function create(Request $request): Response
    {
        $employees = HrEmployeeProfile::getActiveIds();

        return $this->view('HR.Views.disciplinary.form', [
            'action'    => null,
            'employees' => $employees,
        ]);
    }

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

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

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

        $data['status'] = 'investigation';
        $data['created_at'] = date('Y-m-d H:i:s');
        $data['updated_at'] = date('Y-m-d H:i:s');
        $data['created_by'] = $employee ? (int) $employee->id : null;

        $actionId = $db->insert('hr_disciplinary_actions', $data);

        if (WorkflowEngine::hasDefinition('hr_disciplinary')) {
            try {
                WorkflowEngine::createInstance('hr_disciplinary', 'hr_disciplinary_actions', $actionId);
            } catch (\Throwable $e) {
                Logger::warning("Workflow instance creation failed for disciplinary", ['action_id' => $actionId, 'error' => $e->getMessage()]);
            }
        }

        Logger::info("Disciplinary action created", ['action_id' => $actionId, 'profile_id' => $data['employee_profile_id']]);

        return $this->redirect('/hr/disciplinary/' . $actionId)->withSuccess('تم إنشاء الإجراء التأديبي بنجاح');
    }

    public function show(Request $request, string $id): Response
    {
        $action = HrDisciplinaryAction::find((int) $id);
        if (!$action) {
            return $this->redirect('/hr/disciplinary')->withError('الإجراء التأديبي غير موجود');
        }

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

        $canAppeal = DisciplinaryService::canAppeal($action->toArray());

        $db = App::getInstance()->db();
        $decider = $action->decided_by
            ? $db->selectOne("SELECT name FROM employees WHERE id = ?", [(int) $action->decided_by])
            : null;

        return $this->view('HR.Views.disciplinary.show', [
            'action'    => $action,
            'profile'   => $profile,
            'canAppeal' => $canAppeal,
            'decider'   => $decider,
        ]);
    }

    public function edit(Request $request, string $id): Response
    {
        $action = HrDisciplinaryAction::find((int) $id);
        if (!$action) {
            return $this->redirect('/hr/disciplinary')->withError('الإجراء التأديبي غير موجود');
        }

        if (!in_array($action->status, ['investigation', 'pending_decision'], true)) {
            return $this->redirect('/hr/disciplinary/' . $id)->withError('لا يمكن تعديل إجراء تأديبي في هذه الحالة');
        }

        $employees = HrEmployeeProfile::getActiveIds();

        return $this->view('HR.Views.disciplinary.form', [
            'action'    => $action,
            'employees' => $employees,
        ]);
    }

    public function update(Request $request, string $id): Response
    {
        $action = HrDisciplinaryAction::find((int) $id);
        if (!$action) {
            return $this->redirect('/hr/disciplinary')->withError('الإجراء التأديبي غير موجود');
        }

        if (!in_array($action->status, ['investigation', 'pending_decision'], true)) {
            return $this->redirect('/hr/disciplinary/' . $id)->withError('لا يمكن تعديل إجراء تأديبي في هذه الحالة');
        }

        $data = $this->extractData($request);
        unset($data['employee_profile_id']); // Can't change employee
        $errors = $this->validateInput($data, true);

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

        $action->update($data);

        return $this->redirect('/hr/disciplinary/' . $id)->withSuccess('تم تحديث الإجراء التأديبي بنجاح');
    }

    public function decide(Request $request, string $id): Response
    {
        $action = HrDisciplinaryAction::find((int) $id);
        if (!$action) {
            return $this->redirect('/hr/disciplinary')->withError('الإجراء التأديبي غير موجود');
        }

        if (!in_array($action->status, ['investigation', 'pending_decision'], true)) {
            return $this->redirect('/hr/disciplinary/' . $id)->withError('لا يمكن اتخاذ قرار في هذه الحالة');
        }

        $penaltyType = trim((string) $request->post('penalty_type', ''));
        $penaltyDays = (int) $request->post('penalty_days', 0);
        $effectiveDate = trim((string) $request->post('effective_date', date('Y-m-d')));
        $notes = trim((string) $request->post('notes', ''));

        $errors = [];
        if (!in_array($penaltyType, ['warning', 'wage_deduction', 'raise_postponement', 'suspension', 'demotion', 'termination'], true)) {
            $errors[] = 'نوع الجزاء غير صالح';
        }

        // Validate against Art.58 limits
        if (in_array($penaltyType, ['wage_deduction', 'suspension'], true) && $penaltyDays > 0) {
            $limitErrors = DisciplinaryService::validatePenalty(
                (int) $action->employee_profile_id,
                $penaltyType,
                $penaltyDays,
                $effectiveDate
            );
            $errors = array_merge($errors, $limitErrors);
        }

        if (!empty($errors)) {
            $session = App::getInstance()->session();
            $session->flash('_alerts', array_map(fn($e) => ['type' => 'error', 'message' => $e], $errors));
            return $this->redirect('/hr/disciplinary/' . $id);
        }

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

        $updateData = [
            'penalty_type'  => $penaltyType,
            'effective_date' => $effectiveDate,
            'notes'         => $notes ?: null,
            'decided_by'    => $employee ? (int) $employee->id : null,
            'decided_at'    => date('Y-m-d H:i:s'),
            'status'        => 'decided',
            'updated_at'    => date('Y-m-d H:i:s'),
            'updated_by'    => $employee ? (int) $employee->id : null,
        ];

        if ($penaltyType === 'wage_deduction') {
            $updateData['wage_deduction_days'] = $penaltyDays;
        } elseif ($penaltyType === 'suspension') {
            $updateData['suspension_days'] = $penaltyDays;
        }

        $db->update('hr_disciplinary_actions', $updateData, '`id` = ?', [(int) $id]);

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

        Logger::info("Disciplinary decision made", [
            'action_id'    => (int) $id,
            'penalty_type' => $penaltyType,
            'penalty_days' => $penaltyDays,
        ]);

        return $this->redirect('/hr/disciplinary/' . $id)->withSuccess('تم اتخاذ القرار التأديبي بنجاح');
    }

    public function appeal(Request $request, string $id): Response
    {
        $action = HrDisciplinaryAction::find((int) $id);
        if (!$action) {
            return $this->redirect('/hr/disciplinary')->withError('الإجراء التأديبي غير موجود');
        }

        if (!DisciplinaryService::canAppeal($action->toArray())) {
            return $this->redirect('/hr/disciplinary/' . $id)->withError('انتهت فترة التظلم (7 أيام من تاريخ التنفيذ)');
        }

        $appealText = trim((string) $request->post('appeal_text', ''));
        if ($appealText === '') {
            return $this->redirect('/hr/disciplinary/' . $id)->withError('سبب التظلم مطلوب');
        }

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

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

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

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

    private function extractData(Request $request): array
    {
        return [
            'employee_profile_id'  => (int) $request->post('employee_profile_id', 0),
            'incident_date'        => trim((string) $request->post('incident_date', '')),
            'incident_description' => trim((string) $request->post('incident_description', '')),
            'investigation_notes'  => trim((string) $request->post('investigation_notes', '')) ?: null,
            'investigation_result' => trim((string) $request->post('investigation_result', '')) ?: null,
        ];
    }

    private function validateInput(array $data, bool $isUpdate = false): array
    {
        $errors = [];
        if (!$isUpdate && ($data['employee_profile_id'] ?? 0) <= 0) {
            $errors[] = 'الموظف غير محدد';
        }
        if (($data['incident_date'] ?? '') === '') {
            $errors[] = 'تاريخ الواقعة مطلوب';
        }
        if (($data['incident_description'] ?? '') === '') {
            $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);
    }
}
