<?php

declare(strict_types=1);

require_once __DIR__ . '/PublicHeroImagePolicy.php';

final class MyronPublicCriticalResourceResolver
{
    public const HERO_RENDITION = MyronPublicHeroImagePolicy::SPLIT_RENDITION;
    public const HERO_SIZES = MyronPublicHeroImagePolicy::SPLIT_SIZES;

    /**
     * @var array<string, int>
     */
    public const HERO_SRCSET_RENDITIONS = MyronPublicHeroImagePolicy::SRCSET_RENDITIONS;

    /**
     * @param callable(string):?array<string,mixed>|null $fileResolver
     */
    public function __construct(private $fileResolver = null)
    {
    }

    /**
     * @param array<string, mixed> $page
     * @return array{href:string,imagesrcset:string,imagesizes:string,width:int|null,height:int|null,fileId:string,source:string}|null
     */
    public function primaryHeroImage(array $page): ?array
    {
        $content = is_array($page['content'] ?? null) ? (array) $page['content'] : [];
        $schema = is_array($page['template_schema'] ?? null) ? (array) $page['template_schema'] : [];
        $sections = is_array($schema['sections'] ?? null) ? (array) $schema['sections'] : [];

        foreach ($sections as $position => $section) {
            if (!is_array($section)) {
                continue;
            }

            $componentId = trim((string) ($section['componentId'] ?? ''));
            if (in_array($componentId, ['header', 'footer'], true)) {
                continue;
            }
            if (!in_array($componentId, ['hero-simple', 'hero-with-cta'], true)) {
                return null;
            }

            $sectionContent = myronSectionContentForInstance($content, $section, (int) $position);
            $fileId = trim((string) ($sectionContent['mediaFileId'] ?? ''));
            if ($fileId === '') {
                return null;
            }

            $variant = trim((string) ($section['variant'] ?? ''));
            $mediaLayout = MyronPublicHeroImagePolicy::mediaLayoutForSection($section);

            return $this->imageCandidateForFileId($fileId, $componentId, $variant, $mediaLayout);
        }

        return null;
    }

    /**
     * @return array{href:string,imagesrcset:string,imagesizes:string,width:int|null,height:int|null,fileId:string,source:string}|null
     */
    private function imageCandidateForFileId(string $fileId, string $source, string $variant, string $mediaLayout = 'auto'): ?array
    {
        try {
            $file = $this->resolveFile($fileId);
        } catch (Throwable) {
            return null;
        }
        if (!is_array($file) || !($file['is_image'] ?? false)) {
            return null;
        }

        $policy = MyronPublicHeroImagePolicy::forSection($source, $variant, $mediaLayout);
        $rendition = (string) $policy['rendition'];
        $href = self::imageUrlForRendition($file, $rendition);
        if ($href === '' || !self::isLocalPreloadHref($href)) {
            return null;
        }

        $dimensions = self::imageDimensionsForRendition($file, $rendition);

        return [
            'href' => $href,
            'imagesrcset' => self::imageSrcset($file, $policy['srcset']),
            'imagesizes' => (string) $policy['sizes'],
            'width' => $dimensions['width'] ?? null,
            'height' => $dimensions['height'] ?? null,
            'fileId' => $fileId,
            'source' => $source,
        ];
    }

    /**
     * @return array<string, mixed>|null
     */
    private function resolveFile(string $fileId): ?array
    {
        if (is_callable($this->fileResolver)) {
            $file = ($this->fileResolver)($fileId);
            return is_array($file) ? $file : null;
        }

        return myronFileRepository()->getFileById($fileId);
    }

    /**
     * @param array<string, mixed> $file
     */
    public static function imageUrlForRendition(array $file, ?string $rendition): string
    {
        $rendition = $rendition !== null ? trim($rendition) : '';
        $urls = is_array($file['image_rendition_urls'] ?? null) ? (array) $file['image_rendition_urls'] : [];
        if ($rendition !== '' && isset($urls[$rendition]) && trim((string) $urls[$rendition]) !== '') {
            return (string) $urls[$rendition];
        }

        return (string) (($file['revisioned_file_url'] ?? '') ?: ($file['file_url'] ?? ''));
    }

    /**
     * @param array<string, mixed> $file
     * @param array<string, int> $renditions
     */
    public static function imageSrcset(array $file, array $renditions): string
    {
        if ($renditions === []) {
            return '';
        }

        $items = [];
        foreach ($renditions as $preset => $width) {
            $preset = trim((string) $preset);
            $width = (int) $width;
            if ($preset === '' || $width <= 0) {
                continue;
            }

            $url = self::imageUrlForRendition($file, $preset);
            if ($url === '') {
                continue;
            }

            $items[] = $url . ' ' . self::imageEffectiveRenditionWidth($file, $width) . 'w';
        }

        return implode(', ', array_values(array_unique($items)));
    }

    /**
     * @param array<string, mixed> $file
     */
    public static function imageEffectiveRenditionWidth(array $file, int $targetWidth): int
    {
        $originalWidth = (int) ($file['image_width'] ?? 0);
        if ($originalWidth <= 0) {
            return $targetWidth;
        }

        return max(1, min($originalWidth, $targetWidth));
    }

    /**
     * @param array<string, mixed> $file
     * @return array{width:int,height:int}|null
     */
    public static function imageDimensionsForRendition(array $file, ?string $rendition): ?array
    {
        $width = (int) ($file['image_width'] ?? 0);
        $height = (int) ($file['image_height'] ?? 0);
        if ($width <= 0 || $height <= 0) {
            return null;
        }

        $targetWidth = MyronPublicHeroImagePolicy::renditionWidth($rendition) ?? $width;
        if ($targetWidth >= $width) {
            return ['width' => $width, 'height' => $height];
        }

        return [
            'width' => $targetWidth,
            'height' => max(1, (int) round(($targetWidth / $width) * $height)),
        ];
    }

    public static function isLocalPreloadHref(string $href): bool
    {
        $href = trim($href);
        return $href !== '' && str_starts_with($href, '/') && !str_starts_with($href, '//');
    }
}
