<?php

namespace App\Http\Resources;

use App\Facades\Settings;
use App\ShortcodeHelper;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Support\Collection;
use Mtc\ContentManager\Contracts\Content;
use Mtc\ContentManager\Contracts\MediaUse;
use Mtc\ContentManager\Contracts\VersionModel;
use Mtc\ContentManager\Models\MediaSize;
use Mtc\MercuryDataModels\SeoData;
use Mtc\MercuryDataModels\SeoDefault;
use Mtc\MercuryDataModels\Form;

class PageResource extends JsonResource
{
    use FranchiseData;

    protected Collection $mediaSizes;

    private const string UI_RESOURCE_PREFIX = 'App\\Http\\Resources\\';

    private Request $request;

    /**
     * Transform the resource into an array.
     *
     * @param \Illuminate\Http\Request $request
     * @return array|\Illuminate\Contracts\Support\Arrayable|\JsonSerializable
     */
    #[\Override]
    public function toArray($request)
    {
        $this->request = $request;
        if ($request->filled('v')) {
            $this->resource->data = $this->previewVersion($request->input('v'));
        }
        if (empty($this->resource->data)) {
            $this->resource->load([
                'allContent.mediaUses.media',
                'allContent.contentElement',
                'allContent.globalContent.contentElement',
                'categories' => fn($query) => $query->orderBy('name'),
                'tags' => fn($query) => $query->orderBy('name'),
            ]);
            $this->resource->data = $this->mapContentList(
                $this->organiseContentInTree($this->resource->allContent)
            );
        }

        $this->resource->setHidden([
            'text',
        ]);

        $this->resource->setHidden([
            'created_at',
            'updated_at',
            'deleted_at',
            'search_content',
            'template_id',
            'status',
        ]);
        $resource = $this->resource->toArray();
        $resource['isFranchise'] = $this->isFranchise();
        $resource['featured'] = (bool)$resource['featured'];
        $resource['franchise'] = $this->franchiseData();
        $resource['seo'] = $this->fillSeo();
        $resource['social_meta'] = new SocialMetaResource($resource['seo']);
        $resource['published_at'] = $this->resource->published_at?->format('F j, Y');
        $resource['categories'] = $this->resource->categories->pluck('name', 'slug')->toArray();
        $resource['tags'] = $this->resource->tags->pluck('name', 'slug')->toArray();

        if (empty($resource['categories'])) {
            $resource['categories'] = null;
        }
        if (empty($resource['tags'])) {
            $resource['tags'] = null;
        }
        if ($this->resource->template?->slug) {
            $resource['template_slug'] = $this->resource->template->slug;
        }

        unset($resource['all_content']);
        unset($resource['password']);
        unset($resource['root_level_content']);
        return $resource;
    }

    private function previewVersion(mixed $versionId): ?Collection
    {
        /** @var VersionModel $version */
        $version = $this->resource->versions()
            ->where('uuid', $versionId)
            ->first();

        if (!$version) {
            return null;
        }

        $version->load([
            'allContent.mediaUses.media',
            'allContent.contentElement',
            'allContent.globalContent.contentElement',
        ]);

        return $this->mapContentList(
            $this->organiseContentInTree($version->allContent)
        );
    }

    private function mapContentList(Collection $contentList): Collection
    {
        return $contentList
            ->filter(fn($content) => $this->isViewableContent($content))
            ->map(fn(Content $content) => $this->isGlobalContent($content)
                ? $this->mapGlobalContent($content)
                : $this->mapContentElement($content));
    }

    private function isViewableContent($content): bool
    {
        if (!empty($content->hidden)) {
            return false;
        }

        if ($this->isGlobalContent($content)) {
            if (
                !empty($content->globalContent->available_from)
                && Carbon::parse($content->globalContent->available_from) > now()
            ) {
                return false;
            }

            if (
                !empty($content->globalContent->available_to)
                && Carbon::parse($content->globalContent->available_to) < now()
            ) {
                return false;
            }
        }

        return true;
    }

    private function isGlobalContent(Content $content): bool
    {
        return !empty($content->global_content_id);
    }

    private function mapGlobalContent(Content $content): array
    {
        return [
            'ui_component' => $content->globalContent?->ui_component ?? $this->componentFromField($content),
            'name' => $content->name,
            'slug' => $content->slug,
            'value' => $this->mapValue($content),
            'config' => [],
            'data' => [],

        ];
    }

    private function mapContentElement(Content $content): array
    {
        return [
            'ui_component' => $content->contentElement?->ui_component ?? $this->componentFromField($content),
            'name' => $content->name,
            'slug' => $content->slug,
            'value' => $this->mapValue($content),
            'config' => [],
            'data' => $content->children && $this->componentHasUiResource($content) === false
                ? $this->mapContentList($content->children)
                : [],
        ];
    }


