"""Tests for the Mira builder."""

from __future__ import annotations

import json
import os
import zipfile

import pytest

from mira.builder.builder import MiraBuilder
from mira.library.resolver import PackageResolver
from mira.signature import generate_keypair, verify_signature
from tests.helpers import temporary_directory


def _create_project(base_dir: str) -> str:
    project_dir = os.path.join(base_dir, "sample_project")
    os.makedirs(project_dir, exist_ok=True)

    pyproject = """
[project]
name = "my_engine"
version = "1.0.0"
description = "My awesome engine"
dependencies = []

[tool.mira]
include = "engine"
entry_point = "engine.main:run"
exclude = ["tests", "docs", "*.test.py"]
copy_extra = ["assets/", "config.yaml"]
""".strip()

    with open(os.path.join(project_dir, "pyproject.toml"), "w", encoding="utf-8") as handle:
        handle.write(pyproject)

    engine_dir = os.path.join(project_dir, "engine")
    os.makedirs(engine_dir, exist_ok=True)

    with open(os.path.join(engine_dir, "__init__.py"), "w", encoding="utf-8") as handle:
        handle.write("VALUE = 42\n")

    with open(os.path.join(engine_dir, "core.py"), "w", encoding="utf-8") as handle:
        handle.write("def answer():\n    return 42\n")

    assets_dir = os.path.join(project_dir, "assets")
    os.makedirs(assets_dir, exist_ok=True)
    with open(os.path.join(assets_dir, "data.txt"), "w", encoding="utf-8") as handle:
        handle.write("payload")

    with open(os.path.join(project_dir, "config.yaml"), "w", encoding="utf-8") as handle:
        handle.write("enabled: true\n")

    return project_dir


def test_build_creates_mira_archive() -> None:
    with temporary_directory() as tmp:
        project_dir = _create_project(tmp)
        builder = MiraBuilder(project_dir, output_dir="dist")
        archive_path = builder.build()

        assert os.path.isfile(archive_path)
        with zipfile.ZipFile(archive_path, "r") as archive:
            names = archive.namelist()
            assert "manifest.json" in names
            manifest = json.loads(archive.read("manifest.json").decode("utf-8"))
            assert manifest["name"] == "my_engine"
            assert "engine/__init__.pyc" in names


def test_build_creates_encrypted_archive() -> None:
    with temporary_directory() as tmp:
        project_dir = _create_project(tmp)
        builder = MiraBuilder(project_dir, output_dir="dist")
        mira_path = builder.build(
            encryption_key="secret-key", cipher="hmac-sha256-stream"
        )

        assert os.path.isfile(mira_path)
        assert not zipfile.is_zipfile(mira_path)

        resolver = PackageResolver()
        manifest = resolver.read_manifest(mira_path, key="secret-key")
        assert manifest["name"] == "my_engine"


def test_build_creates_aes_encrypted_archive() -> None:
    pytest.importorskip("cryptography")
    with temporary_directory() as tmp:
        project_dir = _create_project(tmp)
        builder = MiraBuilder(project_dir, output_dir="dist")
        mira_path = builder.build(encryption_key="secret-key", cipher="aes-256-gcm")

        assert os.path.isfile(mira_path)
        assert not zipfile.is_zipfile(mira_path)


def test_build_signs_manifest() -> None:
    pytest.importorskip("cryptography")
    private_key, public_key = generate_keypair()
    with temporary_directory() as tmp:
        project_dir = _create_project(tmp)
        builder = MiraBuilder(project_dir, output_dir="dist")
        mira_path = builder.build(signing_key=private_key, embed_public_key=True)

        with zipfile.ZipFile(mira_path, "r") as archive:
            manifest = json.loads(archive.read("manifest.json").decode("utf-8"))
            signature = json.loads(archive.read("signature.json").decode("utf-8"))
        verify_signature(manifest, signature, public_key_pem=public_key)
