<?php
/**
 * AuthService
 * Handles business logic for authentication, security, and session management
 */

require_once __DIR__ . '/../Models/User.php';
require_once __DIR__ . '/../Models/LoginAttempt.php';
require_once __DIR__ . '/../Models/LoginHistory.php';
require_once __DIR__ . '/../Models/UserSession.php';
require_once __DIR__ . '/../Models/PasswordResetToken.php';
require_once __DIR__ . '/../Models/UserAgreement.php';
require_once __DIR__ . '/../Models/ActivityLog.php';
require_once __DIR__ . '/../Helpers/Validator.php';
require_once __DIR__ . '/../Helpers/Session.php';
require_once __DIR__ . '/../Helpers/Security.php';
require_once __DIR__ . '/../Helpers/Auth.php';
require_once __DIR__ . '/../Helpers/Language.php';

class AuthService {
    private User $userModel;
    private LoginAttempt $attemptModel;
    private LoginHistory $historyModel;
    private UserSession $sessionModel;
    private PasswordResetToken $resetTokenModel;
    private UserAgreement $agreementModel;
    private ActivityLog $activityLog;

    public function __construct() {
        $this->userModel = new User();
        $this->attemptModel = new LoginAttempt();
        $this->historyModel = new LoginHistory();
        $this->sessionModel = new UserSession();
        $this->resetTokenModel = new PasswordResetToken();
        $this->agreementModel = new UserAgreement();
        $this->activityLog = new ActivityLog();
    }

    /**
     * Register a new user with Customer role
     */
    public function register(array $data): array {
        $validator = Validator::make($data, [
            'username' => 'required|username|unique:users,username',
            'email' => 'required|email|unique:users,email',
            'phone' => 'required|phone|unique:users,phone',
            'first_name' => 'required|max:100',
            'last_name' => 'max:100',
            'password' => 'required|min:8',
            'confirm_password' => 'required|matches:password',
            'terms_accepted' => 'accepted'
        ]);

        if (!$validator->validate()) {
            return ['success' => false, 'errors' => $validator->errors()];
        }

        // Clean phone number (digits only)
        $cleanPhone = preg_replace('/[^0-9]/', '', (string)$data['phone']);

        $passwordHash = password_hash($data['password'], PASSWORD_BCRYPT, ['cost' => 12]);

        $userId = $this->userModel->insert([
            'username' => trim($data['username']),
            'email' => trim(strtolower($data['email'])),
            'phone' => $cleanPhone,
            'password' => $passwordHash,
            'first_name' => trim($data['first_name']),
            'last_name' => trim($data['last_name'] ?? ''),
            'profile_image' => null,
            'status' => 'active',
            'is_temp_password' => 0,
            'created_at' => date('Y-m-d H:i:s'),
            'updated_at' => date('Y-m-d H:i:s')
        ]);

        // Assign Customer role by default
        $this->userModel->assignRole($userId, 'customer');

        // Record agreement acceptance
        $agreement = $this->agreementModel->getLatestActive('customer');
        if ($agreement) {
            $this->agreementModel->recordAcceptance($userId, (int)$agreement['id'], 'customer', $agreement['version']);
        }

        // Log registration activity
        $this->activityLog->record($userId, 'user_register', 'user', $userId, 'User registered account');

        return ['success' => true, 'user_id' => $userId];
    }

    /**
     * Authenticate user with Username or Email + Password
     */
    public function authenticate(string $identifier, string $password, bool $rememberMe = false): array {
        $identifier = trim($identifier);
        $clientIp = Security::getClientIp();

        if (empty($identifier) || empty($password)) {
            return ['success' => false, 'message' => __('invalid_credentials')];
        }

        // Check Brute Force Lockout
        [$isLocked, $lockRemainingMinutes] = $this->attemptModel->checkLockout($identifier, $clientIp);
        if ($isLocked) {
            return [
                'success' => false,
                'message' => __('account_locked', ['minutes' => $lockRemainingMinutes])
            ];
        }

        $user = $this->userModel->findByUsernameOrEmail($identifier);

        if (!$user || !password_verify($password, $user['password'])) {
            $isNowLocked = $this->attemptModel->recordFailure($identifier, $clientIp);

            if ($user) {
                $this->historyModel->record((int)$user['id'], $isNowLocked ? 'locked' : 'failed');
            }

            if ($isNowLocked) {
                return [
                    'success' => false,
                    'message' => __('account_locked', ['minutes' => 15])
                ];
            }

            return ['success' => false, 'message' => __('invalid_credentials')];
        }

        // Check Account Status
        if ($user['status'] === 'suspended') {
            $this->historyModel->record((int)$user['id'], 'failed');
            return ['success' => false, 'message' => __('account_suspended')];
        }

        if ($user['status'] === 'deleted' || !empty($user['deleted_at'])) {
            return ['success' => false, 'message' => __('account_deleted')];
        }

        // Clear failed attempts upon successful authentication
        $this->attemptModel->clearAttempts($identifier, $clientIp);

        // Regenerate PHP session to prevent session fixation
        Session::regenerate();
        Session::set('user_id', (int)$user['id']);
        Session::set('username', $user['username']);
        Session::set('email', $user['email']);

        // Update last login
        $this->userModel->updateLastLogin((int)$user['id']);

        // Record successful login in history
        $this->historyModel->record((int)$user['id'], 'success');

        // Handle Remember Me token
        if ($rememberMe) {
            $token = $this->sessionModel->createRememberSession((int)$user['id']);
            if (!headers_sent()) {
                setcookie(
                    'cargoo_remember',
                    $token,
                    time() + 2592000, // 30 days
                    '/',
                    '',
                    isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on',
                    true // HttpOnly
                );
            }
        }

        // Clear cached auth helper
        Auth::clearCache();

        return [
            'success' => true,
            'user' => $user,
            'roles' => $this->userModel->getUserRoles((int)$user['id'])
        ];
    }