    private function mapValue(Content $content)
    {
        if ($this->componentHasUiResource($content)) {
            $contentResource = $this->getUiResource($content);
            return new $contentResource($content);
        }

        return match ($content->data['fieldId'] ?? '') {
            'image', 'images' => collect($content->mediaUses)
                ->map(fn($mediaUse) => [
                    'id' => $mediaUse->id,
                    'src' => $mediaUse->getUrl('square-tile'),
                    'sizes' => $this->allSizesForUse($content, $mediaUse),
                    'alt' => $mediaUse->alt_text,
                    'caption' => $mediaUse->caption,
                    'title' => $mediaUse->title,
                    'hex1' => $mediaUse->media?->hex1,
                    'hex2' => $mediaUse->media?->hex2,
                ]),
            'form', 'form-field' => new FormViewResource(Form::query()
                ->with('sections.questions')
                ->where('id', $content->content)
                ->first()),
            'text', 'editor' => ShortcodeHelper::replaceShortcodes($content->content),
            default => $content->content
        };
    }

    private function componentHasUiResource(Content $content): bool
    {
        if (!empty($content->global_content_id)) {
            return !empty($content->globalContent->ui_component)
                && class_exists(self::UI_RESOURCE_PREFIX . $content->globalContent->ui_component);
        }

        return !empty($content->contentElement->ui_component)
            && class_exists(self::UI_RESOURCE_PREFIX . $content->contentElement->ui_component);
    }

    private function getUiResource(Content $content): string
    {
        return
            !empty($content->global_content_id)
                ? self::UI_RESOURCE_PREFIX . $content->globalContent->ui_component
                : self::UI_RESOURCE_PREFIX . $content->contentElement->ui_component;
    }

    private function componentFromField(Content $content)
    {
        return $content->data['uiComponent'] ?? $this->primitiveField($content);
    }

    private function primitiveField(Content $content)
    {
        return match ($content->data['fieldId'] ?? null) {
            'text-content' => 'UiArticleText',
            'text_area' => 'UiArticleText',
            'images' => 'UiArticleBanner',
            'image' => 'UiArticleBanner',
            default => null,
        };
    }

    private function fillSeo(): array
    {
        $seoData = $this->resource['seo'] ?? [];
        if (!empty($seoData['title']) && !empty($seoData['description'])) {
            return $seoData;
        }

        $data = SeoData::query()->where('path', $this->request->header('x-path'))->first();
        if ($data) {
            return $data->toArray();
        }

        $default = SeoDefault::query()->where('section', 'page')->first();
        if (!$default) {
            return $seoData;
        }

        if (empty($seoData['title'])) {
            $seoData['title'] = $this->replaceSeoTags([
                '{{TITLE}}' => $this->resource->title,
                '{{SITE_NAME}}' => Settings::get('app-name'),

            ], $default->title ?? $this->resource->title);
        }

        if (empty($seoData['description'])) {
            $seoData['description'] = $this->replaceSeoTags([
                '{{TITLE}}' => $this->resource->title,
                '{{SITE_NAME}}' => Settings::get('app-name'),
                '{{CONTENT_EXCERPT}}' => $this->resource->excerpt,

            ], $default->description ?? '');
        }
        return $seoData;
    }

    private function replaceSeoTags(array $replacements, string $string): string
    {
        return str_replace(array_keys($replacements), $replacements, $string);
    }

    private function allSizesForUse(Content $content, MediaUse $mediaUse): Collection
    {
        if (!isset($this->mediaSizes)) {
            $this->mediaSizes = MediaSize::query()
                ->where('model', $mediaUse->getOwnerType())
                ->get();
        }
        $sizes = collect($mediaUse->allowed_sizes ?? [])
            ->reject(fn($value, $key) => !is_numeric($key))
            ->keyBy(fn($size) => $size)
            ->map(fn($size) => $mediaUse->getUrl($size));
        return $this->mediaSizes
            ->keyBy('label')
            ->each(fn(MediaSize $size) => $mediaUse->owner_type = $this->getContentOwnerType())
            ->map(fn(MediaSize $size) => $mediaUse->getUrl($size->label))
            ->merge($sizes)
            ->put('original', $mediaUse->media->getOriginalUrlAttribute());
    }

    private function organiseContentInTree(Collection $entries, ?int $parent_id = null): Collection
    {
        $branch = collect();

        $entries->filter(fn($entry) => $entry->parent_id === $parent_id)
            ->each(function ($entry) use ($branch, $entries) {
                $children = $this->organiseContentInTree($entries, $entry->id);
                if ($children->isNotEmpty()) {
                    $entry['children'] = $children;
                }
                $branch[] = $entry;
            });

        return $branch;
    }

    protected function getContentOwnerType(): string
    {
        return 'content';
    }
}
