#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import argparse
import hashlib
import json
import os
import sys
from pathlib import Path
from typing import Optional


IGNORED_TOP_LEVEL_DIRS = {
    "Cache",
    "Errors",
    "Logs",
    "Screenshots",
    "WTF",
}
IGNORED_DIRS_ANYWHERE = {
    ".git",
    ".moonwell_launcher",
}
IGNORED_TOP_LEVEL_FILES = {
    "Wow.moonwell-patched.backup.exe",
    "WarcraftXL_on12.disable",
}

ENV_VAR_NAME = "WOW_HOME"


def sha256_bytes(data: bytes) -> str:
    return hashlib.sha256(data).hexdigest()


def sha256_file(path: Path, chunk_size: int = 1024 * 1024) -> str:
    h = hashlib.sha256()
    with path.open("rb") as f:
        while chunk := f.read(chunk_size):
            h.update(chunk)
    return h.hexdigest()


def is_ignored(rel_path: Path) -> bool:
    if not rel_path.parts:
        return False
    if rel_path.parts[0] in IGNORED_TOP_LEVEL_DIRS:
        return True
    if len(rel_path.parts) == 1 and rel_path.name in IGNORED_TOP_LEVEL_FILES:
        return True
    return any(part in IGNORED_DIRS_ANYWHERE for part in rel_path.parts[:-1])


def load_dotenv(env_path: Path) -> dict[str, str]:
    values: dict[str, str] = {}

    if not env_path.exists():
        return values

    for raw_line in env_path.read_text(encoding="utf-8").splitlines():
        line = raw_line.strip()

        if not line or line.startswith("#") or "=" not in line:
            continue

        key, value = line.split("=", 1)
        key = key.strip()
        value = value.strip()

        if key.startswith("export "):
            key = key.removeprefix("export ").strip()

        if not key:
            continue

        if len(value) >= 2 and value[0] == value[-1] and value[0] in {"'", '"'}:
            value = value[1:-1]

        values[key] = value

    return values


def resolve_root(explicit_dir: Optional[str]) -> Optional[Path]:
    if explicit_dir:
        return Path(explicit_dir).resolve()

    env_value = os.environ.get(ENV_VAR_NAME)
    if env_value:
        return Path(env_value).expanduser().resolve()

    project_root = Path(__file__).resolve().parent
    dotenv_values = load_dotenv(project_root / ".env")
    env_value = dotenv_values.get(ENV_VAR_NAME)

    if env_value:
        os.environ.setdefault(ENV_VAR_NAME, env_value)
        return Path(env_value).expanduser().resolve()

    return None


def compute_build_hash(files: list[dict]) -> str:
    lines = [
        f'{item["path"]}:{item["size"]}:{item["sha256"]}'
        for item in sorted(files, key=lambda x: x["path"])
    ]
    payload = "\n".join(lines).encode("utf-8")
    return sha256_bytes(payload)


def main() -> int:
    parser = argparse.ArgumentParser(description="Collect files into manifest.json")
    parser.add_argument(
        "--dir",
        help=f"Directory to scan. Defaults to {ENV_VAR_NAME} from env or .env",
    )
    parser.add_argument("--output", default="manifest.json", help="Output file")
    args = parser.parse_args()

    root = resolve_root(args.dir)
    output = Path(args.output).resolve()

    if root is None:
        print(
            f"ERROR: directory is not set. Pass --dir or define {ENV_VAR_NAME} in env/.env",
            file=sys.stderr,
        )
        return 1

    if not root.exists() or not root.is_dir():
        print(f"ERROR: directory not found: {root}", file=sys.stderr)
        return 1

    files = []

    for file_path in sorted(root.rglob("*")):
        if not file_path.is_file():
            continue

        if file_path.resolve() == output:
            continue

        rel_path_obj = file_path.relative_to(root)

        if is_ignored(rel_path_obj):
            continue

        rel_path = rel_path_obj.as_posix()
        files.append({
            "path": rel_path,
            "size": file_path.stat().st_size,
            "sha256": sha256_file(file_path),
        })

    build_hash = compute_build_hash(files)

    manifest = {
        "buildHash": build_hash,
        "files": files
    }

    with output.open("w", encoding="utf-8", newline="\n") as f:
        json.dump(manifest, f, ensure_ascii=False, indent=2)
        f.write("\n")

    print(f"OK: written {output}")
    print(f"Files: {len(files)}")
    print(f"Build hash: {build_hash}")
    return 0


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