<?php
/*
 * Copyright 2026 Alessandro Chiapparini <sprint_planer_web@chiapparini.org>
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * See the LICENSE file in the project root for the full license text.
 */

declare(strict_types=1);

namespace App\Controllers;

use App\Auth\SessionGuard;
use App\Http\Request;
use App\Http\Response;
use App\Http\View;
use App\Repositories\AppSettingsRepository;
use App\Repositories\UserRepository;
use App\Services\AuditLogger;
use PDO;
use Throwable;

/**
 * /settings — admin-only global configuration.
 *
 * Holds keys with app-wide effect. Currently:
 *   task_status_enabled    bool  — toggles Phase 18 per-cell status
 *                                  selectors + filter on /sprints/{id} +
 *                                  /sprints/{id}/present.
 *   assignment_slider_max  int   — upper bound for the per-cell popover
 *                                  slider (1..100). Default 10. The day
 *                                  input itself stays unbounded; only the
 *                                  slider is capped.
 *
 * Updates POST a form (CSRF via _csrf input, like /auth/logout) and audit
 * with entity_type='app_setting', entity_id=null. Pure admin surface; no
 * per-sprint scoping.
 */
final class SettingsController
{
    /** Whitelisted keys + their type ('bool'|'int') + label. */
    public const KEYS = [
        'task_status_enabled'   => ['bool', 'Task status colors'],
        'assignment_slider_max' => ['int',  'Cell slider max (days)'],
    ];

    public const ASSIGNMENT_SLIDER_MAX_DEFAULT = 10;
    public const ASSIGNMENT_SLIDER_MAX_FLOOR   = 1;
    public const ASSIGNMENT_SLIDER_MAX_CEIL    = 100;

    public function __construct(
        private readonly PDO                   $pdo,
        private readonly UserRepository        $users,
        private readonly AppSettingsRepository $settings,
        private readonly AuditLogger           $audit,
        private readonly View                  $view,
    ) {
    }

    /** GET /settings — admin only. */
    public function show(Request $req): Response
    {
        $actor = SessionGuard::requireAdmin($this->users);
        if ($actor instanceof Response) {
            return $actor;
        }

        $values = [];
        foreach (self::KEYS as $key => [$type]) {
            if ($type === 'bool') {
                $values[$key] = $this->settings->getBool($key, false);
            } else { // int
                $values[$key] = $this->settings->getInt(
                    $key,
                    self::ASSIGNMENT_SLIDER_MAX_DEFAULT,
                );
            }
        }

        return Response::html($this->view->render('settings/index', [
            'title'       => 'Settings',
            'currentUser' => $actor,
            'csrfToken'   => SessionGuard::csrfToken(),
            'values'      => $values,
            'keyLabels'   => array_map(fn(array $meta) => $meta[1], self::KEYS),
            'flash'       => $req->queryString('updated') !== '' ? 'Saved' : null,
        ]));
    }

    /** POST /settings — admin only, CSRF via _csrf form field. */
    public function update(Request $req): Response
    {
        $actor = SessionGuard::requireAdminForm($req, $this->users);
        if ($actor instanceof Response) {
            return $actor;
        }

        $this->pdo->beginTransaction();
        try {
            foreach (self::KEYS as $key => [$type]) {
                $raw = isset($req->post[$key]) ? (string) $req->post[$key] : '';

                if ($type === 'bool') {
                    $next = in_array($raw, ['1', 'on', 'true'], true) ? '1' : '0';
                } else { // int — clamp into [floor, ceil]
                    $n = is_numeric($raw)
                        ? (int) $raw
                        : self::ASSIGNMENT_SLIDER_MAX_DEFAULT;
                    if ($n < self::ASSIGNMENT_SLIDER_MAX_FLOOR) {
                        $n = self::ASSIGNMENT_SLIDER_MAX_FLOOR;
                    } elseif ($n > self::ASSIGNMENT_SLIDER_MAX_CEIL) {
                        $n = self::ASSIGNMENT_SLIDER_MAX_CEIL;
                    }
                    $next = (string) $n;
                }

                $result = $this->settings->set($key, $next);
                if ($result['before'] === $result['after']) {
                    continue;
                }
                $this->audit->recordForRequest(
                    'UPDATE', 'app_setting', null,
                    ['key' => $key, 'value' => $result['before']],
                    ['key' => $key, 'value' => $result['after']],
                    $req, $actor,
                );
            }
            $this->pdo->commit();
        } catch (Throwable) {
            $this->pdo->rollBack();
            return Response::redirect('/settings?error=db');
        }

        return Response::redirect('/settings?updated=1');
    }
}
