<?php

declare(strict_types=1);

namespace App\Services\Theme;

use App\Models\Board;
use App\Models\Skin;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use ZipArchive;

/**
 * SkinManager — gnuboard g5_skin parity for W·Dot.
 *
 *  skins/{type}/{slug}/skin.json
 *  skins/{type}/{slug}/*.vue
 */
class SkinManager
{
    public function skinsRoot(): string
    {
        return rtrim(config('theme.skins_root', base_path('../skins')), '/');
    }

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

    public function scan(?string $type = null, ?string $tenantId = null): Collection
    {
        $root = $this->skinsRoot();
        if (! is_dir($root)) return collect();
        $tenantId = $tenantId ?? optional(app()->bound('currentTenant') ? app('currentTenant') : null)->id;

        $types = $type ? [$type] : Skin::TYPES;
        $found = [];

        foreach ($types as $t) {
            $typeDir = "$root/$t";
            if (! is_dir($typeDir)) continue;
            foreach (scandir($typeDir) ?: [] as $entry) {
                if ($entry === '.' || $entry === '..' || ! is_dir("$typeDir/$entry")) continue;
                $manifestPath = "$typeDir/$entry/skin.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'];
                $shotRel = $manifest['screenshot'] ?? 'screenshot.svg';
                if (! is_file("$typeDir/$entry/$shotRel") && is_file("$typeDir/$entry/screenshot.png")) {
                    $shotRel = 'screenshot.png';
                }
                $screenshotUrl = $this->publicUrlBase() . "/$t/$entry/$shotRel";

                $q = Skin::query()->withoutGlobalScope('tenant')->where('type', $t)->where('slug', $slug);
                if ($tenantId) $q->where('tenant_id', $tenantId);
                else $q->whereNull('tenant_id');

                $data = [
                    'name' => $manifest['name'] ?? ucfirst($slug),
                    'version' => $manifest['version'] ?? '1.0.0',
                    'author' => $manifest['author'] ?? null,
                    'description' => $manifest['description'] ?? null,
                    'path' => "skins/$t/$entry",
                    'screenshot_url' => $screenshotUrl,
                    'settings' => $manifest['settings'] ?? null,
                    'manifest' => $manifest,
                    'installed_at' => now(),
                ];
                $existing = $q->first();
                if ($existing) {
                    $existing->update($data);
                } else {
                    Skin::create(array_merge($data, [
                        'tenant_id' => $tenantId,
                        'type' => $t,
                        'slug' => $slug,
                        'is_default' => false,
                    ]));
                }
                $found[] = "$t/$slug";
            }
        }

        Log::info('SkinManager.scan', ['count' => count($found), 'items' => $found, 'tenant_id' => $tenantId]);
        return Skin::query()
            ->when($type, fn($q) => $q->where('type', $type))
            ->when($tenantId, fn($q) => $q->where('tenant_id', $tenantId))
            ->get();
    }

    public function activateDefault(string $type, string $slug, ?string $tenantId = null): bool
    {
        $tenantId = $tenantId ?? optional(app()->bound('currentTenant') ? app('currentTenant') : null)->id;
        $q = Skin::query()->withoutGlobalScope('tenant')->where('type', $type);
        if ($tenantId) $q->where('tenant_id', $tenantId);
        $skin = (clone $q)->where('slug', $slug)->first();
        if (! $skin) return false;
        (clone $q)->where('id', '!=', $skin->id)->update(['is_default' => false]);
        $skin->update(['is_default' => true]);
        return true;
    }

    public function assignToBoard(string $boardSlug, string $skinSlug): bool
    {
        $skin = Skin::query()->withoutGlobalScope('tenant')->where('type', 'board')->where('slug', $skinSlug)->first();
        if (! $skin) return false;
        $board = Board::query()->where('slug', $boardSlug)->first();
        if (! $board) return false;
        $board->update(['skin' => $skinSlug]);
        return true;
    }

    public function installFromZip(string $zipPath, string $type, ?string $tenantId = null): ?Skin
    {
        if (! in_array($type, Skin::TYPES, true)) {
            throw new \RuntimeException('INVALID_SKIN_TYPE');
        }
        $zip = new ZipArchive();
        if ($zip->open($zipPath) !== true) {
            throw new \RuntimeException('CANNOT_OPEN_ZIP');
        }
        $manifestStr = $zip->getFromName('skin.json');
        if ($manifestStr === false) {
            for ($i = 0; $i < $zip->numFiles; $i++) {
                $name = $zip->getNameIndex($i);
                if (str_ends_with($name, '/skin.json') && substr_count($name, '/') === 1) {
                    $manifestStr = $zip->getFromIndex($i);
                    break;
                }
            }
        }
        if (! $manifestStr) { $zip->close(); throw new \RuntimeException('SKIN_JSON_NOT_FOUND'); }
        $manifest = json_decode($manifestStr, true);
        $slug = preg_replace('/[^a-z0-9_-]/', '', strtolower((string) ($manifest['slug'] ?? '')));
        if (! $slug) { $zip->close(); throw new \RuntimeException('INVALID_SLUG'); }
        $target = $this->skinsRoot() . "/$type/$slug";
        if (! is_dir($target)) mkdir($target, 0775, true);
        $zip->extractTo($target);
        $zip->close();
        $this->scan($type, $tenantId);
        return Skin::query()->withoutGlobalScope('tenant')->where('type', $type)->where('slug', $slug)->first();
    }

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