<?php

declare(strict_types=1);

namespace App\Services\Theme;

use App\Models\Theme;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;
use ZipArchive;

/**
 * ThemeManager — gnuboard g5_theme parity for W·Dot.
 *
 *  themes/{slug}/theme.json
 *  themes/{slug}/assets/theme.css
 *  themes/{slug}/templates/...   (optional Vue)
 *  themes/{slug}/screenshot.{svg,png}
 */
class ThemeManager
{
    public function themesRoot(): string
    {
        return rtrim(config('theme.themes_root', base_path('../themes')), '/');
    }

    public function publicUrlBase(): string
    {
        return rtrim(config('theme.themes_url', '/themes'), '/');
    }

    /**
     * Scan themes/ directory, register new themes in DB.
     * Returns the collection of themes after scan (current tenant).
     */
    public function scan(?string $tenantId = null): Collection
    {
        $root = $this->themesRoot();
        if (! is_dir($root)) {
            return collect();
        }
        $tenantId = $tenantId ?? optional(app()->bound('currentTenant') ? app('currentTenant') : null)->id;

        $found = [];
        foreach (scandir($root) ?: [] as $entry) {
            if ($entry === '.' || $entry === '..' || ! is_dir("$root/$entry")) continue;
            $manifestPath = "$root/$entry/theme.json";
            if (! is_file($manifestPath)) continue;

            $manifest = json_decode((string) file_get_contents($manifestPath), true);
            if (! is_array($manifest) || empty($manifest['slug'])) continue;

            $slug = (string) $manifest['slug'];
            $path = "themes/$entry";
            $shotRel = $manifest['screenshot'] ?? 'screenshot.svg';
            if (! is_file("$root/$entry/$shotRel") && is_file("$root/$entry/screenshot.png")) {
                $shotRel = 'screenshot.png';
            }
            $screenshotUrl = $this->publicUrlBase() . "/$entry/$shotRel";

            $type = $manifest['type'] ?? Theme::TYPE_COMMUNITY;
            if (! in_array($type, [Theme::TYPE_CORE, Theme::TYPE_COMMUNITY, Theme::TYPE_PREMIUM], true)) {
                $type = Theme::TYPE_COMMUNITY;
            }

            $found[] = $slug;

            $query = Theme::query()->withoutGlobalScope('tenant');
            if ($tenantId) $query->where('tenant_id', $tenantId);
            else $query->whereNull('tenant_id');

            $existing = (clone $query)->where('slug', $slug)->first();

            $data = [
                'name' => $manifest['name'] ?? Str::title($slug),
                'version' => $manifest['version'] ?? '1.0.0',
                'author' => $manifest['author'] ?? null,
                'description' => $manifest['description'] ?? null,
                'path' => $path,
                'screenshot_url' => $screenshotUrl,
                'type' => $type,
                'settings' => $manifest['settings'] ?? null,
                'manifest' => $manifest,
                'installed_at' => $existing?->installed_at ?? now(),
            ];

            if ($existing) {
                $existing->update($data);
            } else {
                Theme::create(array_merge($data, [
                    'tenant_id' => $tenantId,
                    'slug' => $slug,
                    'is_active' => false,
                ]));
            }
        }

        Log::info('ThemeManager.scan', ['count' => count($found), 'slugs' => $found, 'tenant_id' => $tenantId]);

        return Theme::query()->when($tenantId, fn($q) => $q->where('tenant_id', $tenantId))->get();
    }

    public function activate(string $slug, ?string $tenantId = null): bool
    {
        $tenantId = $tenantId ?? optional(app()->bound('currentTenant') ? app('currentTenant') : null)->id;
        $q = Theme::query()->withoutGlobalScope('tenant');
        if ($tenantId) $q->where('tenant_id', $tenantId);
        $theme = (clone $q)->where('slug', $slug)->first();
        if (! $theme) return false;

        // Deactivate others in tenant
        (clone $q)->where('id', '!=', $theme->id)->update(['is_active' => false]);
        $theme->update(['is_active' => true]);
        return true;
    }

    public function getActive(?string $tenantId = null): ?Theme
    {
        $tenantId = $tenantId ?? optional(app()->bound('currentTenant') ? app('currentTenant') : null)->id;
        $q = Theme::query()->withoutGlobalScope('tenant')->where('is_active', true);
        if ($tenantId) $q->where('tenant_id', $tenantId);
        return $q->first();
    }

    public function uninstall(string $slug, ?string $tenantId = null): bool
    {
        $tenantId = $tenantId ?? optional(app()->bound('currentTenant') ? app('currentTenant') : null)->id;
        $q = Theme::query()->withoutGlobalScope('tenant')->where('slug', $slug);
        if ($tenantId) $q->where('tenant_id', $tenantId);
        $theme = $q->first();
        if (! $theme) return false;
        if ($theme->type === Theme::TYPE_CORE) return false; // core protected
        $theme->delete();
        return true;
    }

    public function validate(array $manifest): bool
    {
        return ! empty($manifest['slug']) && ! empty($manifest['name']);
    }

    /**
     * Extract a ZIP package into themes/{slug}/ and register.
     */
    public function installFromZip(string $zipPath, ?string $tenantId = null): ?Theme
    {
        $zip = new ZipArchive();
        if ($zip->open($zipPath) !== true) {
            throw new \RuntimeException('CANNOT_OPEN_ZIP');
        }
        // Read theme.json from zip first
        $manifestStr = $zip->getFromName('theme.json');
        if ($manifestStr === false) {
            // Try root subdir
            for ($i = 0; $i < $zip->numFiles; $i++) {
                $name = $zip->getNameIndex($i);
                if (str_ends_with($name, '/theme.json') && substr_count($name, '/') === 1) {
                    $manifestStr = $zip->getFromIndex($i);
                    break;
                }
            }
        }
        if (! $manifestStr) {
            $zip->close();
            throw new \RuntimeException('THEME_JSON_NOT_FOUND');
        }
        $manifest = json_decode($manifestStr, true);
        if (! $this->validate($manifest ?? [])) {
            $zip->close();
            throw new \RuntimeException('INVALID_MANIFEST');
        }
        $slug = preg_replace('/[^a-z0-9_-]/', '', strtolower((string) $manifest['slug']));
        if (! $slug) {
            $zip->close();
            throw new \RuntimeException('INVALID_SLUG');
        }
        $target = $this->themesRoot() . "/$slug";
        if (! is_dir($target)) mkdir($target, 0775, true);
        $zip->extractTo($target);
        $zip->close();

        $this->scan($tenantId);
        return Theme::query()->withoutGlobalScope('tenant')->where('slug', $slug)->first();
    }
}
