<?php

declare(strict_types=1);

namespace App\Controllers;

use App\Core\Auth;
use App\Core\Controller;
use App\Core\Csrf;
use App\Core\Response;
use App\Core\Session;
use App\Core\Database;
use App\Services\AuthService;
use RuntimeException;

final class AuthController extends Controller
{
    public function __construct(private ?AuthService $service = null)
    {
        $this->service ??= new AuthService();
    }

    public function loginForm(): Response
    {
        return $this->view('auth/login', ['pageTitle' => 'Login', 'csrf' => Csrf::token(), 'error' => Session::pull('auth_error')], 'layouts/auth');
    }

    public function login(): Response
    {
        Csrf::verify((string) ($_POST['_csrf'] ?? ''));
        $email = trim((string) ($_POST['email'] ?? ''));
        $password = (string) ($_POST['password'] ?? '');

        $attempts = (int) Session::get('login_attempts', 0);
        $lockedUntil = (int) Session::get('login_locked_until', 0);
        if ($lockedUntil > time()) {
            throw new RuntimeException('Too many login attempts. Please try again later.', 429);
        }

        if ($email === '' || !filter_var($email, FILTER_VALIDATE_EMAIL) || $password === '') {
            Session::put('auth_error', 'Enter a valid email address and password.');
            return Response::redirect('/crm/public/login');
        }

        if (!Auth::attempt($email, $password)) {
            $attempts++;
            Session::put('login_attempts', $attempts);
            if ($attempts >= 5) {
                Session::put('login_locked_until', time() + 300);
                Session::put('login_attempts', 0);
                Session::put('auth_error', 'Too many login attempts. Please try again in 5 minutes.');
            } else {
                Session::put('auth_error', 'Invalid email or password.');
            }
            return Response::redirect('/crm/public/login');
        }

        Session::forget('login_attempts');
        Session::forget('login_locked_until');
        return Response::redirect('/crm/public/');
    }

    public function logout(): Response
    {
        Csrf::verify((string) ($_POST['_csrf'] ?? ''));
        Auth::logout();
        return Response::redirect('/crm/public/login');
    }

    public function forgotForm(): Response
    {
        return $this->view('auth/forgot', ['pageTitle' => 'Forgot Password', 'csrf' => Csrf::token(), 'message' => Session::pull('reset_message')], 'layouts/auth');
    }

    public function forgot(): Response
    {
        Csrf::verify((string) ($_POST['_csrf'] ?? ''));
        $email = trim((string) ($_POST['email'] ?? ''));
        if (filter_var($email, FILTER_VALIDATE_EMAIL)) {
            $token = $this->service->createResetToken($email);
            if ($token !== null) {
                $this->sendResetEmail($email, $token);
            }
        }
        Session::put('reset_message', 'If an account exists for this email, a password reset link has been sent.');
        return Response::redirect('/crm/public/forgot-password');
    }

    public function resetForm(): Response
    {
        $token = trim((string) ($_GET['token'] ?? ''));
        if ($token === '') throw new RuntimeException('Invalid password reset link.', 419);
        return $this->view('auth/reset', ['pageTitle' => 'Reset Password', 'csrf' => Csrf::token(), 'token' => $token, 'error' => Session::pull('reset_error')], 'layouts/auth');
    }

    public function reset(): Response
    {
        Csrf::verify((string) ($_POST['_csrf'] ?? ''));
        $token = trim((string) ($_POST['token'] ?? ''));
        $password = (string) ($_POST['password'] ?? '');
        $confirmation = (string) ($_POST['password_confirmation'] ?? '');
        if (strlen($password) < 12 || !hash_equals($password, $confirmation)) {
            Session::put('reset_error', 'Password must be at least 12 characters and both password fields must match.');
            return Response::redirect('/crm/public/reset-password?token=' . rawurlencode($token));
        }
        if (!$this->service->resetPassword($token, $password)) {
            throw new RuntimeException('Invalid or expired password reset link.', 419);
        }
        Session::put('auth_error', 'Your password has been reset. You can now sign in.');
        return Response::redirect('/crm/public/login');
    }

