<?php

declare(strict_types=1);

namespace Maestro\Core\Http\Controllers\Superadmin;

use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
use Illuminate\Validation\Rule;
use Maestro\Core\Http\Controllers\Controller;
use Maestro\Core\Models\MaestroAppSetting;
use Maestro\Core\Support\MaestroAppSettingsPresenter;

class MaestroAppSettingsController extends Controller
{
    /**
     * @var list<string>
     */
    private const BRANDING_SLOTS = ['sidebar', 'header', 'compact', 'favicon', 'loginHero'];

    /**
     * @var array<string, string>
     */
    private const COLUMN_BY_SLOT = [
        'sidebar' => 'logo_sidebar_path',
        'header' => 'logo_header_path',
        'compact' => 'logo_compact_path',
        'favicon' => 'favicon_path',
        'loginHero' => 'login_hero_path',
    ];

    public function update(Request $request): JsonResponse
    {
        $validated = $request->validate([
            'theme' => ['sometimes', 'array'],
            'theme.preset' => ['sometimes', 'string'],
            'theme.primary' => ['sometimes', 'string'],
            'theme.surface' => ['sometimes', 'nullable', 'string'],
            'theme.darkTheme' => ['sometimes', 'boolean'],
            'theme.menuMode' => ['sometimes', 'string'],
            'theme.menuTheme' => ['sometimes', 'string'],
            'theme.cardStyle' => ['sometimes', 'string'],
            'theme.primaryCustom' => ['sometimes', 'nullable', 'string', 'regex:/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/'],
            'theme.surfaceCustom' => ['sometimes', 'nullable', 'string', 'regex:/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/'],
            'publicPage' => ['sometimes', 'array'],
            'publicPage.enabled' => ['sometimes', 'boolean'],
            'publicPage.mode' => ['sometimes', 'string', Rule::in(['core_default', 'customer_override'])],
            'publicPage.rootMode' => ['sometimes', 'string', Rule::in(['dashboard', 'login', 'public'])],
            'login' => ['sometimes', 'array'],
            'login.providers' => ['sometimes', 'array'],
            'login.providers.microsoft' => ['sometimes', 'nullable', 'boolean'],
            'login.providers.google' => ['sometimes', 'boolean'],
            'login.providers.apple' => ['sometimes', 'boolean'],
        ]);

        if (
            ! array_key_exists('theme', $validated)
            && ! array_key_exists('publicPage', $validated)
            && ! array_key_exists('login', $validated)
        ) {
            abort(422, 'At least one of `theme`, `publicPage`, or `login` must be provided.');
        }

        $row = MaestroAppSetting::instance();

        if (array_key_exists('theme', $validated)) {
            $incoming = self::normalizeThemePayload($validated['theme']);
            $row->theme = array_replace($row->theme ?? [], $incoming);
        }

        if (array_key_exists('publicPage', $validated)) {
            $incoming = self::normalizePublicPagePayload($validated['publicPage']);
            $current = is_array($row->public_page) ? $row->public_page : [];
            $next = array_replace($current, $incoming);

            // Keep root behavior simple and deterministic:
            // enabled=false => login redirect, enabled=true => public homepage.
            $enabled = (bool) ($next['enabled'] ?? false);
            $next['root_mode'] = $enabled ? 'public' : 'login';

            $row->public_page = $next;
        }

        if (array_key_exists('login', $validated)) {
            $incoming = self::normalizeLoginPayload($validated['login']);
            $current = is_array($row->login) ? $row->login : [];
            $currentProviders = is_array($current['providers'] ?? null) ? $current['providers'] : [];
            $incomingProviders = is_array($incoming['providers'] ?? null) ? $incoming['providers'] : [];
            $row->login = [
                'providers' => array_replace($currentProviders, $incomingProviders),
            ];
        }

        $row->save();

        return response()->json(MaestroAppSettingsPresenter::toArray($row));
    }

