#!/usr/bin/env python3

import datetime
import os
import shutil
import subprocess
import sys
import tempfile
from pathlib import Path


DEFAULT_CONFIG = """# Variables
submitter="Your Name"
email="your@email.address"
patches="patches@disconnected-by-peer.at"

# disabled/enabled
SUBMIT_PATCH="disabled"
mailprog="mail"
editorprog="mcedit"
COMPRESS="disabled"
compressprog="bzip2"
"""

ENV_HELP = """go-patch is not configured yet.

Add something like this to ~/.bashrc or ~/.bash_profile:

export GO_PATCH_SUBMITTER="Your Name"
export GO_PATCH_EMAIL="your@email.address"
export GO_PATCHES="patches@example.org"
export EDITOR="nano"

Optional:
export GO_PATCH_SUBMIT="disabled"
export GO_PATCH_MAILER="mail"
export GO_PATCH_COMPRESS="disabled"
export GO_PATCH_COMPRESSOR="bzip2"
"""

CONFIG_KEYS = (
    ("submitter", "Submitter name", "Your Name"),
    ("email", "Submitter email", "your@email.address"),
    ("patches", "Patch recipient", "patches@disconnected-by-peer.at"),
    ("editorprog", "Editor", os.environ.get("EDITOR") or os.environ.get("VISUAL") or "nano"),
    ("SUBMIT_PATCH", "Submit patches by mail", "disabled"),
    ("mailprog", "Mail program", "mail"),
    ("COMPRESS", "Compress mailed patches", "disabled"),
    ("compressprog", "Compressor", "bzip2"),
)


def load_config(path):
    config = {}
    if not path.exists():
        path.parent.mkdir(parents=True, exist_ok=True)
        path.write_text(DEFAULT_CONFIG)
    else:
        for line in path.read_text().splitlines():
            line = line.strip()
            if not line or line.startswith("#") or "=" not in line:
                continue
            key, value = line.split("=", 1)
            config[key.strip()] = value.strip().strip('"').strip("'")

    env_map = {
        "GO_PATCH_SUBMITTER": "submitter",
        "GO_PATCH_EMAIL": "email",
        "GO_PATCHES": "patches",
        "GO_PATCH_SUBMIT": "SUBMIT_PATCH",
        "GO_PATCH_MAILER": "mailprog",
        "GO_PATCH_COMPRESS": "COMPRESS",
        "GO_PATCH_COMPRESSOR": "compressprog",
        "EDITOR": "editorprog",
        "VISUAL": "editorprog",
    }
    for env_name, config_name in env_map.items():
        value = os.environ.get(env_name)
        if value:
            config[config_name] = value

    defaults = {
        "submitter": "Your Name",
        "email": "your@email.address",
        "patches": "patches@disconnected-by-peer.at",
        "SUBMIT_PATCH": "disabled",
        "mailprog": "mail",
        "editorprog": "mcedit",
        "COMPRESS": "disabled",
        "compressprog": "bzip2",
    }
    for key, value in defaults.items():
        config.setdefault(key, value)
    return config


def config_is_default(config):
    return (
        config.get("submitter") == "Your Name"
        or config.get("email") == "your@email.address"
        or not config.get("patches")
    )


def save_config(path, config):
    path.parent.mkdir(parents=True, exist_ok=True)
    path.write_text(
        "\n".join(
            [
                "# go-patch configuration",
                f"submitter={config['submitter']!r}",
                f"email={config['email']!r}",
                f"patches={config['patches']!r}",
                "",
                "# disabled/enabled",
                f"SUBMIT_PATCH={config['SUBMIT_PATCH']!r}",
                f"mailprog={config['mailprog']!r}",
                f"editorprog={config['editorprog']!r}",
                f"COMPRESS={config['COMPRESS']!r}",
                f"compressprog={config['compressprog']!r}",
                "",
            ]
        )
    )


def ask(prompt, default):
    value = input(f"{prompt} [{default}]: ").strip()
    return value or default


def configure(path, current=None):
    print("go-patch configuration wizard")
    print("Press Enter to keep the suggested value.\n")
    current = current or {}
    config = {}
    for key, prompt, fallback in CONFIG_KEYS:
        default = current.get(key) or fallback
        if key in ("SUBMIT_PATCH", "COMPRESS"):
            while True:
                value = ask(f"{prompt} (enabled/disabled)", default)
                if value in ("enabled", "disabled"):
                    break
                print("Please enter 'enabled' or 'disabled'.")
        else:
            value = ask(prompt, default)
        config[key] = value
    save_config(path, config)
    print(f"\nWrote {path}")
    return config


