import os
import sys
import base64
import json
from urllib.parse import quote

import httpx
from dotenv import load_dotenv

load_dotenv()

DEVOPS_ORG = os.getenv("DEVOPS_ORG", "")
DEVOPS_PROJECT = os.getenv("DEVOPS_PROJECT", "")
DEVOPS_PAT = os.getenv("DEVOPS_PAT", "")
DEVOPS_WORK_ITEM_TYPE = os.getenv("DEVOPS_WORK_ITEM_TYPE", "Task")
# Optional: assign the demo work item to this email so you can confirm
# person-assignment works. Leave blank to skip.
DEVOPS_DEMO_ASSIGN_TO = os.getenv("DEVOPS_DEMO_ASSIGN_TO", "")


def auth_header() -> dict:
    """Build the Basic auth header from the PAT.
    Azure DevOps expects 'Authorization: Basic base64(":{PAT}")' — note the leading colon."""
    token = base64.b64encode(f":{DEVOPS_PAT}".encode()).decode()
    return {"Authorization": f"Basic {token}"}


def step_1_test_connection() -> bool:
    """Verify PAT works by fetching project metadata."""
    url = f"https://dev.azure.com/{quote(DEVOPS_ORG)}/_apis/projects/{quote(DEVOPS_PROJECT)}?api-version=7.1"
    print(f"  GET {url}")
    r = httpx.get(url, headers=auth_header(), timeout=30)
    if r.status_code == 200:
        proj = r.json()
        print(f"  OK — Project: {proj.get('name')}")
        print(f"       Id:      {proj.get('id')}")
        print(f"       State:   {proj.get('state')}")
        return True
    print(f"  FAILED — HTTP {r.status_code}")
    print(f"  Response: {r.text[:400]}")
    if r.status_code == 401:
        print("  → PAT is invalid, expired, or lacks 'Work Items' scope.")
    elif r.status_code == 404:
        print("  → DEVOPS_ORG or DEVOPS_PROJECT is wrong (check spelling/case).")
    return False


def step_2_create_work_item() -> int | None:
    """Create a test work item and return its ID."""
    url = (
        f"https://dev.azure.com/{quote(DEVOPS_ORG)}/{quote(DEVOPS_PROJECT)}"
        f"/_apis/wit/workitems/${quote(DEVOPS_WORK_ITEM_TYPE)}?api-version=7.1"
    )
    patch_doc = [
        {"op": "add", "path": "/fields/System.Title",
         "value": "[DEMO] Connection test from demo_devops.py"},
        {"op": "add", "path": "/fields/System.Description",
         "value": "This is a test work item created by demo_devops.py to verify the API "
                  "connection. Safe to delete after verification."},
    ]
    if DEVOPS_DEMO_ASSIGN_TO:
        patch_doc.append({
            "op": "add", "path": "/fields/System.AssignedTo",
            "value": DEVOPS_DEMO_ASSIGN_TO,
        })
        print(f"  Assigning to: {DEVOPS_DEMO_ASSIGN_TO}")
    headers = {**auth_header(), "Content-Type": "application/json-patch+json"}
    print(f"  POST {url}")
    r = httpx.post(url, json=patch_doc, headers=headers, timeout=30)
    if r.status_code in (200, 201):
        wi = r.json()
        wid = wi.get("id")
        wurl = wi.get("_links", {}).get("html", {}).get("href")
        print(f"  OK — Created work item #{wid}")
        print(f"       URL: {wurl}")
        return wid
    print(f"  FAILED — HTTP {r.status_code}")
    print(f"  Response: {r.text[:500]}")
    return None


def step_3_read_back(work_item_id: int) -> bool:
    """Read the work item we just created to confirm round-trip works."""
    url = (
        f"https://dev.azure.com/{quote(DEVOPS_ORG)}/{quote(DEVOPS_PROJECT)}"
        f"/_apis/wit/workitems/{work_item_id}?api-version=7.1"
    )
    print(f"  GET {url}")
    r = httpx.get(url, headers=auth_header(), timeout=30)
    if r.status_code == 200:
        wi = r.json()
        fields = wi.get("fields", {})
        print(f"  OK — Round-trip confirmed")
        print(f"       Title:       {fields.get('System.Title')}")
        print(f"       State:       {fields.get('System.State')}")
        print(f"       Created By:  {fields.get('System.CreatedBy', {}).get('displayName')}")
        print(f"       Assigned To: {fields.get('System.AssignedTo', {}).get('displayName') if fields.get('System.AssignedTo') else '(unassigned)'}")
        return True
    print(f"  FAILED — HTTP {r.status_code}: {r.text[:300]}")
    return False


def main():
    print("=" * 60)
    print(" Azure DevOps API Connection Demo")
    print("=" * 60)

    # Validate config
    missing = [k for k, v in [
        ("DEVOPS_ORG", DEVOPS_ORG),
        ("DEVOPS_PROJECT", DEVOPS_PROJECT),
        ("DEVOPS_PAT", DEVOPS_PAT),
    ] if not v]
    if missing:
        print(f"\n  ERROR: Missing env vars: {', '.join(missing)}")
        print("  → Copy .env.example to .env and fill in the values.")
        sys.exit(1)

    print(f"\n  Org:            {DEVOPS_ORG}")
    print(f"  Project:        {DEVOPS_PROJECT}")
    print(f"  Work Item Type: {DEVOPS_WORK_ITEM_TYPE}")
    print(f"  PAT:            {'*' * 10}{DEVOPS_PAT[-4:] if len(DEVOPS_PAT) > 4 else '****'}")

    print("\n[1/3] Test connection...")
    if not step_1_test_connection():
        sys.exit(2)

    print("\n[2/3] Create test work item...")
    wid = step_2_create_work_item()
    if not wid:
        sys.exit(3)

    print("\n[3/3] Read work item back...")
    if not step_3_read_back(wid):
        sys.exit(4)

    print("\n" + "=" * 60)
    print(f"  SUCCESS — Azure DevOps API is reachable and writable.")
    print(f"  Test work item #{wid} was created. You can delete it manually.")
    print("=" * 60)


if __name__ == "__main__":
    main()