<?php

namespace App\Controllers;

use App\Core\Auth;
use App\Core\AuditLog;
use App\Core\Router;

class AuthController
{
    public function showLogin(): void
    {
        if (Auth::check()) {
            Router::redirect('/admin/users');
        }
        $error = $_SESSION['login_error'] ?? null;
        $success = $_SESSION['reset_success'] ?? null;
        unset($_SESSION['login_error'], $_SESSION['reset_success']);
        require __DIR__ . '/../Views/auth/login.php';
    }

    public function login(): void
    {
        $username = trim($_POST['username'] ?? '');
        $password = $_POST['password'] ?? '';
        $securityAnswer = trim($_POST['security_answer'] ?? '');
        $expected = (int)($_SESSION['security_expected'] ?? -1);

        if ((int)$securityAnswer !== $expected) {
            $_SESSION['login_error'] = 'Security check failed. Please try again.';
            Router::redirect('/login');
        }

        if (!empty($_POST['remember_me'])) {
            // Extend the session cookie to 30 days instead of the default session length
            $params = session_get_cookie_params();
            setcookie(session_name(), session_id(), [
                'expires'  => time() + (30 * 24 * 60 * 60),
                'path'     => $params['path'],
                'domain'   => $params['domain'],
                'secure'   => $params['secure'],
                'httponly' => true,
                'samesite' => 'Lax',
            ]);
        }

        $ip = $_SERVER['REMOTE_ADDR'] ?? '';
        $result = Auth::attempt($username, $password, $ip);

        if (!$result) {
            $_SESSION['login_error'] = 'Invalid username or password.';
            Router::redirect('/login');
        }

        if (isset($result['error']) && $result['error'] === 'disabled') {
            $_SESSION['login_error'] = 'This account has been disabled. Contact your administrator.';
            Router::redirect('/login');
        }

        if (isset($result['requires_2fa'])) {
            Router::redirect('/login/verify');
        }

        // Route by role: Super Admin -> admin panel, others -> workspace
        if ($result['is_super_admin']) {
            Router::redirect('/admin/users');
        }
        Router::redirect('/workspace');
    }

    public function showVerify(): void
    {
        if (!Auth::hasPending2fa()) {
            Router::redirect('/login');
        }
        $error = $_SESSION['verify_error'] ?? null;
        unset($_SESSION['verify_error']);
        require __DIR__ . '/../Views/auth/verify_2fa.php';
    }

    public function verify(): void
    {
        $code = trim($_POST['code'] ?? '');
        $result = Auth::verifyTwoFactorAndLogin($code);

        if (!$result) {
            $_SESSION['verify_error'] = 'Invalid or expired code. Please try again.';
            Router::redirect('/login/verify');
        }

        if ($result['is_super_admin']) {
            Router::redirect('/admin/users');
        }
        Router::redirect('/workspace');
    }

    public function logout(): void
    {
        Auth::logout();
        Router::redirect('/login');
    }

    public function showForgotPassword(): void
    {
        $error = $_SESSION['forgot_error'] ?? null;
        $sent = $_SESSION['forgot_sent'] ?? null;
        unset($_SESSION['forgot_error'], $_SESSION['forgot_sent']);
        require __DIR__ . '/../Views/auth/forgot_password.php';
    }

