<?php
declare(strict_types=1);

namespace App\Modules\Auth\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\Users\Models\Employee;
use App\Modules\Auth\Models\LoginAttempt;
use App\Modules\Auth\Models\ActiveSession;

class AuthController extends Controller
{
    public function home(Request $request): Response
    {
        $redirect = config('auth.default_redirect', '/users');
        return $this->redirect($redirect);
    }

    public function login(Request $request): Response
    {
        $session = App::getInstance()->session();
        if ($session->get('employee_id')) {
            return $this->redirect(config('auth.default_redirect', '/users'));
        }
        return $this->view('Auth.Views.login', []);
    }

    public function authenticate(Request $request): Response
    {
        $username = trim((string) $request->post('username', ''));
        $password = (string) $request->post('password', '');
        $ip = $request->ip();
        $userAgent = $request->userAgent();

        if ($username === '' || $password === '') {
            return $this->redirect('/login')->withError('يرجى إدخال اسم المستخدم وكلمة المرور');
        }

        $employee = Employee::findByUsername($username);

        if (!$employee) {
            LoginAttempt::record($username, $ip, $userAgent, false, 'user_not_found');
            return $this->redirect('/login')->withError('بيانات الدخول غير صحيحة');
        }

        if ($employee->is_archived) {
            LoginAttempt::record($username, $ip, $userAgent, false, 'archived');
            return $this->redirect('/login')->withError('هذا الحساب محذوف');
        }

        if (!$employee->is_active) {
            LoginAttempt::record($username, $ip, $userAgent, false, 'inactive');
            return $this->redirect('/login')->withError('هذا الحساب معطل');
        }

        if ($employee->isLocked()) {
            LoginAttempt::record($username, $ip, $userAgent, false, 'locked');
            return $this->redirect('/login')->withError('الحساب مقفل مؤقتاً بسبب محاولات دخول فاشلة. حاول لاحقاً.');
        }

        if (!password_verify($password, $employee->password_hash)) {
            $employee->incrementFailedLogins();
            LoginAttempt::record($username, $ip, $userAgent, false, 'wrong_password');
            return $this->redirect('/login')->withError('بيانات الدخول غير صحيحة');
        }

        $employee->resetFailedLogins();
        $employee->recordLogin($ip);

        $session = App::getInstance()->session();
        $session->regenerate();
        $session->set('employee_id', (int) $employee->id);
        $session->set('employee_name', $employee->full_name_ar);
        $session->set('logged_in_at', time());
        $session->set('last_activity_at', time());

        LoginAttempt::record($username, $ip, $userAgent, true, null);
        ActiveSession::createSession((int) $employee->id, $session->id(), $ip, $userAgent);

        $this->enforceMaxSessions((int) $employee->id, $session->id());

        $eventData = ['employee_id' => (int) $employee->id, 'ip' => $ip];
        EventBus::dispatch('auth.login', $eventData);

        Logger::info("Employee logged in: {$username}", ['ip' => $ip]);

        if ($employee->force_password_change) {
            return $this->redirect('/change-password');
        }

        return $this->redirect(config('auth.default_redirect', '/users'));
    }

    public function logout(Request $request): Response
    {
        $app = App::getInstance();
        $session = $app->session();
        $employeeId = $session->get('employee_id');

        if ($employeeId) {
            ActiveSession::deactivateSession($session->id());
            $eventData = ['employee_id' => $employeeId];
            EventBus::dispatch('auth.logout', $eventData);
            Logger::info("Employee logged out", ['employee_id' => $employeeId]);
        }

        $session->destroy();
        return $this->redirect('/login');
    }

    public function changePassword(Request $request): Response
    {
        return $this->view('Auth.Views.change-password', []);
    }

    public function updatePassword(Request $request): Response
    {
        $employee = $this->currentEmployee();
        if (!$employee) {
            return $this->redirect('/login');
        }

        $currentPassword = (string) $request->post('current_password', '');
        $newPassword = (string) $request->post('new_password', '');
        $confirmPassword = (string) $request->post('new_password_confirmation', '');

        $errors = [];

        if (!$employee->force_password_change && $currentPassword === '') {
            $errors[] = 'يرجى إدخال كلمة المرور الحالية';
        }

        if (!$employee->force_password_change && !password_verify($currentPassword, $employee->password_hash)) {
            $errors[] = 'كلمة المرور الحالية غير صحيحة';
        }

        $minLength = (int) config('auth.password_min_length', 8);
        if (mb_strlen($newPassword) < $minLength) {
            $errors[] = "كلمة المرور الجديدة يجب أن تكون {$minLength} أحرف على الأقل";
        }

        if ($newPassword !== $confirmPassword) {
            $errors[] = 'كلمة المرور الجديدة غير متطابقة مع التأكيد';
        }

        if (!preg_match('/[A-Z]/', $newPassword) || !preg_match('/[a-z]/', $newPassword) || !preg_match('/[0-9]/', $newPassword)) {
            $errors[] = 'كلمة المرور يجب أن تحتوي على حرف كبير وحرف صغير ورقم';
        }

        if (empty($errors)) {
            $historyCount = (int) config('auth.password_history_count', 5);
            $db = App::getInstance()->db();
            $history = $db->select(
                "SELECT password_hash FROM password_history WHERE employee_id = ? ORDER BY created_at DESC LIMIT ?",
                [$employee->id, $historyCount]
            );
            foreach ($history as $h) {
                if (password_verify($newPassword, $h['password_hash'])) {
                    $errors[] = 'لا يمكن استخدام كلمة مرور تم استخدامها سابقاً';
                    break;
                }
            }
        }

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

        $cost = (int) config('auth.password_bcrypt_cost', 12);
        $newHash = password_hash($newPassword, PASSWORD_BCRYPT, ['cost' => $cost]);
        $db = App::getInstance()->db();

        $db->update('employees', [
            'password_hash'         => $newHash,
            'force_password_change' => 0,
            'password_changed_at'   => date('Y-m-d H:i:s'),
            'updated_at'            => date('Y-m-d H:i:s'),
        ], '`id` = ?', [$employee->id]);

        $db->insert('password_history', [
            'employee_id'   => (int) $employee->id,
            'password_hash' => $newHash,
            'created_at'    => date('Y-m-d H:i:s'),
        ]);

        $eventData = ['employee_id' => (int) $employee->id];
        EventBus::dispatch('auth.password_changed', $eventData);
        Logger::info("Password changed", ['employee_id' => $employee->id]);

        return $this->redirect(config('auth.default_redirect', '/users'))
            ->withSuccess('تم تغيير كلمة المرور بنجاح');
    }

    private function enforceMaxSessions(int $employeeId, string $currentSessionId): void
    {
        $max = (int) config('auth.max_concurrent_sessions', 3);
        $db = App::getInstance()->db();
        $activeSessions = $db->select(
            "SELECT id, session_id FROM active_sessions WHERE employee_id = ? AND is_active = 1 ORDER BY logged_in_at ASC",
            [$employeeId]
        );

        if (count($activeSessions) > $max) {
            $toDeactivate = array_slice($activeSessions, 0, count($activeSessions) - $max);
            foreach ($toDeactivate as $s) {
                if ($s['session_id'] !== $currentSessionId) {
                    $db->update('active_sessions', ['is_active' => 0], '`id` = ?', [$s['id']]);
                }
            }
        }
    }
}