<?php

declare(strict_types=1);

namespace App\Services\Localization;

use App\Models\User;
use App\Models\Workspace;
use App\Services\Settings\SettingsResolver;
use App\Support\Workspaces\WorkspaceContext;
use Illuminate\Http\Request;

class LocaleResolver
{
    public const SESSION_OVERRIDE_KEY = 'tenantpilot.locale_override';

    public const REQUEST_ATTRIBUTE = 'tenantpilot.resolved_locale';

    public const SETTING_DOMAIN = 'localization';

    public const SETTING_DEFAULT_LOCALE = 'default_locale';

    public const SOURCE_EXPLICIT_OVERRIDE = 'explicit_override';

    public const SOURCE_USER_PREFERENCE = 'user_preference';

    public const SOURCE_WORKSPACE_DEFAULT = 'workspace_default';

    public const SOURCE_SYSTEM_DEFAULT = 'system_default';

    /**
     * @var list<string>
     */
    private const SUPPORTED_LOCALES = ['en', 'de'];

    public function __construct(
        private SettingsResolver $settingsResolver,
        private WorkspaceContext $workspaceContext,
    ) {}

    /**
     * @return list<string>
     */
    public static function supportedLocales(): array
    {
        return self::SUPPORTED_LOCALES;
    }

    /**
     * @return array<string, string>
     */
    public static function localeOptions(): array
    {
        return [
            'en' => __('localization.locales.en'),
            'de' => __('localization.locales.de'),
        ];
    }

    public static function isSupported(mixed $locale): bool
    {
        return self::normalize($locale) !== null;
    }

    public static function normalize(mixed $locale): ?string
    {
        if (! is_string($locale)) {
            return null;
        }

        $normalized = strtolower(trim($locale));

        return in_array($normalized, self::SUPPORTED_LOCALES, true) ? $normalized : null;
    }

    /**
     * @return array{
     *     locale: string,
     *     source: string,
     *     fallback_locale: string,
     *     user_preference_locale: ?string,
     *     workspace_default_locale: ?string,
     *     machine_artifacts_invariant: true
     * }
     */
    public function resolve(Request $request, ?string $plane = null): array
    {
        $plane = $this->normalizePlane($plane, $request);

        $explicitOverride = $this->explicitOverride($request);
        $systemDefault = (string) config('app.fallback_locale', 'en');

        if ($plane === 'system') {
            return $this->resolveFromSources(
                explicitOverride: $explicitOverride,
                userPreference: null,
                workspaceDefault: null,
                systemDefault: $systemDefault,
                includeUserPreference: false,
                includeWorkspaceDefault: false,
            );
        }

        $user = $request->user();
        $userPreference = $user instanceof User ? $user->preferred_locale : null;
        $workspaceDefault = $this->workspaceDefault($request);

        return $this->resolveFromSources(
            explicitOverride: $explicitOverride,
            userPreference: $userPreference,
            workspaceDefault: $workspaceDefault,
            systemDefault: $systemDefault,
            includeUserPreference: true,
            includeWorkspaceDefault: true,
        );
    }

    /**
     * @return array{
     *     locale: string,
     *     source: string,
     *     fallback_locale: string,
     *     user_preference_locale: ?string,
     *     workspace_default_locale: ?string,
     *     machine_artifacts_invariant: true
     * }
     */
    public function resolveFromSources(
        mixed $explicitOverride,
        mixed $userPreference,
        mixed $workspaceDefault,
        mixed $systemDefault,
        bool $includeUserPreference = true,
        bool $includeWorkspaceDefault = true,
    ): array {
        $fallbackLocale = self::normalize(config('app.fallback_locale', 'en')) ?? 'en';

        $candidates = [
            self::SOURCE_EXPLICIT_OVERRIDE => self::normalize($explicitOverride),
        ];

        if ($includeUserPreference) {
            $candidates[self::SOURCE_USER_PREFERENCE] = self::normalize($userPreference);
        }

        if ($includeWorkspaceDefault) {
            $candidates[self::SOURCE_WORKSPACE_DEFAULT] = self::normalize($workspaceDefault);
        }

        $candidates[self::SOURCE_SYSTEM_DEFAULT] = self::normalize($systemDefault) ?? $fallbackLocale;

        foreach ($candidates as $source => $locale) {
            if ($locale !== null) {
                return [
                    'locale' => $locale,
                    'source' => $source,
                    'fallback_locale' => $fallbackLocale,
                    'user_preference_locale' => $includeUserPreference ? self::normalize($userPreference) : null,
                    'workspace_default_locale' => $includeWorkspaceDefault ? self::normalize($workspaceDefault) : null,
                    'machine_artifacts_invariant' => true,
                ];
            }
        }

        return [
            'locale' => $fallbackLocale,
            'source' => self::SOURCE_SYSTEM_DEFAULT,
            'fallback_locale' => $fallbackLocale,
            'user_preference_locale' => $includeUserPreference ? self::normalize($userPreference) : null,
            'workspace_default_locale' => $includeWorkspaceDefault ? self::normalize($workspaceDefault) : null,
            'machine_artifacts_invariant' => true,
        ];
    }

    private function explicitOverride(Request $request): ?string
    {
        $queryLocale = self::normalize($request->query('locale'));

        if ($queryLocale !== null) {
            return $queryLocale;
        }

        if (! $request->hasSession()) {
            return null;
        }

        return self::normalize($request->session()->get(self::SESSION_OVERRIDE_KEY));
    }

    private function workspaceDefault(Request $request): ?string
    {
        $workspace = $this->workspaceContext->currentWorkspace($request);

        if (! $workspace instanceof Workspace) {
            return null;
        }

        return self::normalize($this->settingsResolver->resolveValue(
            workspace: $workspace,
            domain: self::SETTING_DOMAIN,
            key: self::SETTING_DEFAULT_LOCALE,
        ));
    }

    private function normalizePlane(?string $plane, Request $request): string
    {
        $plane = strtolower(trim((string) $plane));

        if (in_array($plane, ['admin', 'tenant', 'system'], true)) {
            return $plane;
        }

        return $request->is('system', 'system/*') ? 'system' : 'admin';
    }
}