    /**
     * Check Remember-Me Cookie on boot
     */
    public function checkRememberCookie(): void {
        if (!Auth::check() && !empty($_COOKIE['cargoo_remember'])) {
            $rawToken = $_COOKIE['cargoo_remember'];
            $session = $this->sessionModel->findValidSession($rawToken);

            if ($session) {
                $user = $this->userModel->findById((int)$session['user_id']);
                if ($user && $user['status'] === 'active') {
                    Session::regenerate();
                    Session::set('user_id', (int)$user['id']);
                    Session::set('username', $user['username']);
                    Session::set('email', $user['email']);
                    $this->userModel->updateLastLogin((int)$user['id']);
                } else {
                    $this->sessionModel->removeSessionByToken($rawToken);
                    setcookie('cargoo_remember', '', time() - 3600, '/');
                }
            } else {
                setcookie('cargoo_remember', '', time() - 3600, '/');
            }
        }
    }

    /**
     * Logout
     */
    public function logout(): void {
        if (!empty($_COOKIE['cargoo_remember'])) {
            $this->sessionModel->removeSessionByToken($_COOKIE['cargoo_remember']);
            setcookie('cargoo_remember', '', time() - 3600, '/');
        }

        Auth::logout();
    }

    /**
     * Request Password Reset Link
     */
    public function requestPasswordReset(string $identifier): array {
        $identifier = trim($identifier);
        $user = $this->userModel->findByUsernameOrEmail($identifier);

        // Security rule: Do not reveal if user exists to avoid user enumeration
        $genericSuccess = [
            'success' => true,
            'message' => __('reset_link_sent')
        ];

        if (!$user || $user['status'] !== 'active') {
            return $genericSuccess;
        }

        $token = $this->resetTokenModel->createToken((int)$user['id']);
        $resetUrl = Url::to('/reset-password?token=' . $token);

        // In production this sends an email. For local/demo environment, we also save token in flash message for testing
        $genericSuccess['reset_link'] = $resetUrl;
        $genericSuccess['token'] = $token;

        $this->activityLog->record((int)$user['id'], 'password_reset_requested', 'user', (int)$user['id'], 'Requested password reset token');

        return $genericSuccess;
    }

    /**
     * Verify and Reset Password using Token
     */
    public function resetPassword(string $token, string $newPassword, string $confirmPassword): array {
        $validator = Validator::make([
            'password' => $newPassword,
            'confirm_password' => $confirmPassword
        ], [
            'password' => 'required|min:8',
            'confirm_password' => 'required|matches:password'
        ]);

        if (!$validator->validate()) {
            return ['success' => false, 'errors' => $validator->errors()];
        }

        $tokenRecord = $this->resetTokenModel->verifyToken($token);
        if (!$tokenRecord) {
            return ['success' => false, 'message' => __('invalid_or_expired_token')];
        }

        $userId = (int)$tokenRecord['user_id'];
        $newHash = password_hash($newPassword, PASSWORD_BCRYPT, ['cost' => 12]);

        $this->userModel->update($userId, [
            'password' => $newHash,
            'is_temp_password' => 0,
            'updated_at' => date('Y-m-d H:i:s')
        ]);

        $this->resetTokenModel->markAsUsed((int)$tokenRecord['id']);

        // Invalidate all active sessions for this user
        $this->sessionModel->removeAllForUser($userId);

        $this->activityLog->record($userId, 'password_reset_completed', 'user', $userId, 'Password reset via secure token');

        return ['success' => true, 'message' => __('password_reset_success')];
    }
}
