from __future__ import annotations

import os
import re
import subprocess
from pathlib import Path

from rich.console import Console
from rich.text import Text

from sunwaee.core.logger import get_logger

_log = get_logger("core.git")
_console = Console()

_DIFF_MAX_LINES = 120
_DIFF_EXTENDED_PREFIXES = (
    "diff --git ",
    "index ",
    "new file mode ",
    "deleted file mode ",
    "old mode ",
    "new mode ",
    "similarity index ",
    "rename from ",
    "rename to ",
    "Binary files ",
)
_HUNK_RE = re.compile(r"^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@")

_GITIGNORE = "logs/\ngen/sessions/\n"
_GIT_ENV = {
    "GIT_AUTHOR_NAME": "SUNWÆE",
    "GIT_AUTHOR_EMAIL": "sunwaee@local",
    "GIT_COMMITTER_NAME": "SUNWÆE",
    "GIT_COMMITTER_EMAIL": "sunwaee@local",
}


def _run(args: list[str], cwd: Path) -> subprocess.CompletedProcess:
    return subprocess.run(
        args,
        cwd=cwd,
        capture_output=True,
        text=True,
        env={**os.environ, **_GIT_ENV},
    )


def ensure_git_repo(ws_dir: Path) -> None:
    """Git-init the workspace directory if it isn't already a repo. Updates .gitignore otherwise."""
    if (ws_dir / ".git").exists():
        _ensure_gitignore_entries(ws_dir)
        return

    _log.info("Initializing git repo at %s", ws_dir)
    ws_dir.mkdir(parents=True, exist_ok=True)
    _run(["git", "init"], cwd=ws_dir)

    gitignore = ws_dir / ".gitignore"
    if not gitignore.exists():
        gitignore.write_text(_GITIGNORE)

    _run(["git", "add", ".gitignore"], cwd=ws_dir)
    _run(["git", "commit", "-m", "init: workspace"], cwd=ws_dir)


def _ensure_gitignore_entries(ws_dir: Path) -> None:
    """Add any missing required entries to an existing .gitignore."""
    gitignore = ws_dir / ".gitignore"
    current = gitignore.read_text() if gitignore.exists() else ""
    required = [line for line in _GITIGNORE.splitlines() if line]
    missing = [e for e in required if e not in current.splitlines()]
    if not missing:
        return
    gitignore.write_text(current.rstrip("\n") + "\n" + "\n".join(missing) + "\n")
    _run(["git", "add", ".gitignore"], cwd=ws_dir)
    _run(["git", "commit", "-m", "chore: update .gitignore"], cwd=ws_dir)


def ws_commit(ws_dir: Path, message: str) -> str:
    """Stage all changes, capture diff, commit. Returns the unified diff string."""
    _run(["git", "add", "-A"], cwd=ws_dir)
    diff = _run(["git", "diff", "--cached"], cwd=ws_dir).stdout
    result = _run(["git", "commit", "-m", message], cwd=ws_dir)
    if result.returncode != 0:
        _log.warning("git commit failed: %s", result.stderr.strip())
    return diff


def render_diff(diff: str) -> None:
    """Render a unified git diff to the terminal with syntax highlighting."""
    all_lines = diff.splitlines()

    old_line = new_line = shown = 0
    break_idx = len(all_lines)

    for i, raw in enumerate(all_lines):
        if shown >= _DIFF_MAX_LINES:
            break_idx = i
            break

        if raw.startswith(_DIFF_EXTENDED_PREFIXES):
            continue

        if raw.startswith("--- "):
            continue

        if raw.startswith("+++ "):
            filename = re.sub(r"^b/", "", raw[4:])
            _console.print()
            _console.print(f"  [bold]{filename}[/bold]")
            continue

        if raw.startswith("@@"):
            m = _HUNK_RE.match(raw)
            if m:
                old_line = int(m.group(1))
                new_line = int(m.group(2))
            _console.print("  [dim]     ...[/dim]")
            shown += 1
            continue

        if raw.startswith("+"):
            t = Text(f"  {new_line:>4} +", style="on #1a3a1a")
            t.append(raw[1:], style="on #1a3a1a")
            t.append(" " * max(0, _console.width - len(t.plain)), style="on #1a3a1a")
            _console.print(t)
            new_line += 1
            shown += 1
            continue

        if raw.startswith("-"):
            t = Text(f"  {old_line:>4} -", style="on #3a1a1a")
            t.append(raw[1:], style="on #3a1a1a")
            t.append(" " * max(0, _console.width - len(t.plain)), style="on #3a1a1a")
            _console.print(t)
            old_line += 1
            shown += 1
            continue

        # context line
        t = Text(f"  {new_line:>4}  ", style="dim")
        t.append(raw[1:])
        _console.print(t)
        old_line += 1
        new_line += 1
        shown += 1

    remaining = sum(
        1
        for l in all_lines[break_idx:]
        if not l.startswith(_DIFF_EXTENDED_PREFIXES) and not l.startswith(("--- ", "+++ "))
    )
    if remaining > 0:
        _console.print(f"  [dim]… {remaining} more lines[/dim]")
    _console.print()


_DEMO_DIFF = """\
diff --git a/sunwaee/core/example.py b/sunwaee/core/example.py
index 1234567..89abcde 100644
--- a/sunwaee/core/example.py
+++ b/sunwaee/core/example.py
@@ -1,10 +1,12 @@
 from __future__ import annotations

-import os
+import os
+import sys
 from pathlib import Path


-def greet(name: str) -> None:
-    print(f"Hello, {name}")
+def greet(name: str, loud: bool = False) -> None:
+    msg = f"Hello, {name}"
+    print(msg.upper() if loud else msg)


 def farewell(name: str) -> None:
diff --git a/sunwaee/core/new_file.py b/sunwaee/core/new_file.py
new file mode 100644
index 0000000..aaaaaaa
--- /dev/null
+++ b/sunwaee/core/new_file.py
@@ -0,0 +1,4 @@
+from __future__ import annotations
+
+
+CONSTANT = 42
"""


if __name__ == "__main__":
    render_diff(_DEMO_DIFF)