    public function sendResetLink(): void
    {
        $email = trim($_POST['email'] ?? '');

        if ($email === '') {
            $_SESSION['forgot_error'] = 'Please enter your email address.';
            Router::redirect('/forgot-password');
        }

        $db = \App\Core\Database::connection();
        $tables = [
            'superadmin' => 'superadmins',
            'manager'    => 'managers',
            'employee'   => 'employees',
        ];

        $found = null;
        foreach ($tables as $type => $table) {
            $stmt = $db->prepare("SELECT id, email FROM `{$table}` WHERE email = :e LIMIT 1");
            $stmt->execute(['e' => $email]);
            $row = $stmt->fetch();
            if ($row) { $found = ['type' => $type, 'table' => $table, 'id' => $row['id']]; break; }
        }

        // Always show the same success message whether or not the email exists,
        // so this can't be used to enumerate valid accounts.
        if ($found) {
            $token = bin2hex(random_bytes(32));
            $expires = gmdate('Y-m-d H:i:s', time() + 3600); // 1 hour, stored in UTC to match UTC_TIMESTAMP() below

            $db->prepare("UPDATE `{$found['table']}` SET reset_token = :t, reset_expires = :e WHERE id = :id")
               ->execute(['t' => $token, 'e' => $expires, 'id' => $found['id']]);

            $cfg = require __DIR__ . '/../../config/config.php';
            $resetUrl = rtrim($cfg['app']['url'], '/') . '/reset-password?token=' . $token;

            $html = "
                <div style='font-family:Arial,sans-serif;max-width:480px;margin:0 auto;'>
                    <h2 style='color:#0f2557;'>Reset your Hammerhead Workspace password</h2>
                    <p>We received a request to reset your password. Click the button below to choose a new one. This link expires in 1 hour.</p>
                    <p style='margin:28px 0;'>
                        <a href='{$resetUrl}' style='background:#1d63e0;color:#fff;padding:12px 22px;border-radius:8px;text-decoration:none;font-weight:bold;'>Reset Password</a>
                    </p>
                    <p style='color:#666;font-size:13px;'>If you didn't request this, you can safely ignore this email.</p>
                </div>
            ";

            \App\Core\Mailer::send($email, $email, 'Reset your Hammerhead Workspace password', $html);
        }

        $_SESSION['forgot_sent'] = true;
        Router::redirect('/forgot-password');
    }

    public function showResetPassword(): void
    {
        $token = $_GET['token'] ?? '';
        $person = $this->findByResetToken($token);

        if (!$person) {
            $error = 'This password reset link is invalid or has expired. Please request a new one.';
            require __DIR__ . '/../Views/auth/reset_password_invalid.php';
            return;
        }

        $error = $_SESSION['reset_error'] ?? null;
        unset($_SESSION['reset_error']);
        require __DIR__ . '/../Views/auth/reset_password.php';
    }

    public function resetPassword(): void
    {
        $token = $_POST['token'] ?? '';
        $password = $_POST['password'] ?? '';
        $confirm = $_POST['password_confirm'] ?? '';

        $person = $this->findByResetToken($token);
        if (!$person) {
            $_SESSION['login_error'] = 'This password reset link is invalid or has expired. Please request a new one.';
            Router::redirect('/login');
        }

        if (strlen($password) < 8) {
            $_SESSION['reset_error'] = 'Password must be at least 8 characters.';
            Router::redirect('/reset-password?token=' . urlencode($token));
        }

        if ($password !== $confirm) {
            $_SESSION['reset_error'] = 'Passwords do not match.';
            Router::redirect('/reset-password?token=' . urlencode($token));
        }

        $db = \App\Core\Database::connection();
        $db->prepare("UPDATE `{$person['table']}` SET password_hash = :p, reset_token = NULL, reset_expires = NULL WHERE id = :id")
           ->execute(['p' => password_hash($password, PASSWORD_DEFAULT), 'id' => $person['id']]);

        AuditLog::record($person['type'], (int)$person['id'], '', 'user', (string)$person['id'], 'password_reset');

        $_SESSION['login_error'] = null;
        $_SESSION['reset_success'] = 'Your password has been reset. Please sign in.';
        Router::redirect('/login');
    }

    private function findByResetToken(string $token): ?array
    {
        if ($token === '') return null;

        $db = \App\Core\Database::connection();
        $tables = ['superadmin' => 'superadmins', 'manager' => 'managers', 'employee' => 'employees'];

        foreach ($tables as $type => $table) {
            $stmt = $db->prepare("SELECT id FROM `{$table}` WHERE reset_token = :t AND reset_expires > UTC_TIMESTAMP() LIMIT 1");
            $stmt->execute(['t' => $token]);
            $row = $stmt->fetch();
            if ($row) return ['type' => $type, 'table' => $table, 'id' => $row['id']];
        }

        return null;
    }
}
