#!/usr/bin/env python3
import tomllib
from pathlib import Path
import shutil
from enum import Enum


CONFIG_FILE = Path(__file__).parent / "config.toml"


class ToolType(str, Enum):
    FILE = "file"
    DIR = "dir"


def parse_dotfiles_toml(path: Path):
    """
    Parse a dotfiles TOML and return a flat list of entries.
    Rules:
      - Every entry (top-level or nested) must have a 'type' (file or dir)
      - Nested entries belong to the same tool
    Returns a list of dicts with keys: tool, repo_path, system_path, type, ...
    """
    with open(path, "rb") as f:
        cfg = tomllib.load(f)

    all_entries = []
    tools = cfg.get("tools", {})

    def process_table(tool_name, table):
        """
        Process a tool table: flatten nested entries, enforce 'type'.
        """
        # If table itself has 'type', treat it as a single entry
        if "type" in table:
            if table["type"] not in ("file", "dir"):
                raise ValueError(f"Invalid type for tool '{tool_name}': {table['type']}")
            entry = dict(table)
            entry["tool"] = tool_name
            all_entries.append(entry)
            return  # stop recursion here

        # Otherwise recurse into nested tables
        for key, value in table.items():
            if isinstance(value, dict):
                process_table(tool_name, value)
            else:
                raise ValueError(f"Invalid entry in tool '{tool_name}': {key}={value}")

    for tool_name, tool_data in tools.items():
        if not isinstance(tool_data, dict):
            raise ValueError(f"Tool '{tool_name}' must be a table")
        process_table(tool_name, tool_data)

    return all_entries


def filter_entries(entries, targets=None):
    if not targets:
        return entries

    available_tools = {e["tool"] for e in entries}
    unknown = [t for t in targets if t not in available_tools]
    if unknown:
        raise ValueError(f"Unknown tool(s) specified: {', '.join(unknown)}")

    return [e for e in entries if e["tool"] in targets]


def install(entries):
    """
    Install all entries from repo_path → system_path.
    If system_path exists, move it to .old before overwriting.
    """
    for entry in entries:
        repo_path = Path(entry["repo_path"]).expanduser()
        system_path = Path(entry["system_path"]).expanduser()
        backup_path = system_path.with_name(system_path.name + ".old")

        # Backup existing file/folder
        if system_path.exists():
            if backup_path.exists():
                shutil.rmtree(backup_path) if backup_path.is_dir() else backup_path.unlink()
            system_path.rename(backup_path)

        # Copy from repo to system
        if entry["type"] == "dir":
            shutil.copytree(repo_path, system_path)
        else:  # file
            system_path.parent.mkdir(parents=True, exist_ok=True)
            shutil.copy2(repo_path, system_path)


def store(entries):
    """
    Store all entries: system_path → repo_path (strict sync).
    - For directories: remove repo folder first
    - For files: overwrite
    """
    for entry in entries:
        repo_path = Path(entry["repo_path"]).expanduser()
        system_path = Path(entry["system_path"]).expanduser()

        if entry["type"] == "dir":
            if repo_path.exists():
                shutil.rmtree(repo_path)
            shutil.copytree(system_path, repo_path)
        else:  # file
            repo_path.parent.mkdir(parents=True, exist_ok=True)
            shutil.copy2(system_path, repo_path)


def restore(entries):
    """
    Restore all entries from .old → system_path.
    """
    for entry in entries:
        system_path = Path(entry["system_path"]).expanduser()
        backup_path = system_path.with_name(system_path.name + ".old")

        if backup_path.exists():
            if system_path.exists():
                if system_path.is_dir():
                    shutil.rmtree(system_path)
                else:
                    system_path.unlink()
            backup_path.rename(system_path)


def clean(entries):
    """
    Remove all .old backups created during install.
    """
    for entry in entries:
        system_path = Path(entry["system_path"]).expanduser()
        backup_path = system_path.with_name(system_path.name + ".old")

        if backup_path.exists():
            if backup_path.is_dir():
                shutil.rmtree(backup_path)
            else:
                backup_path.unlink()


if __name__ == "__main__":
    import argparse

    parser = argparse.ArgumentParser(description="Manage dotfiles from TOML config")
    parser.add_argument(
        "action",
        choices=["install", "store", "restore", "clean"],
        help="Action to perform"
    )
    parser.add_argument(
        "targets",
        nargs="*",
        help="Tools to act on (default: all)"
    )

    args = parser.parse_args()

    entries = parse_dotfiles_toml(CONFIG_FILE)
    entries = filter_entries(entries, args.targets)

    if args.action == "install":
        install(entries)
    elif args.action == "store":
        store(entries)
    elif args.action == "restore":
        restore(entries)
    elif args.action == "clean":
        clean(entries)
