#!/usr/bin/env python3
"""
TAS GitHub Integration Script
GitHub PR operations for tas-review-pr command.

Usage: python tools/tas-github.py <command> [arguments]

Commands:
  pr-get    <pr-id>
  pr-diff   <pr-id>
  pr-comment <pr-id> --comment <text>
  pr-inline  <pr-id> --file <path> --line <n> --comment <text>
  pr-vote    <pr-id> --vote <approve|reject|wait-for-author|reset>

Prerequisites:
  - GitHub CLI (gh) installed and authenticated: gh auth login
  - Python 3.8+
"""

import argparse
import json
import os
import re
import subprocess
import sys
import tempfile
import urllib.error
import urllib.request
from pathlib import Path


# --- Helpers ---

def find_repo_root():
    path = Path.cwd()
    while path != path.parent:
        if (path / ".git").exists():
            return path
        path = path.parent
    print("ERROR: Not inside a git repository.")
    sys.exit(1)


def get_remote_url(root):
    result = subprocess.run(
        ["git", "remote", "get-url", "origin"],
        capture_output=True,
        cwd=str(root),
    )
    if result.returncode != 0:
        print("ERROR: Cannot get git remote URL. Run inside a git repo with 'origin' remote.")
        sys.exit(1)
    return result.stdout.decode("utf-8").strip()


def parse_owner_repo(remote_url):
    """Extract owner/repo from GitHub remote URL (https or ssh)."""
    # ssh: git@github.com:owner/repo.git
    m = re.search(r"github\.com[:/](.+?)(?:\.git)?$", remote_url)
    if m:
        return m.group(1).rstrip("/")
    print(f"ERROR: Cannot parse owner/repo from remote URL: {remote_url}")
    sys.exit(1)


def get_github_token():
    result = subprocess.run(
        ["gh", "auth", "token"],
        capture_output=True,
    )
    if result.returncode != 0:
        print("ERROR: gh CLI not authenticated. Run: gh auth login")
        sys.exit(1)
    return result.stdout.decode("utf-8").strip()


def gh_cmd(args):
    """Run gh CLI command and return parsed JSON output."""
    cmd = ["gh"] + args + ["--output", "json"] if "--output" not in args else ["gh"] + args
    result = subprocess.run(cmd, capture_output=True)

    def _decode(b):
        try:
            return (b or b"").decode("utf-8")
        except UnicodeDecodeError:
            return (b or b"").decode("cp1252", errors="replace")

    if result.returncode != 0:
        print(f"ERROR: gh command failed:\n{_decode(result.stderr)}")
        sys.exit(1)
    stdout = _decode(result.stdout).strip()
    if not stdout:
        return {}
    json_start = re.search(r"[{\[]", stdout)
    if json_start:
        try:
            return json.loads(stdout[json_start.start():])
        except json.JSONDecodeError:
            return {}
    return {}


def github_rest(method, path, data, token):
    """Call GitHub REST API using token auth."""
    import base64
    url = f"https://api.github.com{path}"
    bearer = f"Bearer {token}"
    body = json.dumps(data).encode("utf-8")
    req = urllib.request.Request(url, data=body, method=method)
    req.add_header("Authorization", bearer)
    req.add_header("Content-Type", "application/json")
    req.add_header("Accept", "application/vnd.github+json")
    req.add_header("X-GitHub-Api-Version", "2022-11-28")
    try:
        with urllib.request.urlopen(req) as resp:
            return json.loads(resp.read().decode("utf-8"))
    except urllib.error.HTTPError as e:
        err_body = e.read().decode("utf-8")
        print(f"ERROR: GitHub REST API {method} {path} failed ({e.code}): {err_body}")
        sys.exit(1)


# --- PR Commands ---

