#!/usr/bin/env python3
"""Test script for the tag endpoints on Airflow webserver.

Usage:
    # Quick connectivity check
    python scripts/test_tags_endpoint.py --check

    # Update a single tag status
    python scripts/test_tags_endpoint.py status CHUNK_ID --tag-id TAG_ID --status verified

    # Bulk update tag statuses
    python scripts/test_tags_endpoint.py bulk-status CHUNK_ID --updates TAG_ID1=verified TAG_ID2=rejected

    # Rename a tag
    python scripts/test_tags_endpoint.py rename CHUNK_ID --tag-id TAG_ID --name "domain:new_name"

    # Delete a tag
    python scripts/test_tags_endpoint.py delete CHUNK_ID --tag-id TAG_ID

    # Use custom host
    python scripts/test_tags_endpoint.py --host http://localhost:8081 status CHUNK_ID --tag-id TAG_ID --status verified

Requires HMAC_KEY_RING env var (or HMAC_KEY_RING_FILE) to be set.
"""

import argparse
import json
import sys
import uuid

from dotenv import load_dotenv
load_dotenv()

import requests
from textiq_hmac import load_key_ring, sign_context_headers

DEFAULT_TENANT_ID = "28aa3971-48af-4f2a-aa73-9e4f89fbfba3"
DEFAULT_NODE_ID = "22222222-2222-2222-2222-222222222222"
DEFAULT_USER_ID = "33333333-3333-3333-3333-333333333333"
DEFAULT_ROLE = "content_specialist"


def sign_request(tenant_id, node_id, user_id, role):
    """Generate HMAC headers using textiq_hmac package."""
    key_ring = load_key_ring()
    headers = {
        "X-Tenant-ID": tenant_id,
        "X-Node-ID": node_id,
        "X-User-ID": user_id,
        "X-Role": role,
    }
    return sign_context_headers(headers, key_ring)


def _print_response(r):
    """Print response details."""
    print(f"  Status: {r.status_code}")
    print(f"  X-Correlation-ID: {r.headers.get('X-Correlation-ID', 'NOT PRESENT')}")
    print(f"  X-Request-ID: {r.headers.get('X-Request-ID', 'NOT PRESENT')}")
    try:
        body = r.json()
        print(f"  Body: {json.dumps(body, indent=2)}")
    except Exception:
        print(f"  Body: {r.text[:500]}")


def check_endpoint(host):
    """Quick connectivity check — GET should return 405 if route exists."""
    url = f"{host}/api/v1/blocks/00000000-0000-0000-0000-000000000000/tags/status"
    print(f"GET {url}")
    try:
        r = requests.get(url, timeout=5)
        print(f"  Status: {r.status_code}")
        if r.status_code in (405, 401):
            print(f"  -> Route exists ({r.status_code} = expected for unauthenticated GET)")
            return True
        elif r.status_code == 404:
            print("  -> Route NOT found. Plugin may not be loaded.")
            print("     Run: docker exec textiq-airflow-webserver airflow plugins")
            return False
        else:
            print(f"  -> Unexpected status. Body: {r.text[:200]}")
            return False
    except requests.ConnectionError as e:
        print(e)
        print(f"  -> Cannot connect to {host}. Is the webserver running?")
        return False


def test_no_auth(host):
    """PATCH without auth headers — should return 401."""
    url = f"{host}/api/v1/blocks/00000000-0000-0000-0000-000000000000/tags/status"
    print(f"\nPATCH {url} (no auth headers)")
    r = requests.patch(url, json={"tag_id": "x", "status": "verified"}, timeout=10)
    print(f"  Status: {r.status_code}")
    print(f"  Body: {r.text[:200]}")
    if r.status_code == 401:
        print("  -> PASS: Got 401 as expected")
    else:
        print(f"  -> FAIL: Expected 401, got {r.status_code}")


def update_tag_status(host, chunk_id, tag_id, status, tenant_id, node_id, user_id, role):
    """PATCH /{chunk_id}/tags/status"""
    url = f"{host}/api/v1/blocks/{chunk_id}/tags/status"
    headers = sign_request(tenant_id, node_id, user_id, role)
    headers["X-Correlation-ID"] = str(uuid.uuid4())
    headers["Content-Type"] = "application/json"

    payload = {"tag_id": tag_id, "status": status}

    print(f"\nPATCH {url}")
    print(f"  Payload: {json.dumps(payload)}")
    print(f"  Tenant: {tenant_id}")
    print(f"  Correlation-ID: {headers['X-Correlation-ID']}")

    r = requests.patch(url, headers=headers, json=payload, timeout=30)
    _print_response(r)

    if r.status_code == 200:
        print("  -> PASS: Tag status updated")
    else:
        print(f"  -> FAIL: Expected 200, got {r.status_code}")


def bulk_update_tag_status(host, chunk_id, updates, tenant_id, node_id, user_id, role):
    """PATCH /{chunk_id}/tags/bulk-status"""
    url = f"{host}/api/v1/blocks/{chunk_id}/tags/bulk-status"
    headers = sign_request(tenant_id, node_id, user_id, role)
    headers["X-Correlation-ID"] = str(uuid.uuid4())
    headers["Content-Type"] = "application/json"

    payload = {"updates": [{"tag_id": tid, "status": st} for tid, st in updates]}

    print(f"\nPATCH {url}")
    print(f"  Payload: {json.dumps(payload, indent=2)}")
    print(f"  Tenant: {tenant_id}")
    print(f"  Correlation-ID: {headers['X-Correlation-ID']}")

    r = requests.patch(url, headers=headers, json=payload, timeout=30)
    _print_response(r)

    if r.status_code == 200:
        print("  -> PASS: Bulk tag status updated")
    else:
        print(f"  -> FAIL: Expected 200, got {r.status_code}")


