<?php

namespace App\Jobs;

use App\Imports\Blog\BlogArticleData;
use App\Imports\Blog\BlogAuthorResolver;
use DOMDocument;
use Exception;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Log;
use Mtc\ContentManager\Contracts\PageModel;
use Mtc\ContentManager\Facades\Media;
use Mtc\ContentManager\Facades\Page;
use Mtc\ContentManager\Models\ContentCategory;
use Mtc\ContentManager\Models\MediaFolder;
use Mtc\MercuryDataModels\SeoData;
use Mtc\MercuryDataModels\Template;

class BaseArticleImport implements ShouldQueue
{
    use Dispatchable;
    use InteractsWithQueue;
    use Queueable;
    use SerializesModels;

    public function __construct(
        private readonly BlogArticleData $data,
        private readonly string $mediaFolderName = 'Blog Images',
    ) {
        //
    }

    public function handle(): void
    {
        try {
            $template = Template::query()
                ->where('slug', $this->data->templateSlug)
                ->first();

            if (!$template) {
                Log::error('BaseArticleImport: Template "' . $this->data->templateSlug . '" not found');
                return;
            }

            $mediaFolder = MediaFolder::query()->firstOrCreate(['name' => $this->mediaFolderName]);

            $authorId = app(BlogAuthorResolver::class)->resolveByName($this->data->authorName);

            $page = app(PageModel::class)->updateOrCreate([
                'slug' => $this->data->slug,
            ], array_filter([
                'template_id' => $template->id,
                'title' => html_entity_decode($this->data->title),
                'status' => $this->data->status,
                'published_at' => $this->parsePublishedAt(),
                'category' => $this->data->pageCategory,
                'author_id' => $authorId,
            ], fn ($value) => $value !== null));

            Page::syncContentWithTemplate($page->id);
            $this->syncCategories($page);
            $this->updatePageSeo($page);

            $featuredImage = $this->importFeaturedImage($page, $mediaFolder);

            $internalDomains = $this->getInternalDomains();

            $bodyContent = $this->processContentImages(
                $this->data->body ?? '',
                $mediaFolder
            );
            $bodyContent = $this->simplifyInternalLinks($bodyContent, $internalDomains);

            $this->mapContentToTemplate($page, $bodyContent, $featuredImage, $mediaFolder, $internalDomains);
        } catch (Exception $exception) {
            Log::error('BaseArticleImport: ' . $exception->getMessage(), [
                'slug' => $this->data->slug,
            ]);
        }
    }

    private function parsePublishedAt(): ?Carbon
    {
        if (empty($this->data->publishedAt)) {
            return null;
        }

        if (!empty($this->data->dateFormat)) {
            try {
                $parsed = Carbon::createFromFormat($this->data->dateFormat, $this->data->publishedAt);
                if ($parsed !== false) {
                    return $parsed;
                }
            } catch (Exception $e) {
                // Fall through to Carbon::parse fallback
            }
        }

        try {
            return Carbon::parse($this->data->publishedAt);
        } catch (Exception $e) {
            Log::warning('BaseArticleImport: Could not parse publishedAt, leaving null', [
                'value' => $this->data->publishedAt,
                'format' => $this->data->dateFormat,
                'slug' => $this->data->slug,
            ]);
            return null;
        }
    }

    private function importFeaturedImage(
        PageModel $page,
        MediaFolder $mediaFolder
    ): ?\Mtc\ContentManager\Contracts\Media {
        if (empty($this->data->featuredImageUrl)) {
            return null;
        }

        try {
            $featuredImage = Media::importImageFromUrl($this->data->featuredImageUrl);
            $featuredImage->update(['folder_id' => $mediaFolder->id]);

            if ($page->mediaUses()->exists() === false) {
                Media::setUsesForModel([$featuredImage->id], $page, ['primary' => true]);
            }

            return $featuredImage;
        } catch (Exception $e) {
            Log::error('BaseArticleImport: Failed to import featured image', [
                'url' => $this->data->featuredImageUrl,
                'error' => $e->getMessage(),
            ]);
        }

        return null;
    }

