from __future__ import annotations

import argparse
import hashlib
import importlib.resources as resources
import json
import os
import shutil
from pathlib import Path
from importlib.metadata import PackageNotFoundError, version


MANIFEST_NAME = "install.json"
PACKAGE_NAME = "divami-agents"
MANAGED_DIR = ".github"
MANIFEST_FILE = ".divami-agents.json"


def file_hash(path: Path) -> str:
    return hashlib.sha256(path.read_bytes()).hexdigest()


def package_version() -> str:
    try:
        return version("divami-agents")
    except PackageNotFoundError:
        return "0.0.0+local"


def manifest_path(repo_dir: Path) -> Path:
    return repo_dir / MANIFEST_FILE


def load_manifest(repo_dir: Path) -> dict | None:
    path = manifest_path(repo_dir)
    if not path.exists():
        return None
    return json.loads(path.read_text())


def write_manifest(
    repo_dir: Path,
    managed_dir: Path,
    files: list[str],
    previous: dict | None = None,
    preserved: list[str] | None = None,
) -> None:
    preserved = preserved or []
    managed_files = {}
    for rel in sorted(files):
        if previous and rel in preserved:
            managed_files[rel] = previous["managed_files"][rel]
        else:
            managed_files[rel] = {"sha256": file_hash(managed_dir / rel)}
    manifest = {
        "package": PACKAGE_NAME,
        "installed_version": package_version(),
        "managed_files": managed_files,
    }
    path = manifest_path(repo_dir)
    path.write_text(json.dumps(manifest, indent=2) + "\n")


def classify_file(agents_dir: Path, rel: str, manifest: dict | None) -> str:
    path = agents_dir / rel
    if not path.exists():
        return "new"
    if not manifest:
        return "unmanaged"
    recorded = manifest["managed_files"].get(rel)
    if not recorded:
        return "unmanaged"
    if file_hash(path) == recorded["sha256"]:
        return "replace"
    return "modified"


def sync_tree(src: Path, dst: Path, manifest: dict | None, base: Path | None = None) -> tuple[list[str], list[str]]:
    synced: list[str] = []
    conflicts: list[str] = []
    base = dst if base is None else base
    dst.mkdir(parents=True, exist_ok=True)
    for item in src.iterdir():
        target = dst / item.name
        if item.is_dir():
            child_synced, child_conflicts = sync_tree(item, target, manifest, base)
            synced.extend(child_synced)
            conflicts.extend(child_conflicts)
        elif classify_file(base, str(target.relative_to(base)), manifest) in {"new", "replace"}:
            shutil.copy2(item, target)
            synced.append(str(target.relative_to(base)))
        else:
            conflicts.append(str(target.relative_to(base)))
    return synced, conflicts


def ensure_symlink(target: Path, link_path: Path) -> None:
    if link_path.exists() or link_path.is_symlink():
        return
    rel_target = os.path.relpath(target, start=link_path.parent)
    link_path.symlink_to(rel_target)


def run_setup() -> None:
    cwd = Path.cwd()
    managed_dir = cwd / MANAGED_DIR
    existing = load_manifest(cwd)
    if managed_dir.exists() and not existing and any(managed_dir.iterdir()):
        raise SystemExit(
            f"Found an existing {MANAGED_DIR} directory without install metadata. "
            "Refusing to modify unmanaged files."
        )
    if existing:
        print(
            f"Found {existing['package']} {existing['installed_version']}, "
            f"target is {package_version()}"
        )
    template_dir = resources.files("divami_agents").joinpath(f"templates/{MANAGED_DIR}")
    with resources.as_file(template_dir) as materialized:
        files, conflicts = sync_tree(Path(materialized), managed_dir, existing)
    tracked = set(existing["managed_files"]) if existing else set()
    tracked.update(files)
    if tracked:
        preserved = [rel for rel in conflicts if existing and rel in existing["managed_files"]]
        write_manifest(cwd, managed_dir, sorted(tracked), existing, preserved)
    if conflicts:
        print(f"Skipped modified files: {', '.join(conflicts)}")
    print(f"Installed {MANAGED_DIR}")


def main() -> None:
    parser = argparse.ArgumentParser(prog="divami-agents")
    subparsers = parser.add_subparsers(dest="command", required=True)
    subparsers.add_parser("setup")
    args = parser.parse_args()
    if args.command == "setup":
        run_setup()


if __name__ == "__main__":
    main()
