<?php

declare(strict_types=1);

namespace App\Controllers;

use App\Models\User;
use App\Models\Session;
use App\Services\JwtService;
use App\Services\MailService;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Illuminate\Database\Capsule\Manager as DB;
use Ramsey\Uuid\Uuid;

class AuthController
{
    use ControllerTrait;

    public function __construct(
        private JwtService  $jwt,
        private MailService $mail
    ) {}

    // POST /api/auth/register
    public function register(Request $request, Response $response): Response
    {
        $body = (array)$request->getParsedBody();
        $email   = strtolower(trim($body['email'] ?? ''));
        $password= trim($body['password'] ?? '');
        $prenom  = trim($body['prenom'] ?? '');
        $nom     = strtoupper(trim($body['nom'] ?? ''));

        // Validation
        if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
            return $this->error($response, 'VALIDATION_ERROR', 'Email invalide', 400);
        }
        if (strlen($password) < 8) {
            return $this->error($response, 'PASSWORD_TOO_SHORT', 'Le mot de passe doit faire au moins 8 caractères', 422);
        }

        // Rate limit (géré par middleware, mais double-check)
        $existingUser = User::where('email', $email)->first();
        if ($existingUser) {
            return $this->error($response, 'EMAIL_TAKEN', 'Cette adresse email est déjà utilisée', 409);
        }

