"""Brand-aware build configuration helpers."""

from __future__ import annotations

import argparse
import json
from dataclasses import dataclass
from pathlib import Path
from typing import Any


@dataclass(frozen=True)
class BrandConfig:
    """Packaging metadata for a branded build."""

    brand_id: str
    display_name: str
    asset_prefix: str
    exe_name: str
    manufacturer: str
    install_dir_name: str
    shortcut_description: str
    bundle_identifier: str
    config_dir_name: str
    msi_upgrade_code: str
    update_channel: str
    icon_ico: Path
    icon_icns: Path
    dialog_bmp: Path
    banner_bmp: Path
    license_rtf: Path
    toolbar_icon_home: str
    toolbar_icon_reload: str
    toolbar_icon_open: str
    toolbar_icon_openwith: str

    def windows_installer_name(self, version: str) -> str:
        return f"{self.asset_prefix}-{version}-win-x64.msi"

    def macos_installer_name(self, version: str) -> str:
        return f"{self.asset_prefix}-{version}-macos-universal.dmg"

    @property
    def app_bundle_name(self) -> str:
        return f"{self.asset_prefix}.app"


DEFAULT_BRAND_VALUES: dict[str, Any] = {
    "brand_id": "webdrop_bridge",
    "display_name": "WebDrop Bridge",
    "asset_prefix": "WebDropBridge",
    "exe_name": "WebDropBridge",
    "manufacturer": "HIM-Tools",
    "install_dir_name": "WebDrop Bridge",
    "shortcut_description": "Web Drag-and-Drop Bridge",
    "bundle_identifier": "de.him_tools.webdrop-bridge",
    "config_dir_name": "webdrop_bridge",
    "msi_upgrade_code": "12345678-1234-1234-1234-123456789012",
    "update_channel": "stable",
    "icon_ico": "resources/icons/app.ico",
    "icon_icns": "resources/icons/app.icns",
    "dialog_bmp": "resources/icons/background.bmp",
    "banner_bmp": "resources/icons/banner.bmp",
    "license_rtf": "resources/license.rtf",
    "toolbar_icon_home": "resources/icons/home.ico",
    "toolbar_icon_reload": "resources/icons/reload.ico",
    "toolbar_icon_open": "resources/icons/open.ico",
    "toolbar_icon_openwith": "resources/icons/openwith.ico",
}

DEFAULT_BRAND_ID = str(DEFAULT_BRAND_VALUES["brand_id"])


def project_root() -> Path:
    return Path(__file__).resolve().parents[2]


def brands_dir(root: Path | None = None) -> Path:
    base = root or project_root()
    return base / "build" / "brands"


def available_brand_names(root: Path | None = None) -> list[str]:
    """Return all supported brand names, including the default build."""
    base = root or project_root()
    names = [DEFAULT_BRAND_ID]
    manifest_dir = brands_dir(base)
    if manifest_dir.exists():
        for manifest in sorted(manifest_dir.glob("*.json")):
            if manifest.stem not in names:
                names.append(manifest.stem)
    return names


def load_brand_config(
    brand: str | None = None,
    *,
    root: Path | None = None,
    manifest_path: Path | None = None,
) -> BrandConfig:
    """Load a brand manifest with defaults and asset fallbacks."""
    base = root or project_root()
    values = dict(DEFAULT_BRAND_VALUES)

    if manifest_path is None and brand and brand != DEFAULT_BRAND_ID:
        manifest_path = brands_dir(base) / f"{brand}.json"

    if manifest_path and manifest_path.exists():
        values.update(json.loads(manifest_path.read_text(encoding="utf-8")))
    elif manifest_path and not manifest_path.exists():
        raise FileNotFoundError(f"Brand manifest not found: {manifest_path}")

    def resolve_asset(key: str) -> Path:
        candidate = base / str(values.get(key, DEFAULT_BRAND_VALUES[key]))
        if candidate.exists():
            return candidate
        return base / str(DEFAULT_BRAND_VALUES[key])

    return BrandConfig(
        brand_id=str(values["brand_id"]),
        display_name=str(values["display_name"]),
        asset_prefix=str(values["asset_prefix"]),
        exe_name=str(values["exe_name"]),
        manufacturer=str(values["manufacturer"]),
        install_dir_name=str(values["install_dir_name"]),
        shortcut_description=str(values["shortcut_description"]),
        bundle_identifier=str(values["bundle_identifier"]),
        config_dir_name=str(values["config_dir_name"]),
        msi_upgrade_code=str(values["msi_upgrade_code"]),
        update_channel=str(values.get("update_channel", "stable")),
        icon_ico=resolve_asset("icon_ico"),
        icon_icns=resolve_asset("icon_icns"),
        dialog_bmp=resolve_asset("dialog_bmp"),
        banner_bmp=resolve_asset("banner_bmp"),
        license_rtf=resolve_asset("license_rtf"),
        toolbar_icon_home=str(
            values.get("toolbar_icon_home", DEFAULT_BRAND_VALUES["toolbar_icon_home"])
        ),
        toolbar_icon_reload=str(
            values.get("toolbar_icon_reload", DEFAULT_BRAND_VALUES["toolbar_icon_reload"])
        ),
        toolbar_icon_open=str(
            values.get("toolbar_icon_open", DEFAULT_BRAND_VALUES["toolbar_icon_open"])
        ),
        toolbar_icon_openwith=str(
            values.get("toolbar_icon_openwith", DEFAULT_BRAND_VALUES["toolbar_icon_openwith"])
        ),
    )


