<?php

namespace App\Http\Controllers\Web;

use App\Http\Controllers\Controller;
use App\Models\User;
use App\Support\TwoFactor;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Crypt;

class TwoFactorController extends Controller
{
    public function __construct()
    {
        $this->middleware('guest')->only([
            'setup',
            'setupConfirm',
            'challenge',
            'challengeConfirm',
        ]);
    }

    public function setup(Request $request)
    {
        $user = $this->resolvePendingUser($request);
        if (! $user) {
            return redirect()->route('login');
        }

        $pending = (string) ($user->two_factor_pending_secret ?? '');
        if ($pending === '') {
            return redirect()->route('login');
        }

        $secret = Crypt::decryptString($pending);
        $issuer = (string) (parse_url(config('app.url'), PHP_URL_HOST) ?: config('app.name', 'App'));
        $otpauth = TwoFactor::buildOtpAuthUrl($issuer, (string) $user->username, $secret);

        return view('auth.two_factor_setup', [
            'username' => (string) $user->username,
            'secret' => $secret,
            'otpauth' => $otpauth,
        ]);
    }

    public function setupConfirm(Request $request)
    {
        $user = $this->resolvePendingUser($request);
        if (! $user) {
            return redirect()->route('login');
        }

        $data = $request->validate([
            'code' => ['required', 'string', 'max:10'],
        ]);

        $pending = (string) ($user->two_factor_pending_secret ?? '');
        if ($pending === '') {
            return redirect()->route('login');
        }

        $secret = Crypt::decryptString($pending);
        $code = preg_replace('/\s+/', '', $data['code']) ?? '';

        if (! TwoFactor::verifyTotpCode($secret, $code)) {
            return back()->withErrors(['code' => __('common.invalid')]);
        }

        $user->forceFill([
            'two_factor_enabled' => true,
            'two_factor_secret' => Crypt::encryptString($secret),
            'two_factor_pending_secret' => null,
        ])->save();

        $this->clearTwoFactorSession($request);
        Auth::login($user);

        return redirect()->to('/platform/dashboard');
    }

    public function challenge(Request $request)
    {
        $user = $this->resolvePendingUser($request);
        if (! $user) {
            return redirect()->route('login');
        }

        $secretEncrypted = (string) ($user->two_factor_secret ?? '');
        if (! $user->two_factor_enabled || $secretEncrypted === '') {
            $this->clearTwoFactorSession($request);

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

        return view('auth.two_factor_challenge', [
            'username' => (string) $user->username,
        ]);
    }

    public function challengeConfirm(Request $request)
    {
        $user = $this->resolvePendingUser($request);
        if (! $user) {
            return redirect()->route('login');
        }

        $data = $request->validate([
            'code' => ['required', 'string', 'max:10'],
        ]);

        $secretEncrypted = (string) ($user->two_factor_secret ?? '');
        if (! $user->two_factor_enabled || $secretEncrypted === '') {
            $this->clearTwoFactorSession($request);

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

        $secret = Crypt::decryptString($secretEncrypted);
        $code = preg_replace('/\s+/', '', $data['code']) ?? '';

        if (! TwoFactor::verifyTotpCode($secret, $code)) {
            return back()->withErrors(['code' => __('common.invalid')]);
        }

        $this->clearTwoFactorSession($request);
        Auth::login($user);

        return redirect()->to('/platform/dashboard');
    }

    private function resolvePendingUser(Request $request): ?User
    {
        $userId = (int) ($request->session()->get('two_factor_user_id') ?? 0);
        if ($userId <= 0) {
            return null;
        }

        return User::query()->find($userId);
    }

    private function clearTwoFactorSession(Request $request): void
    {
        $request->session()->forget(['two_factor_user_id']);
    }
}
