"""エンジンマニフェスト関連の処理"""

# マルチエンジン環境下においては、エンジンのバージョンがエディタのバージョンより
# 古くなる可能性が十分に考えられる。その場合、エディタ側がEngineManifestの情報不足によって
# エラーを吐いて表示が崩壊する可能性がある。これを防止するため、EngineManifest関連の定義を
# 変更する際は、Optionalにする必要があることに留意しなければならない。

import json
from base64 import b64encode
from dataclasses import asdict, dataclass
from pathlib import Path
from typing import TypeAlias

from pydantic import BaseModel, Field, TypeAdapter
from pydantic.json_schema import SkipJsonSchema


@dataclass(frozen=True)
class FeatureSupportJson:
    """`engine_manifest.json` の機能サポート状況"""

    type: str
    value: bool
    name: str


@dataclass(frozen=True)
class SupportedFeaturesJson:
    """`engine_manifest.json` のサポート機能一覧"""

    adjust_mora_pitch: FeatureSupportJson
    adjust_phoneme_length: FeatureSupportJson
    adjust_speed_scale: FeatureSupportJson
    adjust_pitch_scale: FeatureSupportJson
    adjust_intonation_scale: FeatureSupportJson
    adjust_volume_scale: FeatureSupportJson
    adjust_pause_length: FeatureSupportJson
    interrogative_upspeak: FeatureSupportJson
    synthesis_morphing: FeatureSupportJson
    sing: FeatureSupportJson
    manage_library: FeatureSupportJson
    return_resource_url: FeatureSupportJson


@dataclass(frozen=True)
class EngineManifestJson:
    """`engine_manifest.json` のコンテンツ"""

    manifest_version: str
    name: str
    brand_name: str
    uuid: str
    version: str
    url: str
    command: str
    port: int
    icon: str
    default_sampling_rate: int
    frame_rate: float
    terms_of_service: str
    update_infos: str
    dependency_licenses: str
    supported_features: SupportedFeaturesJson


_manifest_json_adapter = TypeAdapter(EngineManifestJson)


class UpdateInfo(BaseModel):
    """
    エンジンのアップデート情報
    """

    version: str = Field(description="エンジンのバージョン名")
    descriptions: list[str] = Field(description="アップデートの詳細についての説明")
    contributors: list[str] | SkipJsonSchema[None] = Field(
        default=None, description="貢献者名"
    )


class LicenseInfo(BaseModel):
    """
    依存ライブラリのライセンス情報
    """

    name: str = Field(description="依存ライブラリ名")
    version: str | SkipJsonSchema[None] = Field(
        default=None, description="依存ライブラリのバージョン"
    )
    license: str | SkipJsonSchema[None] = Field(
        default=None, description="依存ライブラリのライセンス名"
    )
    text: str = Field(description="依存ライブラリのライセンス本文")


class SupportedFeatures(BaseModel):
    """
    エンジンが持つ機能の一覧
    """

    adjust_mora_pitch: bool = Field(description="モーラごとの音高の調整")
    adjust_phoneme_length: bool = Field(description="音素ごとの長さの調整")
    adjust_speed_scale: bool = Field(description="全体の話速の調整")
    adjust_pitch_scale: bool = Field(description="全体の音高の調整")
    adjust_intonation_scale: bool = Field(description="全体の抑揚の調整")
    adjust_volume_scale: bool = Field(description="全体の音量の調整")
    adjust_pause_length: bool | SkipJsonSchema[None] = Field(
        default=None, description="句読点などの無音時間の調整"
    )
    interrogative_upspeak: bool = Field(description="疑問文の自動調整")
    synthesis_morphing: bool = Field(
        description="2種類のスタイルでモーフィングした音声を合成"
    )
    sing: bool | SkipJsonSchema[None] = Field(default=None, description="歌唱音声合成")
    manage_library: bool | SkipJsonSchema[None] = Field(
        default=None, description="音声ライブラリのインストール・アンインストール"
    )
    return_resource_url: bool | SkipJsonSchema[None] = Field(
        default=None, description="キャラクター情報のリソースを URL で返送"
    )


EngineName: TypeAlias = str
BrandName: TypeAlias = str


class EngineManifest(BaseModel):
    """
    エンジン自体に関する情報
    """

    manifest_version: str = Field(description="マニフェストのバージョン")
    name: EngineName = Field(description="エンジン名")
    brand_name: BrandName = Field(description="ブランド名")
    uuid: str = Field(description="エンジンのUUID")
    url: str = Field(description="エンジンのURL")
    icon: str = Field(description="エンジンのアイコンをBASE64エンコードしたもの")
    default_sampling_rate: int = Field(description="デフォルトのサンプリング周波数")
    frame_rate: float = Field(description="エンジンのフレームレート")
    terms_of_service: str = Field(description="エンジンの利用規約")
    update_infos: list[UpdateInfo] = Field(description="エンジンのアップデート情報")
    dependency_licenses: list[LicenseInfo] = Field(
        description="依存関係のライセンス情報"
    )
    supported_vvlib_manifest_version: str | SkipJsonSchema[None] = Field(
        default=None, description="エンジンが対応するvvlibのバージョン"
    )
    supported_features: SupportedFeatures = Field(description="エンジンが持つ機能")


def load_manifest(manifest_path: Path) -> EngineManifest:
    """エンジンマニフェストを指定ファイルから読み込む。"""

    root_dir = manifest_path.parent
    manifest = _manifest_json_adapter.validate_json(manifest_path.read_bytes())
    return EngineManifest(
        manifest_version=manifest.manifest_version,
        name=manifest.name,
        brand_name=manifest.brand_name,
        uuid=manifest.uuid,
        url=manifest.url,
        default_sampling_rate=manifest.default_sampling_rate,
        frame_rate=manifest.frame_rate,
        icon=b64encode((root_dir / manifest.icon).read_bytes()).decode("utf-8"),
        terms_of_service=(root_dir / manifest.terms_of_service).read_text("utf-8"),
        update_infos=[
            UpdateInfo(**update_info)
            for update_info in json.loads(
                (root_dir / manifest.update_infos).read_text("utf-8")
            )
        ],
        supported_vvlib_manifest_version=None,
        dependency_licenses=[
            LicenseInfo(**license_info)
            for license_info in json.loads(
                (root_dir / manifest.dependency_licenses).read_text("utf-8")
            )
        ],
        supported_features={
            key: item["value"]
            for key, item in asdict(manifest.supported_features).items()
        },
    )
