<?php

namespace App\Services;

use App\Models\User;
use App\Models\PasswordResetLog;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Password;
use Illuminate\Auth\Events\PasswordReset;
use App\Mail\PasswordResetMail;
use App\Mail\PasswordResetConfirmationMail;

class PasswordResetService
{
    /**
     * Token expiration in minutes.
     */
    const TOKEN_EXPIRATION = 60;

    /**
     * Send password reset link.
     *
     * @param string $email
     * @param string $ipAddress
     * @param string|null $userAgent
     * @return array{success: bool, message: string}
     */
    public function sendResetLink(string $email, string $ipAddress, ?string $userAgent = null): array
    {
        $user = User::where('email', $email)->first();

        // Always log the attempt (even if user doesn't exist)
        $log = PasswordResetLog::create([
            'user_id' => $user?->id,
            'email' => $email,
            'ip_address' => $ipAddress,
            'user_agent' => $userAgent,
            'requested_at' => now(),
        ]);

        if (!$user) {
            // Return success anyway to prevent email enumeration
            return [
                'success' => true,
                'message' => 'If the email exists in our system, you will receive a reset link.',
            ];
        }

        // Generate token
        $token = Password::createToken($user);

        // Store token hash in log
        $log->update(['token_hash' => Hash::make($token)]);

        // Send email
        $resetUrl = route('password.reset', ['token' => $token, 'email' => $email]);
        Mail::to($user->email)->send(new PasswordResetMail($user, $resetUrl));

        return [
            'success' => true,
            'message' => 'If the email exists in our system, you will receive a reset link.',
        ];
    }

    /**
     * Validate reset token.
     *
     * @param string $token
     * @param string $email
     * @return bool
     */
    public function validateToken(string $token, string $email): bool
    {
        $user = User::where('email', $email)->first();

        if (!$user) {
            return false;
        }

        // Check if token is valid
        $credentials = [
            'email' => $email,
            'token' => $token,
        ];

        $user = Password::getUser($credentials);

        return $user && Password::tokenExists($user, $token);
    }

    /**
     * Reset password.
     *
     * @param string $email
     * @param string $token
     * @param string $password
     * @param string $ipAddress
     * @return array{success: bool, message: string}
     */
    public function reset(string $email, string $token, string $password, string $ipAddress): array
    {
        $user = User::where('email', $email)->first();

        if (!$user) {
            return [
                'success' => false,
                'message' => 'Invalid user.',
            ];
        }

        // Verify token
        if (!Password::tokenExists($user, $token)) {
            return [
                'success' => false,
                'message' => 'Invalid or expired token.',
            ];
        }

        // Reset password
        $user->password = Hash::make($password);
        $user->save();

        // Delete the token
        Password::deleteToken($user);

        // Mark log as completed
        PasswordResetLog::where('email', $email)
            ->whereNull('completed_at')
            ->latest()
            ->first()
            ?->markCompleted();

        // Send confirmation email
        Mail::to($user->email)->send(new PasswordResetConfirmationMail($user));

        // Fire event
        event(new PasswordReset($user));

        return [
            'success' => true,
            'message' => 'Password has been reset successfully.',
        ];
    }

    /**
     * Delete all tokens for an email.
     *
     * @param string $email
     * @return void
     */
    public function deleteToken(string $email): void
    {
        $user = User::where('email', $email)->first();

        if ($user) {
            Password::deleteToken($user);
        }
    }

    /**
     * Check rate limit for password reset requests.
     *
     * @param string $email
     * @param string $ipAddress
     * @return bool
     */
    public function isRateLimited(string $email, string $ipAddress): bool
    {
        // Check by email (3 attempts per hour)
        $emailAttempts = PasswordResetLog::forEmail($email)
            ->recent(60)
            ->count();

        if ($emailAttempts >= 3) {
            return true;
        }

        // Check by IP (5 attempts per hour)
        $ipAttempts = PasswordResetLog::byIp($ipAddress)
            ->recent(60)
            ->count();

        if ($ipAttempts >= 5) {
            return true;
        }

        return false;
    }

    /**
     * Get remaining reset attempts for an email.
     *
     * @param string $email
     * @return int
     */
    public function remainingAttempts(string $email): int
    {
        $attempts = PasswordResetLog::forEmail($email)
            ->recent(60)
            ->count();

        return max(0, 3 - $attempts);
    }
}
