<?php

namespace Modules\Vrc\Http\Controllers;

use Illuminate\Http\Request;
use Modules\Vrc\Entities\VrcUserSecurity;
use Modules\Vrc\Services\Security\VrcTotp;

/**
 * Phase 1 — self-service step-up two-factor auth for the VRC area (§9.2). TOTP is
 * stored per user in vrc_user_security; enforcement is the VrcTwoFactor middleware.
 * Module-only: the core users table and login flow are never touched.
 */
class TwoFactorController extends VrcBaseController
{
    public function show(Request $request)
    {
        $sec = $this->record();
        $pendingSecret = null;
        $uri = null;

        if (! $sec || ! $sec->is_enabled) {
            $pendingSecret = $request->session()->get('vrc_2fa_pending_secret');
            if (! $pendingSecret) {
                $pendingSecret = VrcTotp::generateSecret();
                $request->session()->put('vrc_2fa_pending_secret', $pendingSecret);
            }
            $label = (string) ($request->session()->get('user.username') ?: ('user' . auth()->id()));
            $uri = VrcTotp::provisioningUri($pendingSecret, $label, config('app.name', 'VRC'));
        }

        return view('vrc::security.twofactor', [
            'enabled'       => (bool) ($sec->is_enabled ?? false),
            'pendingSecret' => $pendingSecret,
            'uri'           => $uri,
        ]);
    }

    public function enable(Request $request)
    {
        $this->validate($request, ['code' => 'required|string']);
        $secret = $request->session()->get('vrc_2fa_pending_secret');

        if (! $secret || ! VrcTotp::verify($secret, $request->input('code'))) {
            return back()->with('vrc_2fa_error', __('messages.something_went_wrong') . ' — invalid code.');
        }

        $codes = collect(range(1, 6))->map(fn () => strtoupper(bin2hex(random_bytes(4))))->all();

        VrcUserSecurity::updateOrCreate(
            ['business_id' => $this->businessId(), 'user_id' => auth()->id()],
            ['totp_secret' => $secret, 'is_enabled' => true, 'confirmed_at' => now(), 'recovery_codes' => $codes]
        );

        $request->session()->forget('vrc_2fa_pending_secret');
        $request->session()->put('vrc_2fa_verified', true);

        return redirect()->route('vrc.security.2fa')->with('vrc_2fa_recovery', $codes);
    }

    public function disable(Request $request)
    {
        $this->validate($request, ['code' => 'required|string']);
        $sec = $this->record();

        if (! $sec || ! $sec->is_enabled || ! VrcTotp::verify($sec->totp_secret, $request->input('code'))) {
            return back()->with('vrc_2fa_error', 'Invalid code.');
        }

        $sec->update(['is_enabled' => false, 'totp_secret' => null, 'recovery_codes' => null]);
        $request->session()->forget('vrc_2fa_verified');

        return redirect()->route('vrc.security.2fa')->with('vrc_2fa_ok', '2FA disabled.');
    }

    public function challenge()
    {
        return view('vrc::security.challenge');
    }

    public function verify(Request $request)
    {
        $this->validate($request, ['code' => 'required|string']);
        $sec = $this->record();

        if (! $sec || ! $sec->is_enabled) {
            $request->session()->put('vrc_2fa_verified', true);
            return redirect()->route('vrc.dashboard');
        }

        $ok = VrcTotp::verify($sec->totp_secret, $request->input('code'))
            || $this->consumeRecovery($sec, $request->input('code'));

        if (! $ok) {
            return back()->with('vrc_2fa_error', 'Invalid code.');
        }

        $request->session()->put('vrc_2fa_verified', true);
        return redirect()->intended(route('vrc.dashboard'));
    }

    private function record(): ?VrcUserSecurity
    {
        return VrcUserSecurity::where('business_id', $this->businessId())
            ->where('user_id', auth()->id())->first();
    }

    private function consumeRecovery(VrcUserSecurity $sec, string $code): bool
    {
        $codes = (array) $sec->recovery_codes;
        $code = strtoupper(trim($code));
        if (! in_array($code, $codes, true)) {
            return false;
        }
        $sec->update(['recovery_codes' => array_values(array_diff($codes, [$code]))]);
        return true;
    }
}
