<?php

declare(strict_types=1);

namespace App\Domain\Theme;

use App\Settings\GeneralSettings;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\View;
use InvalidArgumentException;
use RuntimeException;
use Throwable;

class ThemeManager
{
    protected string $activeTheme = 'default';

    /** @var array<string, array<string, mixed>> */
    protected array $discovered = [];

    public function discover(): Collection
    {
        $themePath = config('larablog.theme_path', base_path('themes'));

        if (! is_dir($themePath)) {
            return collect();
        }

        $this->discovered = collect(File::directories($themePath))
            ->mapWithKeys(function (string $directory) use ($themePath): array {
                $slug = basename($directory);
                $manifestPath = $directory.'/theme.json';
                $manifest = [];

                if (is_file($manifestPath)) {
                    $decoded = json_decode((string) file_get_contents($manifestPath), true);
                    $manifest = is_array($decoded) ? $decoded : [];
                }

                $theme = array_merge([
                    'slug' => $slug,
                    'name' => $slug,
                    'path' => $directory,
                ], $manifest);

                $fallback = $slug === 'default' ? null : $themePath.'/default/views';
                $theme['slot_report'] = ThemeSlotReport::analyze($slug, $manifest, $directory, $fallback);

                return [$slug => $theme];
            })
            ->all();

        return collect($this->discovered);
    }

    /**
     * Slots declared in theme.json (expanded). Empty if undeclared.
     *
     * @return list<string>
     */
    public function declaredSlots(?string $slug = null): array
    {
        $slug ??= $this->active();
        $theme = $this->discover()->get($slug);

        if (! is_array($theme)) {
            return [];
        }

        return $theme['slot_report']['declared'] ?? [];
    }

    /**
     * @return array<string, mixed>
     */
    public function slotReport(?string $slug = null): array
    {
        $slug ??= $this->active();
        $theme = $this->discover()->get($slug);

        if (! is_array($theme) || ! isset($theme['slot_report'])) {
            return ThemeSlotReport::analyze($slug, [], $this->path($slug));
        }

        return $theme['slot_report'];
    }

    public function supportsSlot(string $slot, ?string $slug = null): bool
    {
        $declared = $this->declaredSlots($slug);

        if ($declared === []) {
            // Undeclared themes: assume unknown (not supported for soft checks).
            return false;
        }

        return in_array($slot, $declared, true);
    }

    public function setActive(string $slug): void
    {
        $themes = $this->discover();

        if (! $themes->has($slug)) {
            throw new InvalidArgumentException("Theme [{$slug}] was not found.");
        }

        $this->activeTheme = $slug;

        $settings = $this->settings();

        if ($settings !== null) {
            $settings->active_theme = $slug;
            $settings->save();
        }

        $this->registerViewNamespaces();
    }

    public function active(): string
    {
        try {
            $settings = app(GeneralSettings::class);

            if (filled($settings->active_theme)) {
                return $settings->active_theme;
            }
        } catch (Throwable) {
            // Settings may be unavailable or incomplete during migrations.
        }

        return $this->activeTheme;
    }

    public function path(?string $slug = null): string
    {
        $slug ??= $this->active();

        return rtrim(config('larablog.theme_path', base_path('themes')), '/').'/'.$slug;
    }

    public function registerViewNamespaces(): void
    {
        try {
            $themePath = config('larablog.theme_path', base_path('themes'));
            $defaultPath = $themePath.'/default';

            if (is_dir($defaultPath)) {
                View::addNamespace('theme', $defaultPath.'/views');
            }

            $active = $this->active();
            $activePath = $this->path($active).'/views';

            if ($active !== 'default' && is_dir($activePath)) {
                View::prependNamespace('theme', $activePath);
            }
        } catch (Throwable) {
            // Ignore during incomplete settings/bootstrap states.
        }
    }

    public function assetUrl(string $path): string
    {
        $trimmed = ltrim($path, '/');

        return url('/themes/'.$this->active().'/'.$trimmed);
    }

    public function ensureActiveThemeExists(): void
    {
        if (! is_dir($this->path($this->active()))) {
            if ($this->active() !== 'default' && is_dir($this->path('default'))) {
                $this->activeTheme = 'default';

                return;
            }

            throw new RuntimeException('No valid theme directory found.');
        }
    }

    protected function settings(): ?GeneralSettings
    {
        try {
            return app(GeneralSettings::class);
        } catch (Throwable) {
            return null;
        }
    }
}