    private function mapContentToTemplate(
        PageModel $page,
        string $bodyContent,
        ?\Mtc\ContentManager\Contracts\Media $featuredImage,
        MediaFolder $mediaFolder,
        array $internalDomains
    ): void {
        if (!empty($this->data->contentElementValues)) {
            $this->applyContentElementMapping($page, $mediaFolder, $internalDomains);
            return;
        }

        $this->applyNameBasedContentMapping($page, $bodyContent, $featuredImage);
    }

    private function applyContentElementMapping(
        PageModel $page,
        MediaFolder $mediaFolder,
        array $internalDomains
    ): void {
        $page->loadMissing('content.contentElement.fields');

        foreach ($page->content as $content) {
            $fieldsByTemplateElement = $this->data->contentElementValues[$content->template_element_id] ?? null;
            if (!$fieldsByTemplateElement) {
                continue;
            }

            foreach ($content->subContent as $subContent) {
                if (!array_key_exists($subContent->slug, $fieldsByTemplateElement)) {
                    continue;
                }

                $value = $fieldsByTemplateElement[$subContent->slug];
                if ($value === null || $value === '') {
                    continue;
                }

                $fieldType = $content->contentElement?->fields
                    ->firstWhere('slug', $subContent->slug)
                    ?->field_type;

                if ($fieldType === 'image') {
                    $this->applyImageValue($subContent, $value, $mediaFolder);
                    continue;
                }

                if (in_array($fieldType, ['editor', 'text'], true)) {
                    $value = $this->cleanRichText($value);
                    $value = $this->simplifyInternalLinks($value, $internalDomains);
                }

                $subContent->content = $value;
                $subContent->save();
            }
        }
    }

    private function cleanRichText(string $value): string
    {
        // Strip standalone separator lines like "---", "***", "___" (3+ chars)
        $value = preg_replace('/^[ \t]*([-*_])\1{2,}[ \t]*$/m', '', $value);
        // Strip leading markdown heading markers (#, ##, ### …) keeping the heading text
        $value = preg_replace('/^[ \t]*#+[ \t]+/m', '', $value);
        // Collapse 3+ consecutive newlines into 2
        $value = preg_replace("/(\r?\n){3,}/", "\n\n", $value);

        return trim($value);
    }

    /**
     * @return string[]
     */
    private function getInternalDomains(): array
    {
        $tenant = tenant();
        if (!$tenant) {
            return [];
        }

        return $tenant->domains->pluck('domain')->all();
    }

    /**
     * @param string[] $internalDomains
     */
    private function simplifyInternalLinks(string $html, array $internalDomains): string
    {
        if (empty($internalDomains) || !str_contains($html, '<a')) {
            return $html;
        }

        $doc = new DOMDocument();
        libxml_use_internal_errors(true);
        $doc->loadHTML(
            '<?xml encoding="UTF-8">' . $html,
            LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD
        );
        libxml_clear_errors();

        foreach ($doc->getElementsByTagName('a') as $link) {
            $href = $link->getAttribute('href');
            if ($href === '') {
                continue;
            }
            $simplified = $this->stripInternalDomain($href, $internalDomains);
            if ($simplified !== $href) {
                $link->setAttribute('href', $simplified);
            }
        }

        $output = $doc->saveHTML();
        return str_replace('<?xml encoding="UTF-8">', '', $output);
    }

    /**
     * @param string[] $internalDomains
     */
    private function stripInternalDomain(string $url, array $internalDomains): string
    {
        $host = parse_url($url, PHP_URL_HOST);
        if (!$host) {
            return $url;
        }

        $hostStripped = strtolower(preg_replace('/^www\./i', '', $host));
        foreach ($internalDomains as $domain) {
            $domainStripped = strtolower(preg_replace('/^www\./i', '', $domain));
            if ($hostStripped !== $domainStripped) {
                continue;
            }

            $path = parse_url($url, PHP_URL_PATH) ?: '/';
            $query = parse_url($url, PHP_URL_QUERY);
            $fragment = parse_url($url, PHP_URL_FRAGMENT);

            return $path
                . ($query ? '?' . $query : '')
                . ($fragment ? '#' . $fragment : '');
        }

        return $url;
    }