def obfuscate_email(email):
    return email.replace("@", " at ").replace(".", " dot ")


def run_editor(editor, path):
    editor_path = shutil.which(editor)
    if not editor_path:
        raise SystemExit(f"Editor not found: {editor}")
    subprocess.run([editor_path, str(path)], check=True)


def write_header(path, name, patch_type, version, config):
    date = datetime.date.today().isoformat()
    submitter = config.get("submitter", "Your Name")
    email = obfuscate_email(config.get("email", "your@email.address"))
    path.write_text(
        "\n".join(
            [
                f"Submitted By: {submitter} ({email})",
                f"Date: {date}",
                "Initial Package Version: ",
                "Origin: ",
                "Upstream Status: ",
                "Description: ",
                "",
            ]
        )
        + "\n"
    )


def append_diff(patch_file, target):
    original_dir = Path(f"{target}.orig")
    target_path = Path(target)
    with patch_file.open("a") as handle:
        if original_dir.exists():
            print(f"Creating patch from directory {target}...")
            subprocess.run(
                ["diff", "-Naur", str(original_dir), str(target_path)],
                stdout=handle,
                check=False,
                env={**os.environ, "LC_ALL": "C", "TZ": "UTC0"},
            )
            return

        for original in sorted(target_path.rglob("*.orig")):
            changed = Path(str(original)[: -len(".orig")])
            print(f"Creating patch from file {changed}...")
            subprocess.run(
                ["diff", "-Naur", str(original), str(changed)],
                stdout=handle,
                check=False,
                env={**os.environ, "LC_ALL": "C", "TZ": "UTC0"},
            )


def maybe_submit(patch_file, mail_text, name, config):
    if config.get("SUBMIT_PATCH") != "enabled":
        return
    mailprog = shutil.which(config.get("mailprog", "mail"))
    if not mailprog:
        raise SystemExit("Configured mail program not found")
    patches = config.get("patches", "")
    email = config.get("email", "")
    answer = input(f"Are you sure you want to send {patch_file.name} to {patches}? [yes/no] ")
    if answer.lower() not in ("y", "yes"):
        return

    attachment = patch_file
    if config.get("COMPRESS") == "enabled":
        compressor = shutil.which(config.get("compressprog", "bzip2"))
        if not compressor:
            raise SystemExit("Configured compressor not found")
        subprocess.run([compressor, str(patch_file)], check=True)
        attachment = Path(f"{patch_file}.bz2")

    subprocess.run(
        [mailprog, "-B", "-s", f"Patch Submission for {name}", "-a", str(attachment), "-r", email, patches],
        input=mail_text.read_text(),
        text=True,
        check=True,
    )


def usage():
    print("Info: go-utils 0.5.0")
    print("Usage: go-patch <directory> <description> [version]")
    print("       go-patch --configure")
    print("       go-patch --env-help")


def main(argv):
    config_path = Path.home() / ".go-patch"

    if len(argv) == 2 and argv[1] == "--configure":
        configure(config_path, load_config(config_path))
        return 0

    if len(argv) == 2 and argv[1] == "--env-help":
        print(ENV_HELP)
        return 0

    if len(argv) < 3:
        usage()
        return 1

    target, patch_type = argv[1], argv[2]
    version = argv[3] if len(argv) > 3 else "1"
    config = load_config(config_path)
    if config_is_default(config):
        if sys.stdin.isatty():
            config = configure(config_path, config)
        else:
            print(ENV_HELP)
            print("A template was also written to ~/.go-patch for compatibility.")
            return 1
    if config_is_default(config):
        print(ENV_HELP)
        return 1

    patch_file = Path(f"{target}-{patch_type}-{version}.patch")
    with tempfile.TemporaryDirectory(prefix="go-patch-") as tempdir:
        mail_text = Path(tempdir) / "patch.mail"
        write_header(patch_file, target, patch_type, version, config)
        run_editor(config.get("editorprog", "mcedit"), patch_file)
        shutil.copy2(patch_file, mail_text)
        append_diff(patch_file, target)
        maybe_submit(patch_file, mail_text, target, config)

    return 0


if __name__ == "__main__":
    sys.exit(main(sys.argv))
