<?php

declare(strict_types=1);

namespace App\Application\Admin;

use App\Domain\Audit\AuditAction;
use App\Domain\Audit\AuditEmitter;
use App\Domain\Settings\AppSettings;
use Doctrine\DBAL\Connection;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;

/**
 * `GET/PATCH /api/v1/admin/app-settings` — runtime-mutable feature flags.
 *
 * Currently exposes the two audit-emission toggles (report.received and
 * blocklist.requested) which are surfaced as switches on the settings
 * page so an admin can silence the high-volume audit rows without
 * restarting the api container.
 *
 * RBAC: Admin only — same trust level as the rest of the settings page.
 */
final class AppSettingsController
{
    use AdminControllerSupport;

    private const KEYS = [
        AppSettings::KEY_AUDIT_REPORT_RECEIVED_ENABLED,
        AppSettings::KEY_AUDIT_BLOCKLIST_REQUEST_ENABLED,
    ];

    public function __construct(
        private readonly AppSettings $settings,
        private readonly AuditEmitter $audit,
        private readonly Connection $connection,
    ) {
    }

    public function show(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface
    {
        return self::json($response, 200, $this->snapshot());
    }

    public function update(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface
    {
        $body = self::jsonBody($request);
        $errors = [];
        $changes = [];

        foreach (self::KEYS as $key) {
            if (!array_key_exists($key, $body)) {
                continue;
            }
            $raw = $body[$key];
            if (!is_bool($raw) && !in_array($raw, [0, 1, '0', '1', 'true', 'false'], true)) {
                $errors[$key] = 'must be a boolean';
                continue;
            }
            $value = is_bool($raw) ? $raw : in_array($raw, [1, '1', 'true'], true);
            $current = $this->settings->getBool($key, true);
            if ($current !== $value) {
                $changes[$key] = ['from' => $current, 'to' => $value];
            }
        }

        if ($errors !== []) {
            return self::validationFailed($response, $errors);
        }

        if ($changes !== []) {
            $auditCtx = self::auditContext($request);
            $this->connection->transactional(function () use ($changes, $auditCtx): void {
                foreach ($changes as $key => $diff) {
                    $this->settings->setBool($key, (bool) $diff['to']);
                }
                $this->audit->emitOrThrow(
                    AuditAction::APP_SETTINGS_UPDATED,
                    'app_settings',
                    null,
                    ['changes' => $changes],
                    $auditCtx,
                    'audit-toggles',
                );
            });
        }

        return self::json($response, 200, $this->snapshot());
    }

    /**
     * @return array<string, bool>
     */
    private function snapshot(): array
    {
        return [
            AppSettings::KEY_AUDIT_REPORT_RECEIVED_ENABLED => $this->settings->getBool(
                AppSettings::KEY_AUDIT_REPORT_RECEIVED_ENABLED,
                true,
            ),
            AppSettings::KEY_AUDIT_BLOCKLIST_REQUEST_ENABLED => $this->settings->getBool(
                AppSettings::KEY_AUDIT_BLOCKLIST_REQUEST_ENABLED,
                true,
            ),
        ];
    }
}
