<?php

declare(strict_types=1);

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Models\SaasAdminAuditLog;
use App\Models\SystemSetting;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;

/**
 * SaaS-admin → Система: edit-flow для system_settings.
 *
 * GET /api/admin/system-settings → список всех ключей.
 * PUT /api/admin/system-settings/{key} → обновление с reason ≥30 chars.
 *
 * Каждое обновление пишет запись в `saas_admin_audit_log`
 * (action='system_settings.update', target_type='system_setting',
 * payload_before/after, reason, ip_address). Hash-chain trigger BEFORE INSERT
 * заполняет log_hash автоматически (OPEN-И-15).
 *
 * Type-validation: int → ctype_digit, decimal → numeric, bool → in('true','false'),
 * json → json_decode без ошибок. Иначе 422.
 *
 * NB: на MVP saas_admin_users auth не реализован — admin_user_id и ip_address
 * принимаются параметрами. Production: middleware('auth:saas-admin') +
 * $request->user()->id + $request->ip().
 */
class AdminSystemSettingsController extends Controller
{
    /** GET /api/admin/system-settings */
    public function index(): JsonResponse
    {
        return response()->json([
            'settings' => SystemSetting::orderBy('key')->get(),
        ]);
    }

    /** PUT /api/admin/system-settings/{key} */
    public function update(Request $request, string $key): JsonResponse
    {
        $newValue = (string) $request->input('value');
        $reason = $request->string('reason')->toString();
        $adminUserId = (int) $request->input('admin_user_id', 1); // TODO: $request->user()->id

        if (mb_strlen($reason) < 30) {
            return response()->json([
                'message' => 'Основание (reason) должно быть не короче 30 символов.',
                'errors' => ['reason' => ['Минимум 30 символов.']],
            ], 422);
        }

        $setting = SystemSetting::find($key);
        if (! $setting) {
            return response()->json(['message' => "Настройка «{$key}» не найдена."], 404);
        }

        if (! $this->isValidForType($newValue, $setting->type)) {
            return response()->json([
                'message' => "Значение не соответствует типу «{$setting->type}».",
                'errors' => ['value' => ["Должно быть валидным {$setting->type}."]],
            ], 422);
        }

        $oldValue = $setting->value;
        if ($oldValue === $newValue) {
            return response()->json([
                'message' => 'Значение не изменилось.',
                'errors' => ['value' => ['Новое значение совпадает со старым.']],
            ], 422);
        }

        DB::transaction(function () use ($setting, $oldValue, $newValue, $reason, $adminUserId, $request) {
            $setting->update([
                'value' => $newValue,
                'updated_at' => now(),
                'updated_by' => $adminUserId,
            ]);

            SaasAdminAuditLog::create([
                'admin_user_id' => $adminUserId,
                'action' => 'system_settings.update',
                'target_type' => 'system_setting',
                'target_id' => null, // PK — string key, не bigint
                'payload_before' => ['key' => $setting->key, 'value' => $oldValue],
                'payload_after' => ['key' => $setting->key, 'value' => $newValue],
                'reason' => $reason,
                'ip_address' => $request->ip() ?? '127.0.0.1',
                'user_agent' => $request->userAgent(),
            ]);
        });

        return response()->json([
            'key' => $setting->key,
            'value' => $newValue,
            'previous_value' => $oldValue,
            'updated_at' => $setting->updated_at->toIso8601String(),
            'message' => 'Настройка обновлена.',
        ]);
    }

    private function isValidForType(string $value, string $type): bool
    {
        return match ($type) {
            'int' => ctype_digit($value) || (str_starts_with($value, '-') && ctype_digit(substr($value, 1))),
            'decimal' => is_numeric($value),
            'bool' => in_array($value, ['true', 'false', '1', '0'], true),
            'json' => json_decode($value, true, 16, JSON_THROW_ON_ERROR) !== null || $value === 'null',
            default => true, // 'string' и unknown типы — без валидации
        };
    }
}