    public function setupForm(): Response
    {
        if (!$this->service->setupAvailable()) throw new RuntimeException('Initial setup has already been completed.', 403);
        return $this->view('setup/index', ['pageTitle' => 'Initial Setup', 'csrf' => Csrf::token(), 'error' => Session::pull('setup_error')], 'layouts/auth');
    }

    public function setup(): Response
    {
        Csrf::verify((string) ($_POST['_csrf'] ?? ''));
        $name = trim((string) ($_POST['full_name'] ?? ''));
        $email = trim((string) ($_POST['email'] ?? ''));
        $password = (string) ($_POST['password'] ?? '');
        $confirmation = (string) ($_POST['password_confirmation'] ?? '');
        if ($name === '' || !filter_var($email, FILTER_VALIDATE_EMAIL) || strlen($password) < 12 || !hash_equals($password, $confirmation)) {
            Session::put('setup_error', 'Enter a name, valid email, and a matching password of at least 12 characters.');
            return Response::redirect('/crm/public/setup');
        }
        try {
            $this->service->createInitialSuperAdmin($name, $email, $password);
        } catch (\PDOException $e) {
            if ((int) $e->errorInfo[1] === 1062) {
                Session::put('setup_error', 'An account with that email already exists.');
                return Response::redirect('/crm/public/setup');
            }
            throw $e;
        }
        Session::put('auth_error', 'Initial administrator created. Please sign in.');
        return Response::redirect('/crm/public/login');
    }

    private function sendResetEmail(string $email, string $token): void
    {
        $host = $_ENV['SMTP_HOST'] ?? '';
        if ($host === '') {
            error_log('Password reset link generated for ' . $email . ': ' . ($_ENV['APP_URL'] ?? 'http://localhost/crm/public') . '/reset-password?token=' . $token);
            return;
        }
        $mail = new \PHPMailer\PHPMailer\PHPMailer(true);
        $mail->isSMTP();
        $mail->Host = $host;
        $mail->Port = (int) ($_ENV['SMTP_PORT'] ?? 587);
        $mail->SMTPAuth = filter_var($_ENV['SMTP_AUTH'] ?? true, FILTER_VALIDATE_BOOL);
        $mail->Username = $_ENV['SMTP_USERNAME'] ?? '';
        $mail->Password = $_ENV['SMTP_PASSWORD'] ?? '';
        $encryption = strtolower((string) ($_ENV['SMTP_ENCRYPTION'] ?? 'tls'));
        if ($encryption === 'ssl') $mail->SMTPSecure = \PHPMailer\PHPMailer\PHPMailer::ENCRYPTION_SMTPS;
        elseif ($encryption === 'tls') $mail->SMTPSecure = \PHPMailer\PHPMailer\PHPMailer::ENCRYPTION_STARTTLS;
        $mail->setFrom($_ENV['SMTP_FROM_EMAIL'] ?? $mail->Username, $_ENV['SMTP_FROM_NAME'] ?? 'Grow Tool Therapies');
        $mail->addAddress($email);
        $mail->isHTML(true);
        $url = rtrim($_ENV['APP_URL'] ?? 'http://localhost/crm/public', '/') . '/reset-password?token=' . rawurlencode($token);
        $mail->Subject = 'Password reset request';
        $mail->Body = '<p>A password reset was requested for your account.</p><p><a href="' . htmlspecialchars($url, ENT_QUOTES, 'UTF-8') . '">Reset your password</a></p><p>This link expires in 60 minutes and can only be used once.</p>';
        $mail->AltBody = 'Reset your password: ' . $url . ' This link expires in 60 minutes and can only be used once.';
        $mail->send();
    }
}
