#!/usr/bin/env python3
"""Remove every trace of a project, and nothing else.

    bin/project-delete <folder>            # say what would go, change nothing
    bin/project-delete <folder> --yes      # do it

What it removes, in this order:

  1. the TUIOS session for the project, and any <session>-git helper session
  2. the Paperclip project, with its workspace records
  3. the registry line in ~/.config/paperclip-harness/projects.toml
  4. the folder's own machinery: .git, .gitignore, .harness, .paperclip, .pi

What it keeps unless you also pass --delete-folder: the folder and every
ordinary file in it, so the work survives and can be adopted again.

This is the destructive one. `project-reset` is the gentle one: it clears the
panes and the worktrees and keeps the Paperclip history.
"""

import json
import os
import shutil
import subprocess
import sys

HERE = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, os.path.join(HERE, "lib"))
import harness as h  # noqa: E402
import project as pj  # noqa: E402
import tuios  # noqa: E402

MACHINERY = [".git", ".gitignore", ".harness", ".paperclip", ".pi"]


def parse(argv):
    opts = {"folder": "", "yes": False, "delete_folder": False}
    for a in argv:
        if a == "--yes" or a == "-y":
            opts["yes"] = True
        elif a == "--delete-folder":
            opts["delete_folder"] = True
        elif not a.startswith("-"):
            opts["folder"] = a
    return opts


def sessions_for(session):
    """The project's session and the helper session office may have made."""
    have = tuios.sessions()
    out = []
    for name in (session, session + "-git"):
        if name in have:
            out.append(name)
    return out


def drop_registry(project_id):
    """Remove one project's block from the registry, and nothing else."""
    path = pj.REGISTRY
    if not os.path.exists(path) or not project_id:
        return False
    keep, skip = [], False
    with open(path) as fh:
        for line in fh:
            if line.startswith("[projects."):
                skip = project_id in line
            if not skip:
                keep.append(line)
    with open(path, "w") as fh:
        fh.writelines(keep)
    return True


def main():
    opts = parse(sys.argv[1:])
    if not opts["folder"]:
        h.say(__doc__.strip())
        return 2
    folder = os.path.abspath(os.path.expanduser(opts["folder"]))
    if not os.path.isdir(folder):
        raise h.Refused(2, "no folder at %s" % folder)

    cfg = {}
    if pj.exists(folder):
        try:
            cfg = pj.load(folder)
        except Exception:
            # A half-written project file must not stop the removal: it is one
            # of the things being removed.
            cfg = {}
    session = cfg.get("session") or os.path.basename(folder)
    project_id = (cfg.get("paperclip") or {}).get("project", "")
    project_name = (cfg.get("paperclip") or {}).get("name", "")

    # Every project that claims this folder, not just the one the project file
    # remembers. Adopting the same folder twice leaves more than one behind, and
    # deleting only the last one leaves the rest pointing at a folder that has
    # been cleared out.
    projects = []
    if project_id:
        projects.append((project_id, project_name))
    try:
        for p in h.api("GET", "/api/companies/%s/projects" % h.COMPANY):
            for w in (p.get("workspaces") or []):
                if (w.get("cwd") or "").rstrip("/") == folder.rstrip("/"):
                    pair = (p.get("id"), p.get("name"))
                    if pair[0] and pair[0] not in [x[0] for x in projects]:
                        projects.append(pair)
                    break
    except Exception:
        pass

    doomed_sessions = sessions_for(session)
    dotfiles = [d for d in MACHINERY if os.path.exists(os.path.join(folder, d))]

    h.say("folder   %s" % folder)
    if projects:
        for pid, pname in projects:
            h.say("project  %s  %s" % (pname or "(no name)", pid))
    else:
        h.say("project  (none found)")
    h.say("sessions %s" % (", ".join(doomed_sessions) if doomed_sessions else "(none)"))
    h.say("files    %s" % (", ".join(dotfiles) if dotfiles else "(none)"))
    h.say("keeps    every ordinary file in the folder%s"
          % ("" if not opts["delete_folder"] else "  — EXCEPT the folder itself, --delete-folder"))
    if not (doomed_sessions or projects or dotfiles or opts["delete_folder"]):
        h.say("")
        h.say("nothing to remove.")
        return 0

    if not opts["yes"]:
        h.say("")
        h.say("nothing was changed. Add --yes to do it.")
        return 0

    # 1. sessions
    for name in doomed_sessions:
        out = subprocess.run(["tuios", "kill-session", name],
                             capture_output=True, text=True, timeout=120)
        h.say("  killed session %s%s" % (name, "" if out.returncode == 0 else " (refused)"))

    # 2. the Paperclip projects
    for pid, pname in projects:
        try:
            h.api("DELETE", "/api/projects/%s" % pid)
            h.say("  deleted the Paperclip project %s" % (pname or pid))
        except h.Refused as exc:
            h.say("  could not delete %s: %s" % (pname or pid, exc))

    # 3. the registry
    for pid, _ in projects:
        if drop_registry(pid):
            h.say("  removed %s from %s" % (pid, pj.REGISTRY))

    # 4. the folder's machinery
    for name in dotfiles:
        path = os.path.join(folder, name)
        if os.path.isdir(path) and not os.path.islink(path):
            shutil.rmtree(path, ignore_errors=True)
        else:
            try:
                os.remove(path)
            except OSError:
                pass
        h.say("  removed %s" % os.path.join(folder, name))

    # 5. the folder itself, only when asked
    if opts["delete_folder"]:
        h.say("")
        h.say("This removes the folder and every file in it: %s" % folder)
        if sys.stdin.isatty():
            typed = input("  type the folder name to confirm: ").strip()
            if typed != os.path.basename(folder):
                raise h.Refused(2, "not confirmed, nothing further was removed")
        shutil.rmtree(folder, ignore_errors=True)
        h.say("  removed the folder")

    h.say("")
    h.say("done.")
    return 0


if __name__ == "__main__":
    h.main_guard(main)
