<?php

namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use App\Models\ActivityLog;
use App\Models\User;
use App\Support\Totp;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;

class TwoFactorController extends Controller
{
    /**
     * Render the setup screen: provision a secret if none exists yet, then
     * display the QR + manual entry code so the user can confirm.
     */
    public function setup(Request $request)
    {
        $user = $request->user();

        if (! $user->two_factor_secret) {
            $user->forceFill(['two_factor_secret' => Totp::generateSecret()])->save();
        }

        $issuer = 'Epelcon Hospitality';
        $otpauth = Totp::otpauthUrl($user->two_factor_secret, $user->email, $issuer);

        return view('auth.twofactor.setup', [
            'user' => $user,
            'secret' => $user->two_factor_secret,
            'otpauth' => $otpauth,
            'confirmed' => $user->hasTwoFactor(),
        ]);
    }

    /**
     * Confirm the setup by verifying a code, then generate + persist recovery codes.
     */
    public function confirm(Request $request)
    {
        $data = $request->validate([
            'code' => ['required', 'digits:6'],
        ]);

        $user = $request->user();

        if (! $user->two_factor_secret || ! Totp::verify($user->two_factor_secret, $data['code'])) {
            return back()->withErrors(['code' => 'Code did not match. Try again.']);
        }

        $plainCodes = $this->makeRecoveryCodes();
        $user->forceFill([
            'two_factor_confirmed_at' => now(),
            'two_factor_recovery_codes' => json_encode(array_map(fn ($c) => Hash::make($c), $plainCodes)),
        ])->save();

        if (class_exists(ActivityLog::class)) {
            ActivityLog::record('enabled two-factor authentication', 'security');
        }

        return redirect()->route('2fa.recovery')
            ->with('recovery_codes', $plainCodes)
            ->with('success', 'Two-factor authentication is now active. Save your recovery codes.');
    }

    /**
     * Turn 2FA off after re-confirming the current password.
     */
    public function disable(Request $request)
    {
        $request->validate([
            'password' => ['required', 'current_password'],
        ]);

        $user = $request->user();
        $user->forceFill([
            'two_factor_secret' => null,
            'two_factor_confirmed_at' => null,
            'two_factor_recovery_codes' => null,
        ])->save();

        if (class_exists(ActivityLog::class)) {
            ActivityLog::record('disabled two-factor authentication', 'security');
        }

        return back()->with('success', 'Two-factor authentication disabled.');
    }

    /**
     * Prompt the pending user for their 6-digit code (or a recovery code).
     */
    public function challenge(Request $request)
    {
        if (! $request->session()->has('two_factor_pending_user_id')) {
            return redirect()->route('login');
        }

        return view('auth.twofactor.challenge');
    }

    /**
     * Validate the code, log the user in, and route them to their home area.
     */
    public function verify(Request $request)
    {
        $data = $request->validate([
            'code' => ['required', 'string', 'min:6', 'max:11'],
        ]);

        $userId = $request->session()->get('two_factor_pending_user_id');
        if (! $userId) {
            return redirect()->route('login');
        }

        $user = User::find($userId);
        if (! $user) {
            $request->session()->forget(['two_factor_pending_user_id', 'two_factor_pending_remember']);

            return redirect()->route('login');
        }

        $code = trim($data['code']);
        $remember = (bool) $request->session()->get('two_factor_pending_remember', false);
        $accepted = false;

        if (preg_match('/^\d{6}$/', $code)) {
            $accepted = Totp::verify((string) $user->two_factor_secret, $code);
        } else {
            $stored = json_decode((string) $user->two_factor_recovery_codes, true) ?: [];
            foreach ($stored as $index => $hash) {
                if (Hash::check($code, $hash)) {
                    unset($stored[$index]);
                    $user->forceFill([
                        'two_factor_recovery_codes' => json_encode(array_values($stored)),
                    ])->save();
                    $accepted = true;
                    break;
                }
            }
        }

        if (! $accepted) {
            return back()->withErrors(['code' => 'That code did not match. Try again.']);
        }

        Auth::login($user, $remember);
        $request->session()->forget(['two_factor_pending_user_id', 'two_factor_pending_remember']);
        $request->session()->put('2fa_passed', true);
        $user->forceFill(['last_login_at' => now()])->save();

        if (class_exists(ActivityLog::class)) {
            ActivityLog::record('logged in (2FA)', 'security');
        }

        return redirect()->intended($this->redirectPathFor($user));
    }

    /**
     * Show recovery codes exactly once (immediately after generation / regeneration).
     */
    public function recoveryCodes(Request $request)
    {
        $codes = $request->session()->get('recovery_codes');

        return view('auth.twofactor.recovery', [
            'codes' => $codes,
        ]);
    }

    /**
     * Rotate recovery codes after a password confirmation.
     */
    public function regenerateCodes(Request $request)
    {
        $request->validate([
            'password' => ['required', 'current_password'],
        ]);

        $user = $request->user();

        if (! $user->hasTwoFactor()) {
            return back()->withErrors(['password' => 'Enable two-factor authentication first.']);
        }

        $plainCodes = $this->makeRecoveryCodes();
        $user->forceFill([
            'two_factor_recovery_codes' => json_encode(array_map(fn ($c) => Hash::make($c), $plainCodes)),
        ])->save();

        if (class_exists(ActivityLog::class)) {
            ActivityLog::record('regenerated 2FA recovery codes', 'security');
        }

        return redirect()->route('2fa.recovery')
            ->with('recovery_codes', $plainCodes)
            ->with('success', 'New recovery codes generated. Save them now.');
    }

    /**
     * @return array<int,string>
     */
    private function makeRecoveryCodes(): array
    {
        $codes = [];
        for ($i = 0; $i < 8; $i++) {
            $codes[] = strtolower(Str::random(10));
        }

        return $codes;
    }

    /**
     * Mirror of LoginController::redirectTo so post-2FA login lands on the right screen.
     */
    private function redirectPathFor(User $user): string
    {
        if ($user->isSuperAdmin()) {
            return route('platform.dashboard');
        }

        return $user->isStaff() ? route('epelcon.dashboard') : route('account.dashboard');
    }
}