def generate_release_manifest(
    version: str,
    brands: list[str],
    *,
    output_path: Path,
    root: Path | None = None,
) -> Path:
    """Generate a shared release-manifest.json from local build outputs."""
    base = root or project_root()
    manifest: dict[str, Any] = {
        "version": version,
        "channel": "stable",
        "brands": {},
    }

    for brand_name in brands:
        brand = load_brand_config(brand_name, root=base)
        manifest["channel"] = brand.update_channel
        entries: dict[str, dict[str, str]] = {}

        windows_dir = base / "build" / "dist" / "windows" / brand.brand_id
        windows_installer = windows_dir / brand.windows_installer_name(version)
        windows_checksum = windows_dir / f"{windows_installer.name}.sha256"
        if windows_installer.exists():
            entries["windows-x64"] = {
                "installer": windows_installer.name,
                "checksum": windows_checksum.name if windows_checksum.exists() else "",
            }

        macos_dir = base / "build" / "dist" / "macos" / brand.brand_id
        macos_installer = macos_dir / brand.macos_installer_name(version)
        macos_checksum = macos_dir / f"{macos_installer.name}.sha256"
        if macos_installer.exists():
            entries["macos-universal"] = {
                "installer": macos_installer.name,
                "checksum": macos_checksum.name if macos_checksum.exists() else "",
            }

        if entries:
            manifest["brands"][brand.brand_id] = entries

    output_path.parent.mkdir(parents=True, exist_ok=True)
    output_path.write_text(json.dumps(manifest, indent=2), encoding="utf-8")
    return output_path


def merge_release_manifests(
    base_manifest: dict[str, Any], overlay_manifest: dict[str, Any]
) -> dict[str, Any]:
    """Merge two release manifests, preserving previously uploaded platforms."""
    merged: dict[str, Any] = {
        "version": overlay_manifest.get("version") or base_manifest.get("version", ""),
        "channel": overlay_manifest.get("channel") or base_manifest.get("channel", "stable"),
        "brands": dict(base_manifest.get("brands", {})),
    }

    for brand_id, entries in overlay_manifest.get("brands", {}).items():
        brand_entry = dict(merged["brands"].get(brand_id, {}))
        for platform_key, platform_value in entries.items():
            if platform_value:
                brand_entry[platform_key] = platform_value
        merged["brands"][brand_id] = brand_entry

    return merged


def collect_local_release_data(
    version: str,
    *,
    platform: str,
    root: Path | None = None,
    brands: list[str] | None = None,
) -> dict[str, Any]:
    """Collect local artifacts and manifest entries for the requested platform."""
    base = root or project_root()
    selected_brands = brands or available_brand_names(base)
    release_manifest: dict[str, Any] = {
        "version": version,
        "channel": "stable",
        "brands": {},
    }
    artifacts: list[str] = []
    found_brands: list[str] = []

    for brand_name in selected_brands:
        brand = load_brand_config(brand_name, root=base)
        release_manifest["channel"] = brand.update_channel

        if platform == "windows":
            artifact_dir = base / "build" / "dist" / "windows" / brand.brand_id
            installer = artifact_dir / brand.windows_installer_name(version)
            checksum = artifact_dir / f"{installer.name}.sha256"
            platform_key = "windows-x64"
        elif platform == "macos":
            artifact_dir = base / "build" / "dist" / "macos" / brand.brand_id
            installer = artifact_dir / brand.macos_installer_name(version)
            checksum = artifact_dir / f"{installer.name}.sha256"
            platform_key = "macos-universal"

            if not installer.exists() and brand.brand_id == DEFAULT_BRAND_ID:
                legacy_installer = (base / "build" / "dist" / "macos") / brand.macos_installer_name(
                    version
                )
                legacy_checksum = legacy_installer.parent / f"{legacy_installer.name}.sha256"
                if legacy_installer.exists():
                    installer = legacy_installer
                    checksum = legacy_checksum
        else:
            raise ValueError(f"Unsupported platform: {platform}")

        if not installer.exists():
            continue

        found_brands.append(brand.brand_id)
        artifacts.append(str(installer))
        if checksum.exists():
            artifacts.append(str(checksum))

        release_manifest["brands"].setdefault(brand.brand_id, {})[platform_key] = {
            "installer": installer.name,
            "checksum": checksum.name if checksum.exists() else "",
        }

    return {
        "version": version,
        "platform": platform,
        "brands": found_brands,
        "artifacts": artifacts,
        "manifest": release_manifest,
    }