def cmd_pr_get(args):
    root = find_repo_root()
    remote_url = get_remote_url(root)
    owner_repo = parse_owner_repo(remote_url)

    result = gh_cmd([
        "pr", "view", str(args.pr_id),
        "--repo", owner_repo,
        "--json", "number,title,body,headRefName,baseRefName,author,state,headRefOid,url",
    ])

    print(f"PR_ID: {result.get('number', args.pr_id)}")
    print(f"TITLE: {result.get('title', '')}")
    print(f"DESCRIPTION: {result.get('body', '')}")
    print(f"SOURCE_BRANCH: {result.get('headRefName', '')}")
    print(f"TARGET_BRANCH: {result.get('baseRefName', '')}")
    author = result.get("author", {})
    print(f"CREATOR: {author.get('login', '') if isinstance(author, dict) else str(author)}")
    print(f"STATUS: {result.get('state', '')}")
    print(f"REPO_ID: {owner_repo}")
    print(f"REPO_NAME: {owner_repo.split('/')[-1]}")
    print(f"HEAD_COMMIT: {result.get('headRefOid', '')}")
    print(f"WORK_ITEMS: ")  # GitHub has no native work item link (use PR body parsing if needed)


def cmd_pr_diff(args):
    root = find_repo_root()
    remote_url = get_remote_url(root)
    owner_repo = parse_owner_repo(remote_url)

    result = gh_cmd([
        "pr", "view", str(args.pr_id),
        "--repo", owner_repo,
        "--json", "headRefName,baseRefName,headRefOid",
    ])

    source_branch = result.get("headRefName", "")
    target_branch = result.get("baseRefName", "")
    fetch_head = result.get("headRefOid", "")

    print(f"SOURCE_BRANCH: {source_branch}")
    print(f"TARGET_BRANCH: {target_branch}")

    fetch_result = subprocess.run(
        ["git", "fetch", "origin", source_branch],
        capture_output=True,
        cwd=str(root),
    )
    if fetch_result.returncode != 0:
        warn = (fetch_result.stderr or b"").decode("utf-8", errors="replace")
        print(f"WARNING: git fetch failed: {warn}")

    if not fetch_head:
        head_result = subprocess.run(
            ["git", "rev-parse", "FETCH_HEAD"],
            capture_output=True,
            cwd=str(root),
        )
        fetch_head = head_result.stdout.decode("utf-8").strip() if head_result.returncode == 0 else ""

    print(f"FETCH_HEAD: {fetch_head}")

    diff_result = subprocess.run(
        ["git", "diff", "--name-status", f"origin/{target_branch}...FETCH_HEAD"],
        capture_output=True,
        cwd=str(root),
    )
    if diff_result.returncode == 0:
        print("CHANGED_FILES:")
        print(diff_result.stdout.decode("utf-8", errors="replace").strip())
    else:
        warn = (diff_result.stderr or b"").decode("utf-8", errors="replace")
        print(f"WARNING: git diff failed: {warn}")


def cmd_pr_comment(args):
    root = find_repo_root()
    remote_url = get_remote_url(root)
    owner_repo = parse_owner_repo(remote_url)

    tmp = tempfile.NamedTemporaryFile(mode="w", suffix=".md", delete=False, encoding="utf-8")
    tmp.write(args.comment)
    tmp.close()
    try:
        result = subprocess.run(
            ["gh", "pr", "comment", str(args.pr_id),
             "--repo", owner_repo,
             "--body-file", tmp.name],
            capture_output=True,
        )
        if result.returncode != 0:
            err = (result.stderr or b"").decode("utf-8", errors="replace")
            print(f"ERROR: gh pr comment failed: {err}")
            sys.exit(1)
        print(f"Posted comment on PR #{args.pr_id}")
    finally:
        try:
            os.unlink(tmp.name)
        except OSError:
            pass


