<?php

namespace App\Services;

use App\Jobs\SendEmailJob;
use App\Mail\VerifyEmailMail;
use App\Mail\WelcomeMail;
use App\Models\User;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

class EmailVerificationService
{
    private const CODE_EXPIRY_MINUTES = 15;
    private const MAX_ATTEMPTS = 5;
    private const RESEND_COOLDOWN_SECONDS = 60;
    private const LINK_EXPIRY_HOURS = 24;

    /**
     * Send a verification code to the user's email.
     */
    public function sendVerificationCode(User $user): void
    {
        // Delete any existing codes for this user
        DB::table('email_verification_codes')
            ->where('user_id', $user->id)
            ->delete();

        $code = str_pad((string) random_int(0, 999999), 6, '0', STR_PAD_LEFT);

        DB::table('email_verification_codes')->insert([
            'user_id' => $user->id,
            'email' => $user->email,
            'code' => $code,
            'expires_at' => now()->addMinutes(self::CODE_EXPIRY_MINUTES),
            'attempts' => 0,
            'created_at' => now(),
        ]);

        // Get user's association for branding
        $association = $user->associations()->first();

        $mail = (new VerifyEmailMail($user, $code, self::CODE_EXPIRY_MINUTES))
            ->withAssociation($association);

        SendEmailJob::dispatch($mail, $user->email);

        Log::info('Verification code sent', ['user_id' => $user->id, 'email' => $user->email, 'association' => $association?->name]);
    }

    /**
     * Verify the code submitted by the user.
     *
     * @return array{success: bool, message: string}
     */
    public function verifyCode(User $user, string $code): array
    {
        // Get the most recent code, regardless of verification status
        $record = DB::table('email_verification_codes')
            ->where('user_id', $user->id)
            ->where('email', $user->email)
            ->orderByDesc('created_at')
            ->first();

        if (!$record) {
            return ['success' => false, 'message' => 'No verification code found. Please request a new one.'];
        }

        // If already verified, check if user email is also verified
        if ($record->verified_at !== null) {
            if ($user->email_verified_at === null) {
                // Edge case: code was marked verified but user wasn't updated
                // Fix it now
                $user->update(['email_verified_at' => now()]);
                Log::warning('Fixed email_verified_at mismatch', ['user_id' => $user->id]);
                return ['success' => true, 'message' => 'Email verified successfully.'];
            }
            return ['success' => false, 'message' => 'This code has already been used. Your email is verified.'];
        }

        if (now()->isAfter($record->expires_at)) {
            return ['success' => false, 'message' => 'Verification code has expired. Please request a new one.'];
        }

        if ($record->attempts >= self::MAX_ATTEMPTS) {
            return ['success' => false, 'message' => 'Too many failed attempts. Please request a new code.'];
        }

        if ($record->code !== $code) {
            DB::table('email_verification_codes')
                ->where('id', $record->id)
                ->increment('attempts');

            $remaining = self::MAX_ATTEMPTS - $record->attempts - 1;

            return [
                'success' => false,
                'message' => "Invalid code. {$remaining} " . ($remaining === 1 ? 'attempt' : 'attempts') . ' remaining.',
            ];
        }

        // Code is valid — mark as verified
        DB::table('email_verification_codes')
            ->where('id', $record->id)
            ->update(['verified_at' => now()]);

        // Set email_verified_at on the user - CRITICAL: Use DB query to ensure it persists
        DB::table('users')
            ->where('id', $user->id)
            ->update(['email_verified_at' => now()]);

        // Refresh user model to get updated value
        $user->refresh();

        // Send welcome email
        $associationName = $user->associations()->first()?->name;
        SendEmailJob::dispatch(
            new WelcomeMail($user, $associationName),
            $user->email,
        );

        Log::info('Email verified', ['user_id' => $user->id, 'email' => $user->email, 'verified_at' => $user->email_verified_at]);

        return ['success' => true, 'message' => 'Email verified successfully.'];
    }

    /**
     * Check if the user can request a new code (60s cooldown).
     */
    public function canResend(User $user): bool
    {
        $lastCode = DB::table('email_verification_codes')
            ->where('user_id', $user->id)
            ->orderByDesc('created_at')
            ->first();

        if (!$lastCode) {
            return true;
        }

        // Convert string to Carbon for proper date math
        $createdAt = \Carbon\Carbon::parse($lastCode->created_at);
        $elapsed = now()->diffInSeconds($createdAt, false);

        return $elapsed >= self::RESEND_COOLDOWN_SECONDS;
    }

    /**
     * Get seconds remaining before user can resend.
     */
    public function resendCooldownSeconds(User $user): int
    {
        $lastCode = DB::table('email_verification_codes')
            ->where('user_id', $user->id)
            ->orderByDesc('created_at')
            ->first();

        if (!$lastCode) {
            return 0;
        }

        // Convert string to Carbon for proper date math
        $createdAt = \Carbon\Carbon::parse($lastCode->created_at);
        $elapsed = now()->diffInSeconds($createdAt, false);
        $remaining = self::RESEND_COOLDOWN_SECONDS - $elapsed;

        return max(0, (int) $remaining);
    }

    /**
     * Send verification link via email (preferred method)
     */
    public function sendVerificationLink(User $user): string
    {
        // Delete any existing tokens for this user
        DB::table('email_verification_tokens')
            ->where('user_id', $user->id)
            ->delete();

        // Generate secure token
        $token = \Illuminate\Support\Str::random(64);

        // Store token
        DB::table('email_verification_tokens')->insert([
            'user_id' => $user->id,
            'token' => $token,
            'expires_at' => now()->addHours(self::LINK_EXPIRY_HOURS),
            'created_at' => now(),
        ]);

        // Build verification URL
        $verificationUrl = route('verification.verify', ['token' => $token]);

        // Get user's association for branding
        $association = $user->associations()->first();

        // Send email (we'll create VerifyEmailLinkMail next)
        $mail = (new \App\Mail\VerifyEmailLinkMail($user, $verificationUrl))
            ->withAssociation($association);

        SendEmailJob::dispatch($mail, $user->email);

        Log::info('Verification link sent', [
            'user_id' => $user->id,
            'email' => $user->email,
            'association' => $association?->name
        ]);

        return $verificationUrl;
    }

    /**
     * Verify email via token link
     */
    public function verifyByLink(string $token): array
    {
        $record = DB::table('email_verification_tokens')
            ->where('token', $token)
            ->whereNull('verified_at')
            ->first();

        if (!$record) {
            return [
                'success' => false,
                'message' => 'Invalid verification link. The link may have already been used.'
            ];
        }

        if (now()->isAfter($record->expires_at)) {
            return [
                'success' => false,
                'message' => 'Verification link has expired. Please request a new one.'
            ];
        }

        // Mark token as verified
        DB::table('email_verification_tokens')
            ->where('id', $record->id)
            ->update(['verified_at' => now()]);

        // Update user email_verified_at
        DB::table('users')
            ->where('id', $record->user_id)
            ->update(['email_verified_at' => now()]);

        // Get user for response
        $user = User::find($record->user_id);

        // Send welcome email
        $association = $user->associations()->first();
        if ($association) {
            SendEmailJob::dispatch(
                (new WelcomeMail($user, $association->name))->withAssociation($association),
                $user->email
            );
        }

        Log::info('Email verified via link', [
            'user_id' => $user->id,
            'email' => $user->email
        ]);

        return [
            'success' => true,
            'message' => 'Email verified successfully!',
            'user_id' => $user->id,
            'user' => $user
        ];
    }
}