def cli_env(args: argparse.Namespace) -> int:
    brand = load_brand_config(args.brand)
    assignments = {
        "WEBDROP_BRAND_ID": brand.brand_id,
        "WEBDROP_APP_DISPLAY_NAME": brand.display_name,
        "WEBDROP_ASSET_PREFIX": brand.asset_prefix,
        "WEBDROP_EXE_NAME": brand.exe_name,
        "WEBDROP_BUNDLE_ID": brand.bundle_identifier,
        "WEBDROP_CONFIG_DIR_NAME": brand.config_dir_name,
        "WEBDROP_UPDATE_CHANNEL": brand.update_channel,
        "WEBDROP_ICON_ICO": str(brand.icon_ico),
        "WEBDROP_ICON_ICNS": str(brand.icon_icns),
        "WEBDROP_TOOLBAR_ICON_HOME": brand.toolbar_icon_home,
        "WEBDROP_TOOLBAR_ICON_RELOAD": brand.toolbar_icon_reload,
        "WEBDROP_TOOLBAR_ICON_OPEN": brand.toolbar_icon_open,
        "WEBDROP_TOOLBAR_ICON_OPENWITH": brand.toolbar_icon_openwith,
    }
    for key, value in assignments.items():
        print(f'export {key}="{value}"')
    return 0


def cli_manifest(args: argparse.Namespace) -> int:
    output = generate_release_manifest(
        args.version,
        args.brands,
        output_path=Path(args.output).resolve(),
    )
    print(output)
    return 0


def cli_local_release_data(args: argparse.Namespace) -> int:
    data = collect_local_release_data(
        args.version,
        platform=args.platform,
        brands=args.brands,
    )
    print(json.dumps(data, indent=2))
    return 0


def cli_merge_manifests(args: argparse.Namespace) -> int:
    base_manifest = json.loads(Path(args.base).read_text(encoding="utf-8"))
    overlay_manifest = json.loads(Path(args.overlay).read_text(encoding="utf-8"))
    merged = merge_release_manifests(base_manifest, overlay_manifest)
    output_path = Path(args.output)
    output_path.write_text(json.dumps(merged, indent=2), encoding="utf-8")
    print(output_path)
    return 0


def cli_show(args: argparse.Namespace) -> int:
    brand = load_brand_config(args.brand)
    print(
        json.dumps(
            {
                "brand_id": brand.brand_id,
                "display_name": brand.display_name,
                "asset_prefix": brand.asset_prefix,
                "exe_name": brand.exe_name,
                "manufacturer": brand.manufacturer,
                "install_dir_name": brand.install_dir_name,
                "shortcut_description": brand.shortcut_description,
                "bundle_identifier": brand.bundle_identifier,
                "config_dir_name": brand.config_dir_name,
                "msi_upgrade_code": brand.msi_upgrade_code,
                "update_channel": brand.update_channel,
                "toolbar_icon_home": brand.toolbar_icon_home,
                "toolbar_icon_reload": brand.toolbar_icon_reload,
                "toolbar_icon_open": brand.toolbar_icon_open,
                "toolbar_icon_openwith": brand.toolbar_icon_openwith,
            },
            indent=2,
        )
    )
    return 0


def main() -> int:
    parser = argparse.ArgumentParser(description="Brand-aware build configuration")
    subparsers = parser.add_subparsers(dest="command", required=True)

    env_parser = subparsers.add_parser("env")
    env_parser.add_argument("--brand", required=True)
    env_parser.set_defaults(func=cli_env)

    manifest_parser = subparsers.add_parser("release-manifest")
    manifest_parser.add_argument("--version", required=True)
    manifest_parser.add_argument("--output", required=True)
    manifest_parser.add_argument("--brands", nargs="+", required=True)
    manifest_parser.set_defaults(func=cli_manifest)

    local_parser = subparsers.add_parser("local-release-data")
    local_parser.add_argument("--version", required=True)
    local_parser.add_argument("--platform", choices=["windows", "macos"], required=True)
    local_parser.add_argument("--brands", nargs="+")
    local_parser.set_defaults(func=cli_local_release_data)

    merge_parser = subparsers.add_parser("merge-manifests")
    merge_parser.add_argument("--base", required=True)
    merge_parser.add_argument("--overlay", required=True)
    merge_parser.add_argument("--output", required=True)
    merge_parser.set_defaults(func=cli_merge_manifests)

    show_parser = subparsers.add_parser("show")
    show_parser.add_argument("--brand", required=True)
    show_parser.set_defaults(func=cli_show)

    args = parser.parse_args()
    return args.func(args)


if __name__ == "__main__":
    raise SystemExit(main())
