<?php

namespace App\Domain\Website\Services;

use App\Domain\Shared\Exceptions\DomainException;
use App\Domain\Website\Blocks\BlockField;
use App\Domain\Website\Blocks\BlockRegistry;
use App\Domain\Website\Blocks\BlockType;
use App\Domain\Website\Blocks\FieldType;
use App\Domain\Website\Models\WebsiteBlock;
use App\Domain\Website\Models\WebsitePage;
use App\Models\User;
use Illuminate\Support\Facades\DB;

/**
 * Import/export a whole site as JSON.
 *
 * Lets a finished design be snapshotted and re-applied to another academy, so
 * building a site once for one client makes it a reusable starting point for
 * the next. Import is explicit (artisan) and never runs during automatic
 * seeding, because every tenant boots from the same image.
 */
class WebsiteBlueprintService
{
    public function __construct(
        private readonly BlockRegistry $registry,
        private readonly WebsitePageService $pages,
    ) {}

    public const VERSION = 1;

    /** @return array<string,mixed> */
    public function export(): array
    {
        return [
            'version' => self::VERSION,
            'pages' => WebsitePage::with('blocks.childrenRecursive')
                ->orderBy('sort_order')
                ->get()
                ->map(fn (WebsitePage $page) => [
                    'slug' => $page->slug,
                    'title' => $page->title,
                    'title_en' => $page->title_en,
                    'meta_description' => $page->meta_description,
                    'meta_description_en' => $page->meta_description_en,
                    'layout' => $page->layout,
                    'is_homepage' => $page->is_homepage,
                    'is_published' => $page->is_published,
                    'sort_order' => $page->sort_order,
                    'blocks' => $page->blocks->map(fn ($b) => $this->exportBlock($b))->all(),
                ])->all(),
        ];
    }

    private function exportBlock(WebsiteBlock $block): array
    {
        return [
            'type' => $block->type,
            'variant' => $block->variant,
            'slot' => $block->slot,
            'is_enabled' => $block->is_enabled,
            'data' => $block->data ?? [],
            'style' => $block->style ?? [],
            'children' => $block->children->map(fn ($c) => $this->exportBlock($c))->all(),
        ];
    }

    /**
     * Imports a blueprint for the current academy.
     *
     * @param  bool  $replace  drop existing pages with the same slug first
     * @return array{pages:int, blocks:int, skipped:array<string>}
     */
    public function import(array $blueprint, User $actor, bool $replace = false): array
    {
        if (($blueprint['version'] ?? null) !== self::VERSION) {
            throw new DomainException('إصدار ملف التصميم غير مدعوم.');
        }

        $stats = ['pages' => 0, 'blocks' => 0, 'skipped' => []];

        DB::transaction(function () use ($blueprint, $actor, $replace, &$stats) {
            foreach ($blueprint['pages'] ?? [] as $pageData) {
                $slug = $pageData['slug'] ?? null;

                if (! $slug) {
                    continue;
                }

                $existing = WebsitePage::where('slug', $slug)->first();

                if ($existing && ! $replace) {
                    $stats['skipped'][] = $slug;

                    continue;
                }

                if ($existing) {
                    WebsiteBlock::where('website_page_id', $existing->id)->delete();
                    $existing->forceDelete();
                }

                $page = $this->pages->create([
                    'slug' => $slug,
                    'title' => $pageData['title'] ?? null,
                    'title_en' => $pageData['title_en'] ?? null,
                    'meta_description' => $pageData['meta_description'] ?? null,
                    'meta_description_en' => $pageData['meta_description_en'] ?? null,
                    'layout' => $pageData['layout'] ?? 'default',
                    'is_published' => $pageData['is_published'] ?? false,
                    'sort_order' => $pageData['sort_order'] ?? 0,
                ], $actor);

                foreach ($pageData['blocks'] ?? [] as $i => $blockData) {
                    $stats['blocks'] += $this->importBlock($blockData, $page->id, null, $i, $stats);
                }

                if ($pageData['is_homepage'] ?? false) {
                    $this->pages->makeHomepage($page);
                }

                $stats['pages']++;
            }
        });

        return $stats;
    }

    /**
     * Strips unsafe link targets from imported content.
     *
     * A blueprint is a file, so it bypasses the form validation that normally
     * constrains link fields. Without this an imported design could carry a
     * `javascript:` URL straight into a rendered href.
     */
    private function sanitizeData(BlockType $definition, array $data): array
    {
        foreach ($definition->fields() as $field) {
            if (! array_key_exists($field->key, $data)) {
                continue;
            }

            if ($field->type === FieldType::Link) {
                $data[$field->key] = safe_url(is_string($data[$field->key]) ? $data[$field->key] : null);

                continue;
            }

            if ($field->type === FieldType::Repeater && is_array($data[$field->key])) {
                foreach ($data[$field->key] as $i => $row) {
                    if (! is_array($row)) {
                        continue;
                    }

                    foreach ($field->fields as $sub) {
                        if ($sub->type === FieldType::Link && array_key_exists($sub->key, $row)) {
                            $data[$field->key][$i][$sub->key] = safe_url(
                                is_string($row[$sub->key]) ? $row[$sub->key] : null,
                            );
                        }
                    }
                }
            }
        }

        return $data;
    }

    private function importBlock(array $data, int $pageId, ?int $parentId, int $order, array &$stats): int
    {
        $type = $data['type'] ?? null;

        // An unknown type means the blueprint came from a build with a block we
        // do not have. Skip it rather than aborting the whole import.
        if (! $type || ! $this->registry->has($type)) {
            $stats['skipped'][] = "block:{$type}";

            return 0;
        }

        $definition = $this->registry->resolve($type);
        $variants = array_keys($definition->variants());
        $variant = $data['variant'] ?? null;

        $block = WebsiteBlock::create([
            'website_page_id' => $pageId,
            'parent_id' => $parentId,
            'slot' => $data['slot'] ?? 'default',
            'type' => $type,
            'variant' => in_array($variant, $variants, true) ? $variant : ($variants[0] ?? 'default'),
            'sort_order' => $order,
            'is_enabled' => $data['is_enabled'] ?? true,
            'data' => $this->sanitizeData($definition, array_replace_recursive(
                $definition->defaultData(),
                $data['data'] ?? [],
            )),
            'style' => $data['style'] ?? [],
        ]);

        $count = 1;

        foreach ($data['children'] ?? [] as $i => $child) {
            $count += $this->importBlock($child, $pageId, $block->id, $i, $stats);
        }

        return $count;
    }
}