    /**
     * @param  array<string, mixed>  $theme
     * @return array<string, mixed>
     */
    private static function normalizeThemePayload(array $theme): array
    {
        $out = [];

        foreach (['preset', 'primary', 'menuMode', 'menuTheme', 'cardStyle'] as $key) {
            if (! array_key_exists($key, $theme)) {
                continue;
            }
            $out[$key] = (string) $theme[$key];
        }

        if (array_key_exists('surface', $theme)) {
            $surface = $theme['surface'];
            $out['surface'] = ($surface === null || $surface === '') ? null : (string) $surface;
        }

        if (array_key_exists('darkTheme', $theme)) {
            $dark = $theme['darkTheme'];
            $parsed = filter_var($dark, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
            $out['darkTheme'] = $parsed ?? (bool) $dark;
        }

        foreach (['primaryCustom', 'surfaceCustom'] as $key) {
            if (! array_key_exists($key, $theme)) {
                continue;
            }
            $value = $theme[$key];
            if ($value === null || $value === '') {
                $out[$key] = null;
                continue;
            }
            $normalized = self::normalizeHexColor((string) $value);
            if ($normalized !== null) {
                $out[$key] = $normalized;
            }
        }

        return $out;
    }

    private static function normalizeHexColor(string $value): ?string
    {
        if (! preg_match('/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/', $value)) {
            return null;
        }

        if (strlen($value) === 4) {
            return sprintf(
                '#%s%s%s%s%s%s',
                $value[1],
                $value[1],
                $value[2],
                $value[2],
                $value[3],
                $value[3],
            );
        }

        return strtolower($value);
    }

    /**
     * @param  array<string, mixed>  $publicPage
     * @return array<string, mixed>
     */
    private static function normalizePublicPagePayload(array $publicPage): array
    {
        $out = [];

        if (array_key_exists('enabled', $publicPage)) {
            $enabled = $publicPage['enabled'];
            $parsed = filter_var($enabled, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
            $out['enabled'] = $parsed ?? (bool) $enabled;
        }

        if (array_key_exists('mode', $publicPage)) {
            $out['mode'] = (string) $publicPage['mode'];
        }

        if (array_key_exists('rootMode', $publicPage)) {
            $out['root_mode'] = (string) $publicPage['rootMode'];
        }

        return $out;
    }

    /**
     * @param  array<string, mixed>  $login
     * @return array<string, mixed>
     */
    private static function normalizeLoginPayload(array $login): array
    {
        $out = [];

        if (! array_key_exists('providers', $login) || ! is_array($login['providers'])) {
            return $out;
        }

        $providers = [];

        if (array_key_exists('microsoft', $login['providers'])) {
            $microsoft = $login['providers']['microsoft'];
            if ($microsoft === null) {
                $providers['microsoft'] = null;
            } else {
                $parsed = filter_var($microsoft, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
                $providers['microsoft'] = $parsed ?? (bool) $microsoft;
            }
        }

        foreach (['google', 'apple'] as $key) {
            if (! array_key_exists($key, $login['providers'])) {
                continue;
            }
            $value = $login['providers'][$key];
            $parsed = filter_var($value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
            $providers[$key] = $parsed ?? (bool) $value;
        }

        if ($providers !== []) {
            $out['providers'] = $providers;
        }

        return $out;
    }

    public function uploadBranding(Request $request): JsonResponse
    {
        $validated = $request->validate([
            'slot' => ['required', 'string', Rule::in(self::BRANDING_SLOTS)],
            'file' => ['required', 'file', 'max:4096', 'mimes:png,jpg,jpeg,webp,gif,svg,ico'],
        ]);

        $column = self::COLUMN_BY_SLOT[$validated['slot']];
        $row = MaestroAppSetting::instance();

        $disk = Storage::disk('public');
        $path = $request->file('file')->store('branding', 'public');

        if ($row->{$column}) {
            $disk->delete($row->{$column});
        }

        $row->{$column} = $path;
        $row->save();

        return response()->json(MaestroAppSettingsPresenter::toArray($row));
    }

    public function clearBranding(string $slot): JsonResponse
    {
        abort_unless(in_array($slot, self::BRANDING_SLOTS, true), 404);

        $column = self::COLUMN_BY_SLOT[$slot];
        $row = MaestroAppSetting::instance();

        if ($row->{$column}) {
            Storage::disk('public')->delete($row->{$column});
            $row->{$column} = null;
            $row->save();
        }

        return response()->json(MaestroAppSettingsPresenter::toArray($row));
    }

    public function uploadOfferDocumentBackground(Request $request): JsonResponse
    {
        $request->validate([
            'file' => ['required', 'file', 'max:8192', 'mimes:jpg,jpeg,png,webp'],
        ]);

        $row = MaestroAppSetting::instance();
        $paths = $row->offer_document_background_paths ?? [];
        if (! is_array($paths)) {
            $paths = [];
        }

        if (count($paths) >= 24) {
            abort(422, 'Maximum number of custom offer backgrounds reached.');
        }

        $disk = Storage::disk('public');
        $stored = $request->file('file')->store('branding/offer-backgrounds', 'public');

        $paths[] = $stored;
        $row->offer_document_background_paths = array_values($paths);
        $row->save();

        return response()->json(MaestroAppSettingsPresenter::toArray($row));
    }

    public function deleteOfferDocumentBackground(int $index): JsonResponse
    {
        $row = MaestroAppSetting::instance();
        $paths = $row->offer_document_background_paths ?? [];
        if (! is_array($paths) || ! array_key_exists($index, $paths)) {
            abort(404);
        }

        $relative = (string) $paths[$index];
        $disk = Storage::disk('public');
        if ($relative !== '' && $disk->exists($relative)) {
            $disk->delete($relative);
        }

        unset($paths[$index]);
        $row->offer_document_background_paths = array_values($paths);
        $row->save();

        return response()->json(MaestroAppSettingsPresenter::toArray($row));
    }
}
