<?php

declare(strict_types=1);

final class MyronSeoFieldResolver
{
    private const SOURCES = ['explicit', 'auto', 'inherited', 'missing'];

    /**
     * @param array<string, mixed> $settings
     */
    public function __construct(private readonly array $settings = [])
    {
    }

    /**
     * @param array<string, mixed> $page
     * @return array{value:string, source:string}
     */
    public function resolve(array $page, string $field): array
    {
        $all = $this->resolveAll($page);
        return $all[$field] ?? ['value' => '', 'source' => 'missing'];
    }

    /**
     * @param array<string, mixed> $page
     * @return array<string, array{value:string, source:string}>
     */
    public function resolveAll(array $page): array
    {
        $pageTitle = $this->firstNonEmpty([$page['version_title'] ?? '', $page['title'] ?? '', $page['page_title'] ?? '']);
        $siteTitle = $this->siteSetting('site_title', function_exists('myronSiteTitle') ? myronSiteTitle() : 'MyronCMS');
        $path = (string) ($page['path'] ?? '/');
        $canonical = myronAppUrl() . myronBuildPublicPageUrl($path);
        $content = is_array($page['content'] ?? null) ? (array) $page['content'] : $this->decodeContent((string) ($page['content_json'] ?? '{}'));
        $excerpt = $this->firstNonEmpty([$content['excerpt'] ?? '', $content['summary'] ?? '']);
        $bodyWords = $this->firstWords($this->contentText($content), 25);
        $defaultDescription = $this->siteSetting('seo_default_description', '');
        $featuredImage = $this->firstNonEmpty([$content['featured_image'] ?? '', $content['image'] ?? '', $content['hero_image'] ?? '']);
        $defaultOgImage = $this->siteSetting('seo_default_og_image', '');

        $seoTitle = $this->tuple($page['seo_title'] ?? '', 'explicit');
        if ($seoTitle['value'] === '') {
            $autoTitle = $pageTitle !== '' ? $pageTitle : $siteTitle;
            $seoTitle = $this->tuple($autoTitle, $autoTitle !== '' ? 'auto' : 'missing');
        }

        $metaDescription = $this->tuple($page['meta_description'] ?? '', 'explicit');
        if ($metaDescription['value'] === '' && $excerpt !== '') {
            $metaDescription = $this->tuple($excerpt, 'auto');
        }
        if ($metaDescription['value'] === '' && $defaultDescription !== '') {
            $metaDescription = $this->tuple($defaultDescription, 'inherited');
        }
        if ($metaDescription['value'] === '' && $pageTitle !== '') {
            $metaDescription = $this->tuple($pageTitle, 'auto');
        }
        if ($metaDescription['value'] === '' && $bodyWords !== '') {
            $metaDescription = $this->tuple($bodyWords, 'auto');
        }
        if ($metaDescription['value'] === '' && $siteTitle !== '') {
            $metaDescription = $this->tuple($siteTitle, 'auto');
        }
        if ($metaDescription['value'] === '') {
            $metaDescription = $this->tuple('', 'missing');
        }

        $canonicalTuple = $this->tuple($page['canonical_override'] ?? '', 'explicit');
        if ($canonicalTuple['value'] === '') {
            $canonicalTuple = $this->tuple($canonical, 'auto');
        }

        $robotsTuple = $this->tuple($page['robots_override'] ?? '', 'explicit');
        if ($robotsTuple['value'] === '') {
            $robotsTuple = $this->tuple('index, follow', 'auto');
        }

        $ogTitle = $this->tuple($page['og_title'] ?? '', 'explicit');
        if ($ogTitle['value'] === '') {
            $ogTitle = $this->tuple($seoTitle['value'], $seoTitle['value'] !== '' ? 'inherited' : 'missing');
        }
        $ogDescription = $this->tuple($page['og_description'] ?? '', 'explicit');
        if ($ogDescription['value'] === '') {
            $ogDescription = $this->tuple($metaDescription['value'], $metaDescription['value'] !== '' ? 'inherited' : 'missing');
        }
        $ogImage = $this->tuple($page['og_image'] ?? '', 'explicit');
        if ($ogImage['value'] === '' && $featuredImage !== '') {
            $ogImage = $this->tuple($featuredImage, 'auto');
        }
        if ($ogImage['value'] === '' && $defaultOgImage !== '') {
            $ogImage = $this->tuple($defaultOgImage, 'inherited');
        }
        if ($ogImage['value'] === '') {
            $ogImage = $this->tuple('', 'missing');
        }

        $twitterTitle = $this->tuple($page['twitter_title'] ?? '', 'explicit');
        if ($twitterTitle['value'] === '') {
            $twitterTitle = $this->tuple($ogTitle['value'], $ogTitle['value'] !== '' ? 'inherited' : 'missing');
        }
        $twitterDescription = $this->tuple($page['twitter_description'] ?? '', 'explicit');
        if ($twitterDescription['value'] === '') {
            $twitterDescription = $this->tuple($ogDescription['value'], $ogDescription['value'] !== '' ? 'inherited' : 'missing');
        }
        $twitterImage = $this->tuple($page['twitter_image'] ?? '', 'explicit');
        if ($twitterImage['value'] === '') {
            $twitterImage = $this->tuple($ogImage['value'], $ogImage['value'] !== '' ? 'inherited' : 'missing');
        }

        return [
            'seo_title' => $seoTitle,
            'meta_description' => $metaDescription,
            'canonical' => $canonicalTuple,
            'robots' => $robotsTuple,
            'og_title' => $ogTitle,
            'og_description' => $ogDescription,
            'og_image' => $ogImage,
            'twitter_title' => $twitterTitle,
            'twitter_description' => $twitterDescription,
            'twitter_image' => $twitterImage,
        ];
    }

    /**
     * @return array{value:string, source:string}
     */
    private function tuple(mixed $value, string $source): array
    {
        $source = in_array($source, self::SOURCES, true) ? $source : 'missing';
        $value = trim((string) $value);
        return ['value' => $value, 'source' => $value !== '' ? $source : ($source === 'explicit' ? 'missing' : $source)];
    }

    private function siteSetting(string $key, string $default): string
    {
        return trim((string) (($this->settings[$key] ?? '') ?: $default));
    }

    /**
     * @param list<mixed> $candidates
     */
    private function firstNonEmpty(array $candidates): string
    {
        foreach ($candidates as $candidate) {
            $value = trim((string) $candidate);
            if ($value !== '') {
                return $value;
            }
        }
        return '';
    }

    /**
     * @return array<string, mixed>
     */
    private function decodeContent(string $json): array
    {
        try {
            $decoded = json_decode($json !== '' ? $json : '{}', true, 512, JSON_THROW_ON_ERROR);
            return is_array($decoded) ? $decoded : [];
        } catch (Throwable) {
            return [];
        }
    }

    /**
     * @param array<string, mixed> $content
     */
    private function contentText(array $content): string
    {
        $parts = [];
        array_walk_recursive($content, static function (mixed $value) use (&$parts): void {
            if (is_scalar($value)) {
                $parts[] = trim(strip_tags((string) $value));
            }
        });
        return trim(implode(' ', array_filter($parts)));
    }

    private function firstWords(string $text, int $limit): string
    {
        $words = preg_split('/\s+/', trim($text)) ?: [];
        $words = array_values(array_filter($words, static fn (string $word): bool => $word !== ''));
        return implode(' ', array_slice($words, 0, max(0, $limit)));
    }
}