    private function applyImageValue(
        Model $subContent,
        string $url,
        MediaFolder $mediaFolder
    ): void {
        try {
            $image = Media::importImageFromUrl($url);
            $image->update(['folder_id' => $mediaFolder->id]);
            Media::setUsesForModel([$image->id], $subContent);
            $subContent->content = '<img src="' . $image->original_url . '"/>';
            $subContent->save();
        } catch (Exception $e) {
            Log::error('BaseArticleImport: Failed to import mapped image', [
                'url' => $url,
                'slug' => $subContent->slug,
                'error' => $e->getMessage(),
            ]);
        }
    }

    private function applyNameBasedContentMapping(
        PageModel $page,
        string $bodyContent,
        ?\Mtc\ContentManager\Contracts\Media $featuredImage
    ): void {
        $contentSet = false;
        $excerptSet = false;

        foreach ($page->content as $content) {
            foreach ($content->subContent as $subContent) {
                $name = strtoupper($subContent->name);

                if (
                    !$contentSet
                    && in_array($name, ['TEXT', 'DESCRIPTION', 'BODY', 'BOTTOM DESCRIPTION'])
                ) {
                    $subContent->content = $bodyContent;
                    $subContent->save();
                    $contentSet = true;
                } elseif (
                    !$excerptSet
                    && in_array($name, ['EXCERPT', 'SUMMARY', 'TOP DESCRIPTION'])
                ) {
                    $subContent->content = $this->data->excerpt ?? '';
                    $subContent->save();
                    $excerptSet = true;
                } elseif (in_array($name, ['IMAGE', 'MAIN IMAGE'])) {
                    if ($featuredImage) {
                        Media::setUsesForModel([$featuredImage->id], $subContent);
                        $subContent->content = '<img src="' . $featuredImage->original_url . '"/>';
                        $subContent->save();
                    }
                }
            }
        }
    }

    private function syncCategories(PageModel $page): void
    {
        if (empty($this->data->categories)) {
            return;
        }

        $categoryIds = [];
        foreach ($this->data->categories as $cat) {
            $category = ContentCategory::firstOrCreate(
                ['slug' => $cat['slug']],
                ['name' => $cat['name']],
            );
            $categoryIds[] = $category->id;
        }

        $page->categories()->sync($categoryIds);
    }

    private function updatePageSeo(PageModel $page): void
    {
        if (empty($this->data->seoTitle) && empty($this->data->seoDescription)) {
            return;
        }

        SeoData::query()->updateOrCreate([
            'path' => $page->urlPath(),
        ], array_filter([
            'title' => $this->data->seoTitle ?: $page->title,
            'description' => $this->data->seoDescription,
        ]));
    }

    private function processContentImages(string $html, MediaFolder $mediaFolder): string
    {
        if (empty($html)) {
            return '';
        }

        $doc = new DOMDocument();
        libxml_use_internal_errors(true);
        $doc->loadHTML(
            '<?xml encoding="UTF-8">' . $html,
            LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD
        );
        libxml_clear_errors();

        $images = $doc->getElementsByTagName('img');
        $importedUrls = [];

        foreach ($images as $img) {
            $src = $img->getAttribute('src');

            if (empty($src) || stripos($src, 'base64') !== false) {
                continue;
            }

            if (isset($importedUrls[$src])) {
                $img->setAttribute('src', $importedUrls[$src]);
                continue;
            }

            try {
                $newImage = Media::importImageFromUrl(str_replace(' ', '%20', $src));
                \Mtc\ContentManager\Models\Media::query()
                    ->where('id', $newImage->id)
                    ->update(['folder_id' => $mediaFolder->id]);

                $importedUrls[$src] = $newImage->original_url;
                $img->setAttribute('src', $newImage->original_url);
            } catch (Exception $e) {
                Log::error('BaseArticleImport: Failed to import inline image', [
                    'url' => $src,
                    'error' => $e->getMessage(),
                ]);
            }
        }

        $output = $doc->saveHTML();
        $output = str_replace('<?xml encoding="UTF-8">', '', $output);

        return $output;
    }
}
