#!/usr/bin/env python3
from __future__ import annotations


import os
import base64
import requests
import typer
import yaml

from collections import defaultdict

from typing import Dict, List
from pathlib import Path
from typing import Dict, List
from urllib.parse import quote
from datetime import datetime
from dataclasses import dataclass

from dotenv import load_dotenv
from rich.console import Console
from rich.table import Table
from rich.text import Text

from .constants import ORG, API_VERSION, DEFAULT_ENV_FILE

# ---------------------------------------------------------------------
# App setup
# ---------------------------------------------------------------------
app = typer.Typer(help="List open Azure DevOps PRs across projects")
console = Console()

# ---------------------------------------------------------------------
# Dataclass
# ---------------------------------------------------------------------
@dataclass
class PullRequestRecord:
    project: str
    pr_id: int
    title: str
    created_date: datetime
    author: str
    repo: str
    url: str
    reviewer_role: str
    vote: int | None
    reviewers: List[Dict]

# ---------------------------------------------------------------------
# Environment loading
# ---------------------------------------------------------------------
def load_env():
    if DEFAULT_ENV_FILE.exists():
        load_dotenv(DEFAULT_ENV_FILE)

    pat = os.getenv("AZDO_PAT")
    user = os.getenv("AZDO_USER")

    if not pat:
        raise RuntimeError(
            f"AZDO_PAT not set (expected in {DEFAULT_ENV_FILE})"
        )
    if not user:
        raise RuntimeError(
            f"AZDO_USER not set (expected in {DEFAULT_ENV_FILE})"
        )

    return pat, user

# ---------------------------------------------------------------------
# Auth
# ---------------------------------------------------------------------
def auth_header(pat: str) -> Dict[str, str]:
    token = base64.b64encode(f":{pat}".encode()).decode()
    return {
        "Authorization": f"Basic {token}",
        "Accept": "application/json",
        "Content-Type": "application/json",
    }

# ---------------------------------------------------------------------
# Azure DevOps helpers
# ---------------------------------------------------------------------
def get_prs_for_project(pat: str, project: str, status: str = "active") -> List[Dict]:
    encoded_project = quote(project, safe="")
    url = f"https://dev.azure.com/{ORG}/{encoded_project}/_apis/git/pullrequests"
    params = {
        "searchCriteria.status": status,
        "api-version": API_VERSION,
    }

    resp = requests.get(url, headers=auth_header(pat), params=params)

    if "application/json" not in resp.headers.get("content-type", ""):
        raise RuntimeError(
            f"Failed retrieving PRs for project '{project}'\n"
            f"HTTP {resp.status_code}\n"
            f"{resp.text[:500]}"
        )

    return resp.json().get("value", [])

# ---------------------------------------------------------------------
# Project loading
# ---------------------------------------------------------------------
def load_projects(config_file: Path | None, project_arg: str | None) -> List[str]:
    if project_arg:
        return [p.strip() for p in project_arg.split(",") if p.strip()]

    if not config_file:
        raise RuntimeError("Either --project or --config-file must be provided")

    with open(config_file, "r") as fh:
        data = yaml.safe_load(fh) or {}

    projects = data.get("projects")
    if not projects:
        raise RuntimeError("No projects defined in config file")

    return projects

# ---------------------------------------------------------------------
# Reviewer logic
# ---------------------------------------------------------------------
def reviewer_role(pr: Dict, user: str) -> str:
    for reviewer in pr.get("reviewers", []):
        if reviewer.get("uniqueName") == user or reviewer.get("displayName") == user:
            return "REQUIRED" if reviewer.get("isRequired") else "OPTIONAL"
    return ""



def reviewer_vote(pr: Dict, user: str) -> int | None:
    for reviewer in pr.get("reviewers", []):
        if reviewer.get("uniqueName") == user or reviewer.get("displayName") == user:
            return reviewer.get("vote")
    return None