def rename_tag(host, chunk_id, tag_id, name, tenant_id, node_id, user_id, role):
    """PATCH /{chunk_id}/tags/{tag_id}/name"""
    url = f"{host}/api/v1/blocks/{chunk_id}/tags/{tag_id}/name"
    headers = sign_request(tenant_id, node_id, user_id, role)
    headers["X-Correlation-ID"] = str(uuid.uuid4())
    headers["Content-Type"] = "application/json"

    payload = {"name": name}

    print(f"\nPATCH {url}")
    print(f"  Payload: {json.dumps(payload)}")
    print(f"  Tenant: {tenant_id}")
    print(f"  Correlation-ID: {headers['X-Correlation-ID']}")

    r = requests.patch(url, headers=headers, json=payload, timeout=30)
    _print_response(r)

    if r.status_code == 200:
        print("  -> PASS: Tag renamed")
    else:
        print(f"  -> FAIL: Expected 200, got {r.status_code}")


def delete_tag(host, chunk_id, tag_id, tenant_id, node_id, user_id, role):
    """DELETE /{chunk_id}/tags/{tag_id}"""
    url = f"{host}/api/v1/blocks/{chunk_id}/tags/{tag_id}"
    headers = sign_request(tenant_id, node_id, user_id, role)
    headers["X-Correlation-ID"] = str(uuid.uuid4())

    print(f"\nDELETE {url}")
    print(f"  Tenant: {tenant_id}")
    print(f"  Correlation-ID: {headers['X-Correlation-ID']}")

    r = requests.delete(url, headers=headers, timeout=30)
    _print_response(r)

    if r.status_code == 204:
        print("  -> PASS: Tag deleted")
    else:
        print(f"  -> FAIL: Expected 204, got {r.status_code}")


def main():
    parser = argparse.ArgumentParser(description="Test DE tag endpoints")
    parser.add_argument("--host", default="http://localhost:8081", help="Airflow webserver URL")
    parser.add_argument("--check", action="store_true", help="Quick connectivity check only")
    parser.add_argument("--tenant-id", default=DEFAULT_TENANT_ID)
    parser.add_argument("--node-id", default=DEFAULT_NODE_ID)
    parser.add_argument("--user-id", default=DEFAULT_USER_ID)
    parser.add_argument("--role", default=DEFAULT_ROLE)

    subparsers = parser.add_subparsers(dest="command")

    # status
    sp_status = subparsers.add_parser("status", help="Update single tag status")
    sp_status.add_argument("chunk_id", help="Chunk UUID")
    sp_status.add_argument("--tag-id", required=True, help="Tag UUID")
    sp_status.add_argument("--status", required=True, choices=["pending", "verified", "rejected"])

    # bulk-status
    sp_bulk = subparsers.add_parser("bulk-status", help="Bulk update tag statuses")
    sp_bulk.add_argument("chunk_id", help="Chunk UUID")
    sp_bulk.add_argument("--updates", nargs="+", required=True, metavar="TAG_ID=STATUS",
                         help="Tag updates as TAG_ID=pending|verified|rejected pairs")

    # rename
    sp_rename = subparsers.add_parser("rename", help="Rename a tag")
    sp_rename.add_argument("chunk_id", help="Chunk UUID")
    sp_rename.add_argument("--tag-id", required=True, help="Tag UUID")
    sp_rename.add_argument("--name", required=True, help="New tag name (namespace:value)")

    # delete
    sp_delete = subparsers.add_parser("delete", help="Delete a tag")
    sp_delete.add_argument("chunk_id", help="Chunk UUID")
    sp_delete.add_argument("--tag-id", required=True, help="Tag UUID")

    args = parser.parse_args()

    print(f"=== Tag Endpoint Test ({args.host}) ===\n")

    alive = check_endpoint(args.host)
    if not alive:
        sys.exit(1)

    if args.check:
        test_no_auth(args.host)
        return

    if not args.command:
        print("\nNo command specified. Running auth-only tests.\n")
        test_no_auth(args.host)
        return

    if args.command == "status":
        update_tag_status(
            args.host, args.chunk_id, args.tag_id, args.status,
            args.tenant_id, args.node_id, args.user_id, args.role,
        )
    elif args.command == "bulk-status":
        updates = []
        for pair in args.updates:
            if "=" not in pair:
                print(f"  ERROR: Invalid format '{pair}'. Use TAG_ID=STATUS")
                sys.exit(1)
            tid, st = pair.split("=", 1)
            if st not in ("pending", "verified", "rejected"):
                print(f"  ERROR: Invalid status '{st}'. Must be pending, verified, or rejected")
                sys.exit(1)
            updates.append((tid, st))
        bulk_update_tag_status(
            args.host, args.chunk_id, updates,
            args.tenant_id, args.node_id, args.user_id, args.role,
        )
    elif args.command == "rename":
        rename_tag(
            args.host, args.chunk_id, args.tag_id, args.name,
            args.tenant_id, args.node_id, args.user_id, args.role,
        )
    elif args.command == "delete":
        delete_tag(
            args.host, args.chunk_id, args.tag_id,
            args.tenant_id, args.node_id, args.user_id, args.role,
        )


if __name__ == "__main__":
    main()
