<?php

namespace App\Services;

use App\Models\User;
use App\Models\LoginAttempt;
use App\Models\UserSession;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Http\Request;

class AuthenticationService
{
    protected OtpService $otpService;

    public function __construct(OtpService $otpService)
    {
        $this->otpService = $otpService;
    }

    /**
     * Authenticate a user with credentials.
     *
     * @param array $credentials
     * @param bool $remember
     * @return User|null
     */
    public function authenticate(array $credentials, bool $remember = false): ?User
    {
        $login = $credentials['login'] ?? $credentials['email'] ?? null;
        $password = $credentials['password'];

        // Determine if login is email or username
        $field = filter_var($login, FILTER_VALIDATE_EMAIL) ? 'email' : 'username';

        $user = User::where($field, $login)->first();

        if (!$user || !Hash::check($password, $user->password)) {
            return null;
        }

        if (!$user->is_active) {
            return null;
        }

        // Log in the user
        Auth::login($user, $remember);

        return $user;
    }

    /**
     * Check if user requires OTP verification.
     *
     * @param User $user
     * @return bool
     */
    public function requiresOtp(User $user): bool
    {
        return $this->otpService->isEnabled();
    }

    /**
     * Check if user requires 2FA verification.
     *
     * @param User $user
     * @return bool
     */
    public function requires2FA(User $user): bool
    {
        return $user->requires2FA();
    }

    /**
     * Complete the authentication process.
     *
     * @param User $user
     * @param Request $request
     * @return void
     */
    public function completeAuthentication(User $user, Request $request): void
    {
        // Create session record
        $this->createSessionRecord($user, $request);

        // Record successful login attempt
        $this->recordLoginAttempt($user, $request, true);
    }

    /**
     * Record a login attempt.
     *
     * @param User|null $user
     * @param Request $request
     * @param bool $successful
     * @param string|null $failureReason
     * @return LoginAttempt
     */
    public function recordLoginAttempt(?User $user, Request $request, bool $successful, ?string $failureReason = null): LoginAttempt
    {
        return LoginAttempt::create([
            'user_id' => $user?->id,
            'ip_address' => $request->ip(),
            'user_agent' => $request->userAgent(),
            'email_attempted' => $request->input('login') ?? $request->input('email'),
            'successful' => $successful,
            'failure_reason' => $failureReason,
            'attempted_at' => now(),
        ]);
    }

    /**
     * Create a session record for the user.
     *
     * @param User $user
     * @param Request $request
     * @return UserSession
     */
    protected function createSessionRecord(User $user, Request $request): UserSession
    {
        // Mark all other sessions as not current
        UserSession::where('user_id', $user->id)
            ->where('is_current', true)
            ->update(['is_current' => false]);

        return UserSession::create([
            'user_id' => $user->id,
            'session_id' => $request->session()->getId(),
            'ip_address' => $request->ip(),
            'user_agent' => $request->userAgent(),
            'device_type' => UserSession::detectDeviceType($request->userAgent()),
            'browser' => UserSession::detectBrowser($request->userAgent()),
            'platform' => UserSession::detectPlatform($request->userAgent()),
            'is_current' => true,
            'last_activity' => now(),
            'created_at' => now(),
        ]);
    }

    /**
     * Get rate limit key for login attempts.
     *
     * @param Request $request
     * @return string
     */
    public function getRateLimitKey(Request $request): string
    {
        return 'login:' . $request->ip();
    }

    /**
     * Check if rate limit is exceeded.
     *
     * @param string $key
     * @param int $maxAttempts
     * @return bool
     */
    public function isRateLimited(string $key, int $maxAttempts = 5): bool
    {
        return RateLimiter::tooManyAttempts($key, $maxAttempts);
    }

    /**
     * Get remaining attempts.
     *
     * @param string $key
     * @param int $maxAttempts
     * @return int
     */
    public function remainingAttempts(string $key, int $maxAttempts = 5): int
    {
        return RateLimiter::remaining($key, $maxAttempts);
    }

    /**
     * Get seconds until rate limit is cleared.
     *
     * @param string $key
     * @return int
     */
    public function retryAfter(string $key): int
    {
        return RateLimiter::availableIn($key);
    }

    /**
     * Increment rate limiter.
     *
     * @param string $key
     * @return void
     */
    public function hitRateLimit(string $key): void
    {
        RateLimiter::hit($key, 60); // 1 minute decay
    }

    /**
     * Clear rate limiter.
     *
     * @param string $key
     * @return void
     */
    public function clearRateLimit(string $key): void
    {
        RateLimiter::clear($key);
    }

    /**
     * Check if login is from a new device.
     *
     * @param User $user
     * @param Request $request
     * @return bool
     */
    public function isNewDevice(User $user, Request $request): bool
    {
        return !UserSession::where('user_id', $user->id)
            ->where('user_agent', $request->userAgent())
            ->exists();
    }

    /**
     * Logout the user and clean up session.
     *
     * @param Request $request
     * @return void
     */
    public function logout(Request $request): void
    {
        $sessionId = $request->session()->getId();

        // Delete session record
        UserSession::where('session_id', $sessionId)->delete();

        // Logout from Auth
        Auth::logout();

        // Invalidate session
        $request->session()->invalidate();
        $request->session()->regenerateToken();
    }
}