# ---------------------------------------------------------------------
# Record construction
# ---------------------------------------------------------------------
def build_pr_record(pr: Dict, project: str, user: str) -> PullRequestRecord:
    created = datetime.fromisoformat(pr["creationDate"].replace("Z", "+00:00"))

    return PullRequestRecord(
        project=project,
        pr_id=pr["pullRequestId"],
        title=pr["title"],
        created_date=created,
        author=pr["createdBy"]["displayName"],
        repo=pr["repository"]["name"],
        url=pr["_links"]["web"]["href"],
        reviewer_role=reviewer_role(pr, user),
        vote=reviewer_vote(pr, user),
        reviewers=pr.get("reviewers", []),
    )

# ---------------------------------------------------------------------
# CLI
# ---------------------------------------------------------------------
@app.command()
def list_prs(
    config_file: Path = typer.Option(
        None,
        "--config-file",
        exists=True,
        readable=True,
        help="YAML file listing Azure DevOps projects",
    ),
    project: str = typer.Option(
        None,
        "--project",
        help="Comma-separated list of project names",
    ),
    mine_only: bool = typer.Option(
        False,
        "--mine-only",
        help="Only show PRs where you are a reviewer",
    ),
    required_only: bool = typer.Option(
        False,
        "--required-only",
        help="Only show PRs where you are a REQUIRED reviewer (blocking PRs)",
    ),
    status: str = typer.Option(
        "active",
        "--status",
        help="PR status filter: 'active', 'completed', 'abandoned', 'all'",
    ),
):
    """
    List PRs across one or more Azure DevOps projects.
    """
    pat, user = load_env()
    projects = load_projects(config_file, project)

    console.print(f"[bold]Authenticated reviewer:[/bold] {user}")
    console.print(f"[bold]Projects:[/bold] {', '.join(projects)}")
    console.print(f"[bold]Status filter:[/bold] {status}\n")

    records: List[PullRequestRecord] = []

    for proj in projects:
        raw_prs = get_prs_for_project(pat, proj, status)
        for pr in raw_prs:
            record = build_pr_record(pr, proj, user)
            records.append(record)

    if required_only:
        records = [
            r for r in records
            if r.reviewer_role == "REQUIRED"
        ]
    elif mine_only:
        records = [
            r for r in records
            if r.reviewer_role in ("REQUIRED", "OPTIONAL")
        ]



    render_pr_tables_by_project(records, status)

# ---------------------------------------------------------------------
# Rich table output
# ---------------------------------------------------------------------
def render_pr_tables_by_project(records: List[PullRequestRecord], status: str = "active"):
    """
    Render one Rich table per project, sorting PRs by age (oldest first).
    """
    grouped: Dict[str, List[PullRequestRecord]] = defaultdict(list)

    for record in records:
        grouped[record.project].append(record)

    status_label = "open" if status == "active" else status

    for project, project_records in sorted(grouped.items()):
        # 🔹 Sort by oldest PR first (review debt)
        project_records = sorted(
            project_records,
            key=lambda r: r.created_date
        )

        table = Table(
            title=f"📦 Project: {project} ({len(project_records)} {status_label} PRs)",
            header_style="bold cyan",
        )

        table.add_column("PR #", justify="right", style="bold")
        table.add_column("Created", style="yellow")
        table.add_column("Author")
        table.add_column("Codebase", style="magenta")
        table.add_column("Reviewer Role", justify="center")
        table.add_column("Vote", justify="center")
        table.add_column("Title")
        table.add_column("URL", style="blue")

        for r in project_records:
            role_text = (
                Text("REQUIRED", style="bold red")
                if r.reviewer_role == "REQUIRED"
                else Text("OPTIONAL", style="yellow")
                if r.reviewer_role == "OPTIONAL"
                else Text("—", style="dim")
            )

            table.add_row(
                str(r.pr_id),
                r.created_date.strftime("%Y-%m-%d"),
                r.author,
                r.repo,
                role_text,
                vote_text(r.vote),
                r.title,
                Text(r.url, style="link " + r.url),
            )

        console.print(table)
        console.print()


def vote_text(vote: int | None) -> Text:
    if vote is None:
        return Text("—", style="dim")
    if vote >= 5:
        return Text("APPROVED", style="bold green")
    if vote <= -10:
        return Text("REJECTED", style="bold red")
    return Text("WAITING", style="yellow")


if __name__ == "__main__":
    app()
