<?php

declare(strict_types=1);

namespace Acms\Services\Asset;

/**
 * 1件のJS/CSSアセット登録を表す値オブジェクト。
 */
final class AssetDefinition
{
    public const TYPE_SCRIPT = 'script';
    public const TYPE_STYLE = 'style';
    public const TYPE_INLINE_SCRIPT = 'inline-script';
    public const TYPE_INLINE_STYLE = 'inline-style';
    public const TYPE_RAW = 'raw';

    public const POSITION_HEAD = 'head';
    public const POSITION_BODY_END = 'body_end';

    /**
     * @param string[] $deps
     * @param array<string, string|bool> $attrs
     */
    private function __construct(
        public readonly string $handle,
        public readonly string $type,
        public readonly ?string $src,
        public readonly ?string $content,
        public readonly array $deps,
        public readonly string $position,
        public readonly array $attrs,
        public readonly ?string $version
    ) {
    }

    /**
     * @param array{deps?: string[], position?: string, attrs?: array<string, string|bool>, version?: ?string} $options
     */
    public static function script(string $handle, string $src, array $options = []): self
    {
        return self::createWithSrc($handle, self::TYPE_SCRIPT, $src, self::POSITION_BODY_END, $options);
    }

    /**
     * @param array{deps?: string[], position?: string, attrs?: array<string, string|bool>, version?: ?string} $options
     */
    public static function style(string $handle, string $src, array $options = []): self
    {
        return self::createWithSrc($handle, self::TYPE_STYLE, $src, self::POSITION_HEAD, $options);
    }

    /**
     * @param array{deps?: string[], position?: string, attrs?: array<string, string|bool>} $options
     */
    public static function inlineScript(string $handle, string $code, array $options = []): self
    {
        return self::createWithContent($handle, self::TYPE_INLINE_SCRIPT, $code, self::POSITION_BODY_END, $options);
    }

    /**
     * @param array{deps?: string[], position?: string, attrs?: array<string, string|bool>} $options
     */
    public static function inlineStyle(string $handle, string $code, array $options = []): self
    {
        return self::createWithContent($handle, self::TYPE_INLINE_STYLE, $code, self::POSITION_HEAD, $options);
    }

    /**
     * Vite の出力等、既に組み立て済みの HTML をそのまま配置するための脱出口。
     * $html はプラグイン作者が組み立てた信頼済みの文字列として扱う（エスケープしない）。
     *
     * @param array{deps?: string[], position?: string} $options
     */
    public static function raw(string $handle, string $html, array $options = []): self
    {
        return self::createWithContent($handle, self::TYPE_RAW, $html, self::POSITION_BODY_END, $options);
    }

    /**
     * @param array{deps?: string[], position?: string, attrs?: array<string, string|bool>, version?: ?string} $options
     */
    private static function createWithSrc(string $handle, string $type, string $src, string $defaultPosition, array $options): self
    {
        return new self(
            $handle,
            $type,
            $src,
            null,
            $options['deps'] ?? [],
            $options['position'] ?? $defaultPosition,
            $options['attrs'] ?? [],
            $options['version'] ?? null
        );
    }

    /**
     * @param array{deps?: string[], position?: string, attrs?: array<string, string|bool>} $options
     */
    private static function createWithContent(string $handle, string $type, string $content, string $defaultPosition, array $options): self
    {
        return new self(
            $handle,
            $type,
            null,
            $content,
            $options['deps'] ?? [],
            $options['position'] ?? $defaultPosition,
            $options['attrs'] ?? [],
            null
        );
    }
}
