"""Packaging utilities for Mira."""

from __future__ import annotations

import os
import platform
import zipfile

from mira.crypto import encrypt_zip
import uuid


class MiraPacker:
    """Pack a build directory into a .mira archive."""

    def pack(
        self,
        build_dir: str,
        output_dir: str,
        name: str,
        version: str,
        encryption_key: str | None = None,
        cipher: str | None = None,
    ) -> str:
        """Create a .mira archive.

        Args:
            build_dir: Build directory to pack.
            output_dir: Output directory for the archive.
            name: Package name.
            version: Package version.
            encryption_key: Optional encryption key to protect the archive.
            cipher: Optional cipher name for encryption.

        Returns:
            Path to the created .mira file.
        """
        os.makedirs(output_dir, exist_ok=True)

        os_name = self._normalize_os(platform.system())
        arch = self._normalize_arch(platform.machine())
        major = platform.python_version_tuple()[0]
        minor = platform.python_version_tuple()[1]

        filename = f"{name}-{version}-py{major}{minor}-{os_name}-{arch}.mira"
        archive_path = os.path.join(output_dir, filename)

        if encryption_key:
            temp_zip_path = os.path.join(
                output_dir, f".mira_tmp_{uuid.uuid4().hex}.zip"
            )
            try:
                self._write_zip(build_dir, temp_zip_path)
                encrypt_zip(
                    temp_zip_path,
                    archive_path,
                    encryption_key,
                    cipher=cipher or "hmac-sha256-stream",
                )
            finally:
                if os.path.exists(temp_zip_path):
                    os.remove(temp_zip_path)
        else:
            self._write_zip(build_dir, archive_path)

        return archive_path

    @staticmethod
    def _write_zip(build_dir: str, output_path: str) -> None:
        """Write build contents into a zip file."""
        with zipfile.ZipFile(
            output_path,
            "w",
            compression=zipfile.ZIP_DEFLATED,
            compresslevel=9,
        ) as archive:
            for root, _, files in os.walk(build_dir):
                for file in files:
                    full_path = os.path.join(root, file)
                    rel_path = os.path.relpath(full_path, build_dir)
                    archive.write(full_path, rel_path.replace(os.sep, "/"))

    @staticmethod
    def _normalize_os(os_name: str) -> str:
        """Normalize OS names to stable identifiers used in .mira filenames."""
        os_name_lower = os_name.lower()
        if os_name_lower.startswith("win"):
            return "windows"
        if os_name_lower.startswith("darwin") or os_name_lower.startswith("mac"):
            return "macos"
        return "linux"

    @staticmethod
    def _normalize_arch(arch: str) -> str:
        """Normalize CPU architecture names to common identifiers."""
        arch_lower = arch.lower()
        if arch_lower in {"amd64", "x86_64"}:
            return "x86_64"
        if arch_lower in {"x86", "i386", "i686"}:
            return "x86"
        if arch_lower in {"arm64", "aarch64"}:
            return "arm64"
        return arch_lower