        DB::beginTransaction();
        try {
            // Créer compte PS
            $prestaService = new \App\Services\PrestashopService();
            $prestaId = null;
            try {
                $prestaId = $prestaService->createCustomer($email, $prenom, $nom, $password);
            } catch (\Exception $e) {
                DB::rollBack();
                error_log('[register] PS error: ' . $e->getMessage());
                return $this->error($response, 'PS_REGISTRATION_FAILED', $e->getMessage(), 502);
            }

            // Créer compte MKO
            $userId = DB::table('users')->insertGetId([
                'email'              => $email,
                'password'           => password_hash($password, PASSWORD_BCRYPT),
                'prenom'             => $prenom ?: null,
                'nom'                => $nom ?: null,
                'role'               => 'sportif',
                'presta_customer_id' => $prestaId,
                'actif'              => 1,
                'date_inscription'   => date('Y-m-d H:i:s'),
            ]);

            DB::commit();

            $token        = $this->jwt->generateAccessToken($userId, 'sportif');
            $refreshToken = $this->jwt->generateRefreshToken($userId, $this->getIp($request), $request->getHeaderLine('User-Agent'));

            // Email bienvenue — toujours envoyé, fallback sur nom ou 'Sportif'
            $destinataire = $prenom ?: ($nom ?: 'Sportif');
            $this->mail->inscriptionSportif($email, $destinataire);

            return $this->success($response, [
                'token'        => $token,
                'refresh_token'=> $refreshToken,
                'user'         => [
                    'id'     => $userId,
                    'email'  => $email,
                    'prenom' => $prenom,
                    'nom'    => $nom,
                    'role'   => 'sportif',
                ],
            ]);
        } catch (\Exception $e) {
            DB::rollBack();
            error_log('[AuthController::register] ' . $e->getMessage());
            return $this->error($response, 'INTERNAL_ERROR', 'Erreur interne', 500);
        }
    }

    // POST /api/auth/login
    public function login(Request $request, Response $response): Response
    {
        $body     = (array)$request->getParsedBody();
        $email    = strtolower(trim($body['email'] ?? ''));
        $password = trim($body['password'] ?? '');

        if (!$email || !$password) {
            return $this->error($response, 'VALIDATION_ERROR', 'Email et mot de passe requis', 400);
        }

        $user = User::where('email', $email)->first();

        // Cas : compte PS sans MKO — créer liaison
        if (!$user) {
            $prestaService = new \App\Services\PrestashopService();
            try {
                // Tentative vérification sur PS (non implémenté sans mot de passe PS stocké)
                // Créer le compte MKO en liaison
            } catch (\Exception $e) {}
        }

        if (!$user) {
            return $this->error($response, 'INVALID_CREDENTIALS', 'Email ou mot de passe incorrect', 401);
        }

        if (!$user->actif) {
            return $this->error($response, 'ACCOUNT_DISABLED', 'Compte désactivé', 401);
        }

        if (!$user->password || !password_verify($password, $user->password)) {
            return $this->error($response, 'INVALID_CREDENTIALS', 'Email ou mot de passe incorrect', 401);
        }

        // 2FA TOTP obligatoire pour super_admin
        if ($user->role === 'super_admin') {
            $totp    = new \App\Services\TotpService();
            $totpCode= trim($body['totp_code'] ?? '');

            if ($user->totp_enabled) {
                // 2FA activé — vérifier le code
                if (!$totpCode) {
                    return $this->error($response, 'TOTP_REQUIRED',
                        'Code Google Authenticator requis pour le super_admin', 401);
                }
                if (!$totp->verifierCode($user->totp_secret, $totpCode)) {
                    return $this->error($response, 'TOTP_INVALID', 'Code 2FA invalide', 401);
                }
            } else {
                // 2FA pas encore configuré — forcer la configuration
                return $this->error($response, 'TOTP_SETUP_REQUIRED',
                    '2FA obligatoire pour le super_admin — configurez Google Authenticator via GET /api/auth/2fa/setup', 403);
            }
        }

        // Mettre à jour dernière connexion
        User::where('id', $user->id)->update(['derniere_connexion' => date('Y-m-d H:i:s')]);

        $token        = $this->jwt->generateAccessToken($user->id, $user->role, $user->atelier_id);
        $refreshToken = $this->jwt->generateRefreshToken($user->id, $this->getIp($request), $request->getHeaderLine('User-Agent'));

        return $this->success($response, [
            'token'        => $token,
            'refresh_token'=> $refreshToken,
            'user'         => [
                'id'        => $user->id,
                'email'     => $user->email,
                'prenom'    => $user->prenom,
                'nom'       => $user->nom,
                'role'      => $user->role,
                'atelier_id'=> $user->atelier_id,
            ],
        ]);
    }

    // POST /api/auth/logout
    public function logout(Request $request, Response $response): Response
    {
        $body = (array)$request->getParsedBody();
        $refreshToken = $body['refresh_token'] ?? '';

        if ($refreshToken) {
            $this->jwt->revokeRefreshToken($refreshToken);
        }

        return $this->success($response, ['message' => 'Déconnecté']);
    }

    // POST /api/auth/refresh
    public function refresh(Request $request, Response $response): Response
    {
        $body         = (array)$request->getParsedBody();
        $refreshToken = $body['refresh_token'] ?? '';

        if (!$refreshToken) {
            return $this->error($response, 'VALIDATION_ERROR', 'refresh_token requis', 400);
        }

        $tokens = $this->jwt->refreshAccessToken($refreshToken);

        if (!$tokens) {
            return $this->error($response, 'INVALID_REFRESH_TOKEN', 'Token invalide ou expiré', 401);
        }

        return $this->success($response, $tokens);
    }

    // GET /api/auth/me
    public function me(Request $request, Response $response): Response
    {
        $user = $this->getAuthUser($request);
        if (!$user) {
            return $this->error($response, 'UNAUTHORIZED', 'Non authentifié', 401);
        }

        return $this->success($response, [
            'id'                => $user->id,
            'email'             => $user->email,
            'prenom'            => $user->prenom,
            'nom'               => $user->nom,
            'role'              => $user->role,
            'atelier_id'        => $user->atelier_id,
            'strava_connected'  => (bool)$user->strava_connected,
            'presta_customer_id'=> $user->presta_customer_id,
            'date_inscription'  => $user->date_inscription,
        ]);
    }

    // POST /api/auth/forgot-password
    public function forgotPassword(Request $request, Response $response): Response
    {
        $body  = (array)$request->getParsedBody();
        $email = strtolower(trim($body['email'] ?? ''));

        // Réponse identique même si email inconnu (sécurité)
        $standardResponse = ['message' => 'Si ce compte existe, un email a été envoyé'];

        if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
            return $this->success($response, $standardResponse);
        }

        $user = User::where('email', $email)->first();
        if (!$user) {
            return $this->success($response, $standardResponse);
        }

        $token   = Uuid::uuid4()->toString();
        $expires = date('Y-m-d H:i:s', time() + 3600);

        User::where('id', $user->id)->update([
            'reset_token'   => $token,
            'reset_expires' => $expires,
        ]);

        $lien = 'https://chrono.mykollector.com/reset-password.html?token=' . $token;
        $this->mail->resetMotDePasse($email, $user->prenom ?? 'Sportif', $lien);

        return $this->success($response, $standardResponse);
    }

    // POST /api/auth/reset-password
    public function resetPassword(Request $request, Response $response): Response
    {
        $body     = (array)$request->getParsedBody();
        $token    = $body['token'] ?? '';
        $password = $body['password'] ?? '';

        if (!$token) {
            return $this->error($response, 'INVALID_TOKEN', 'Token invalide', 400);
        }

        if (strlen($password) < 8) {
            return $this->error($response, 'PASSWORD_TOO_SHORT', 'Le mot de passe doit faire au moins 8 caractères', 422);
        }

        $user = User::where('reset_token', $token)
            ->where('reset_expires', '>', date('Y-m-d H:i:s'))
            ->first();

        if (!$user) {
            return $this->error($response, 'INVALID_TOKEN', 'Token invalide ou expiré', 400);
        }

        User::where('id', $user->id)->update([
            'password'      => password_hash($password, PASSWORD_BCRYPT),
            'reset_token'   => null,
            'reset_expires' => null,
        ]);

        return $this->success($response, ['message' => 'Mot de passe mis à jour']);
    }

    private function getIp(Request $request): string
    {
        $params = $request->getServerParams();
        return $params['HTTP_X_FORWARDED_FOR'] ?? $params['REMOTE_ADDR'] ?? '0.0.0.0';
    }

    // GET /api/auth/2fa/setup — Initier la configuration 2FA
    public function totpSetup(Request $request, Response $response): Response
    {
        $user = $this->getAuthUser($request);
        if (!$user) return $this->error($response, 'UNAUTHORIZED', 'Non authentifié', 401);

        if ($user->role !== 'super_admin') {
            return $this->error($response, 'FORBIDDEN', '2FA uniquement pour super_admin', 403);
        }

        $totp   = new \App\Services\TotpService();
        $result = $totp->activer2FA($user->id, $user->email);

        return $this->success($response, $result);
    }

    // POST /api/auth/2fa/confirmer — Confirmer le setup avec un code valide
    public function totpConfirmer(Request $request, Response $response): Response
    {
        $user = $this->getAuthUser($request);
        if (!$user) return $this->error($response, 'UNAUTHORIZED', 'Non authentifié', 401);

        $body = (array)$request->getParsedBody();
        $code = trim($body['code'] ?? '');

        if (!$code) return $this->error($response, 'VALIDATION_ERROR', 'Code requis', 400);

        $totp = new \App\Services\TotpService();
        $ok   = $totp->confirmer2FA($user->id, $code);

        if (!$ok) return $this->error($response, 'TOTP_INVALID', 'Code invalide — réessayez', 401);

        $this->logAudit($user->id, 'totp_active', 'user', $user->id, [], $request);

        return $this->success($response, ['message' => '2FA activé avec succès sur votre compte super_admin.']);
    }

    // DELETE /api/auth/2fa — Désactiver 2FA (super_admin uniquement)
    public function totpDesactiver(Request $request, Response $response): Response
    {
        $user = $this->getAuthUser($request);
        if (!$user) return $this->error($response, 'UNAUTHORIZED', 'Non authentifié', 401);

        if ($user->role !== 'super_admin') {
            return $this->error($response, 'FORBIDDEN', 'Accès refusé', 403);
        }

        $totp = new \App\Services\TotpService();
        $totp->desactiver2FA($user->id);

        $this->logAudit($user->id, 'totp_desactive', 'user', $user->id, [], $request);

        return $this->success($response, ['message' => '2FA désactivé.']);
    }
}
