<?php

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use App\Support\SecurityAuditLogger;
use App\Support\SecuritySettings;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\View\View;

class SecuritySettingsController extends Controller
{
    public function index(): View
    {
        $settings = SecuritySettings::get();

        return view('admin.security-settings.index', compact('settings'));
    }

    /**
     * Per-tab validation rules. Each Blade form on the security settings
     * page POSTs a hidden `_settings_group` matching one of these keys;
     * only the listed fields are validated and persisted, so saving the
     * Login tab no longer fails on missing Password / Session fields.
     *
     * The leading `*_minutes` aliases on the right side normalize legacy
     * blade field names (e.g. `session_lifetime`) to the canonical
     * SecuritySettings key (e.g. `session_lifetime_minutes`) without
     * touching the templates.
     */
    private const GROUP_FIELDS = [
        'login' => [
            'max_login_attempts'   => 'required|integer|min:1|max:20',
            'lockout_minutes'      => 'required|integer|min:1|max:60',
            'captcha_enabled'      => 'sometimes|boolean',
            'captcha_site_key'     => 'nullable|string|max:255',
            'captcha_secret_key'   => 'nullable|string|max:255',
        ],
        'password' => [
            'password_min_length'        => 'required|integer|min:6|max:30',
            'password_require_uppercase' => 'sometimes|boolean',
            'password_require_numbers'   => 'sometimes|boolean',
            'password_require_symbols'   => 'sometimes|boolean',
        ],
        'session' => [
            // Blade field is `session_lifetime` but the canonical setting
            // key is `session_lifetime_minutes`. We accept either via the
            // alias map below.
            'session_lifetime_minutes' => 'required|integer|min:5|max:43200',
            'single_session'           => 'sometimes|boolean',
        ],
        'twofactor' => [
            // `two_factor_enabled` here is the PLATFORM-LEVEL kill switch
            // (lets admin disable 2FA org-wide). Not to be confused with
            // the per-user `users.two_factor_enabled` column.
            'two_factor_enabled'    => 'sometimes|boolean',
            'two_factor_admin_only' => 'sometimes|boolean',
        ],
        'blocking' => [
            'ip_blocking_enabled'       => 'sometimes|boolean',
            'location_blocking_enabled' => 'sometimes|boolean',
        ],
    ];

    /**
     * Map of legacy / shorthand field names that arrive in $request to
     * the canonical SecuritySettings key they should be stored under.
     * Lets the Blade keep `name="session_lifetime"` while the storage
     * layer keeps the longer, more descriptive key.
     */
    private const FIELD_ALIASES = [
        'session_lifetime' => 'session_lifetime_minutes',
    ];

    public function update(Request $request): RedirectResponse
    {
        // Normalize aliased field names BEFORE validation so a Blade form
        // posting `session_lifetime` populates the canonical
        // `session_lifetime_minutes` rule.
        foreach (self::FIELD_ALIASES as $from => $to) {
            if ($request->has($from) && !$request->has($to)) {
                $request->merge([$to => $request->input($from)]);
            }
        }

        $group = (string) $request->input('_settings_group', '');
        if (!isset(self::GROUP_FIELDS[$group])) {
            // Backwards-compat: a legacy form without _settings_group
            // (or a malformed submission) falls back to validating
            // EVERY known field, but with all numeric requirements
            // relaxed to "sometimes" so we never fail on missing
            // cross-tab values.
            $rules = [];
            foreach (self::GROUP_FIELDS as $g) {
                foreach ($g as $field => $rule) {
                    $rules[$field] = str_replace('required', 'sometimes', $rule);
                }
            }
        } else {
            $rules = self::GROUP_FIELDS[$group];
        }

        $validated = $request->validate($rules);

        // Normalize booleans (unchecked checkboxes are absent from the
        // request, but we want to persist `false` so toggling OFF
        // actually sticks). We only touch booleans that appear in the
        // current group's ruleset.
        foreach (array_keys($rules) as $field) {
            if (str_contains((string) ($rules[$field] ?? ''), 'boolean')) {
                $validated[$field] = $request->boolean($field);
            }
        }

        if (empty($validated)) {
            return back()->with('error', 'Nothing to save — unknown settings group.');
        }

        SecuritySettings::update($validated);

        SecurityAuditLogger::settingsChanged(auth()->user(), $request, [
            'section' => 'security',
            'group'   => $group ?: 'legacy',
            'changes' => array_keys($validated),
        ]);

        return back()->with('success', 'Security settings updated successfully.');
    }
}
