<?php

namespace App\Http\Controllers;

use App\Services\AuditService;
use App\Services\TotpService;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Validation\ValidationException;
use Illuminate\View\View;

class SecuritySettingsController extends Controller
{
    public function __construct(private readonly TotpService $totp) {}

    public function show(Request $request): View
    {
        $user = $request->user();
        $attempts = $user->loginAttempts()->latest('attempted_at')->limit(20)->get();

        return view('security.show', compact('user', 'attempts'));
    }

    public function enable(Request $request): RedirectResponse
    {
        $request->validate(['password' => ['required', 'current_password']]);
        $user = $request->user();

        if ($user->two_factor_confirmed_at) {
            return back()->withErrors(['two_factor' => 'Two-factor authentication is already enabled.']);
        }

        $secret = $this->totp->generateSecret();
        $recovery = $this->totp->generateRecoveryCodes();
        $user->forceFill([
            'two_factor_secret' => $secret,
            'two_factor_recovery_codes' => $recovery['hashes'],
            'two_factor_confirmed_at' => null,
        ])->save();

        AuditService::record('security.two_factor_setup_started', $user);

        return back()
            ->with('success', 'Two-factor setup started. Add the secret to your authenticator, then confirm a code.')
            ->with('two_factor_setup_secret', $secret)
            ->with('two_factor_setup_uri', $this->totp->provisioningUri($user, $secret))
            ->with('two_factor_recovery_codes', $recovery['plain']);
    }

    public function confirm(Request $request): RedirectResponse
    {
        $data = $request->validate(['code' => ['required', 'string', 'max:16']]);
        $user = $request->user();

        if (! $user->two_factor_secret || $user->two_factor_confirmed_at) {
            throw ValidationException::withMessages(['code' => 'Start two-factor setup before confirming it.']);
        }

        if (! $this->totp->verify((string) $user->two_factor_secret, $data['code'])) {
            throw ValidationException::withMessages(['code' => 'The authenticator code is invalid.']);
        }

        $user->forceFill(['two_factor_confirmed_at' => now()])->save();
        AuditService::record('security.two_factor_enabled', $user);

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

    public function disable(Request $request): RedirectResponse
    {
        $request->validate(['password' => ['required', 'current_password']]);
        $user = $request->user();
        $wasEnabled = (bool) $user->two_factor_confirmed_at;

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

        if ($wasEnabled) {
            AuditService::record('security.two_factor_disabled', $user);
        }

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

    public function recoveryCodes(Request $request): RedirectResponse
    {
        $request->validate(['password' => ['required', 'current_password']]);
        $user = $request->user();

        if (! $user->two_factor_confirmed_at) {
            return back()->withErrors(['two_factor' => 'Enable two-factor authentication before generating recovery codes.']);
        }

        $recovery = $this->totp->generateRecoveryCodes();
        $user->forceFill(['two_factor_recovery_codes' => $recovery['hashes']])->save();
        AuditService::record('security.two_factor_recovery_codes_regenerated', $user);

        return back()
            ->with('success', 'New recovery codes generated. Previous recovery codes are no longer valid.')
            ->with('two_factor_recovery_codes', $recovery['plain']);
    }
}