def cmd_pr_inline(args):
    root = find_repo_root()
    remote_url = get_remote_url(root)
    owner_repo = parse_owner_repo(remote_url)
    token = get_github_token()

    # Get HEAD commit for the PR (required by GitHub inline comment API)
    result = gh_cmd([
        "pr", "view", str(args.pr_id),
        "--repo", owner_repo,
        "--json", "headRefOid",
    ])
    commit_id = result.get("headRefOid", "")
    if not commit_id:
        print("ERROR: Could not get PR head commit ID")
        sys.exit(1)

    file_path = args.file_path.replace("\\", "/").lstrip("/")

    data = {
        "body": args.comment,
        "commit_id": commit_id,
        "path": file_path,
        "line": args.line,
        "side": "RIGHT",
    }
    resp = github_rest("POST", f"/repos/{owner_repo}/pulls/{args.pr_id}/comments", data, token)
    comment_id = resp.get("id", "")
    print(f"Posted inline comment on {file_path}:{args.line} (comment #{comment_id})")


def cmd_pr_vote(args):
    root = find_repo_root()
    remote_url = get_remote_url(root)
    owner_repo = parse_owner_repo(remote_url)

    vote = args.vote.lower()
    if vote == "approve":
        gh_args = ["pr", "review", str(args.pr_id), "--repo", owner_repo, "--approve"]
        gh_cmd(gh_args)
        print(f"Approved PR #{args.pr_id}")
    elif vote == "reject":
        gh_args = [
            "pr", "review", str(args.pr_id), "--repo", owner_repo,
            "--request-changes",
            "--body", "AI review found Critical or High severity issues. See inline comments for details.",
        ]
        gh_cmd(gh_args)
        print(f"Requested changes on PR #{args.pr_id}")
    elif vote in ("wait-for-author", "wait"):
        gh_args = [
            "pr", "review", str(args.pr_id), "--repo", owner_repo,
            "--comment",
            "--body", "AI review: waiting for author to address review comments.",
        ]
        gh_cmd(gh_args)
        print(f"Posted 'waiting for author' comment on PR #{args.pr_id}")
    elif vote == "reset":
        print(f"INFO: GitHub does not support vote reset. No action taken for PR #{args.pr_id}")
    else:
        print(f"ERROR: Unknown vote '{vote}'. Use: approve|reject|wait-for-author|reset")
        sys.exit(1)


# --- Main ---

def main():
    parser = argparse.ArgumentParser(description="TAS GitHub PR Integration")
    subparsers = parser.add_subparsers(dest="command", help="Command to run")

    p = subparsers.add_parser("pr-get", help="Get PR metadata")
    p.add_argument("pr_id", type=int, help="Pull Request number")

    p = subparsers.add_parser("pr-diff", help="Fetch PR branch and list changed files")
    p.add_argument("pr_id", type=int, help="Pull Request number")

    p = subparsers.add_parser("pr-comment", help="Post summary comment on PR")
    p.add_argument("pr_id", type=int, help="Pull Request number")
    p.add_argument("--comment", required=True, help="Comment text (markdown supported)")

    p = subparsers.add_parser("pr-inline", help="Post inline comment on PR diff")
    p.add_argument("pr_id", type=int, help="Pull Request number")
    p.add_argument("--file", dest="file_path", required=True, help="File path (e.g. src/api/users.ts)")
    p.add_argument("--line", type=int, required=True, help="Line number")
    p.add_argument("--comment", required=True, help="Inline comment text")

    p = subparsers.add_parser("pr-vote", help="Set vote on PR")
    p.add_argument("pr_id", type=int, help="Pull Request number")
    p.add_argument("--vote", required=True,
                   choices=["approve", "reject", "wait-for-author", "reset"],
                   help="Vote to cast")

    args = parser.parse_args()
    if not args.command:
        parser.print_help()
        sys.exit(1)

    if args.command == "pr-get":
        cmd_pr_get(args)
    elif args.command == "pr-diff":
        cmd_pr_diff(args)
    elif args.command == "pr-comment":
        cmd_pr_comment(args)
    elif args.command == "pr-inline":
        cmd_pr_inline(args)
    elif args.command == "pr-vote":
        cmd_pr_vote(args)
    else:
        parser.print_help()


if __name__ == "__main__":
    main()
