#!/usr/bin/env python3
"""
Basic smoke tests for the ClickUp skill.
Requires a real API key in env or config.

Usage:
    python3 tests/test_clickup.py
    python3 tests/test_clickup.py --list LIST_ID   # also test task CRUD

These are live API tests — they hit the real ClickUp API.
"""

import sys
import os
import json
import argparse
import subprocess
from pathlib import Path

CLICKUP = Path(__file__).parent.parent / "clickup.py"


def run(args: list) -> dict | list | None:
    cmd = [sys.executable, str(CLICKUP)] + args
    result = subprocess.run(cmd, capture_output=True, text=True)
    if result.returncode != 0:
        return None
    try:
        return json.loads(result.stdout)
    except Exception:
        return None


def test_auth():
    """Test: whoami returns a user object."""
    print("Test: whoami ... ", end="")
    data = run(["whoami"])
    assert data is not None, "whoami returned None (check API key)"
    user = data.get("user") or data
    assert "id" in user or "username" in user, f"Unexpected user response: {data}"
    print(f"✅  ({user.get('username', user.get('email', 'unknown'))})")


def test_workspaces():
    """Test: workspaces returns at least one team."""
    print("Test: workspaces ... ", end="")
    data = run(["workspaces"])
    assert data is not None, "workspaces returned None"
    assert isinstance(data, list), f"Expected list, got {type(data)}"
    assert len(data) > 0, "No workspaces returned"
    print(f"✅  ({len(data)} workspace(s))")
    return data[0]["id"]


def test_spaces(workspace_id: str):
    """Test: spaces returns at least one space."""
    print(f"Test: spaces ({workspace_id}) ... ", end="")
    data = run(["spaces", workspace_id])
    assert data is not None, "spaces returned None"
    assert isinstance(data, list), f"Expected list, got {type(data)}"
    assert len(data) > 0, "No spaces returned"
    print(f"✅  ({len(data)} space(s): {[s['name'] for s in data]})")
    return data[0]["id"]


def test_projects(space_id: str):
    """Test: projects returns folders for a space."""
    print(f"Test: projects ({space_id}) ... ", end="")
    data = run(["projects", space_id])
    assert data is not None, "projects returned None"
    assert isinstance(data, list), f"Expected list, got {type(data)}"
    print(f"✅  ({len(data)} project(s))")
    return space_id


def test_tasks_list(list_id: str):
    """Test: tasks returns task list."""
    print(f"Test: tasks ({list_id}) ... ", end="")
    data = run(["tasks", list_id, "--summary"])
    assert data is not None, f"tasks returned None for list {list_id}"
    assert isinstance(data, list), f"Expected list, got {type(data)}"
    print(f"✅  ({len(data)} task(s))")
    return data


def test_create_update_delete(list_id: str):
    """Test: create → read → update → delete lifecycle."""

    # Create
    print(f"Test: create task ... ", end="")
    data = run(["create", "--list", list_id, "--title", "[TEST] ClickUp Skill Smoke Test",
                "--description", "Auto-created by test_clickup.py — safe to delete",
                "--priority", "low"])
    assert data is not None, "create returned None"
    task_id = data.get("id")
    assert task_id, f"No task ID in create response: {data}"
    print(f"✅  (id={task_id})")

    # Read
    print(f"Test: task {task_id} ... ", end="")
    data = run(["task", task_id, "--summary"])
    assert data is not None, f"task read returned None for {task_id}"
    assert data.get("id") == task_id, "Task ID mismatch"
    print(f"✅  ({data.get('name')})")

    # Update
    print(f"Test: update {task_id} ... ", end="")
    data = run(["update", task_id, "--title", "[TEST] Updated — safe to delete"])
    assert data is not None, "update returned None"
    assert data.get("updated") is True, f"update did not return updated=True: {data}"
    print("✅")

    # Delete
    print(f"Test: delete {task_id} ... ", end="")
    data = run(["delete", task_id, "--confirm"])
    assert data is not None, "delete returned None (exit code non-zero)"
    assert data.get("deleted") is True, f"delete did not return deleted=True: {data}"
    print("✅")


def test_search():
    """Test: search returns results (no crash)."""
    print("Test: search ... ", end="")
    data = run(["search", "--query", "test", "--summary"])
    assert data is not None, "search returned None"
    assert isinstance(data, list), f"Expected list, got {type(data)}"
    print(f"✅  ({len(data)} result(s))")


def main():
    parser = argparse.ArgumentParser(description="ClickUp skill smoke tests")
    parser.add_argument("--list", dest="list_id", help="List ID for CRUD tests")
    parser.add_argument("--skip-crud", action="store_true", help="Skip create/update/delete tests")
    args = parser.parse_args()

    # Check API key
    config_path = Path.home() / ".config" / "clickup" / "config.json"
    if not os.environ.get("CLICKUP_API_KEY") and not config_path.exists():
        print("ERROR: No API key found.")
        print(f"  Set CLICKUP_API_KEY or create {config_path}")
        sys.exit(1)

    print(f"\nRunning ClickUp skill tests against live API...\n{'─'*50}")
    failures = []

    tests = [
        ("auth", test_auth, []),
    ]

    # Run basic tests
    try:
        test_auth()
    except AssertionError as e:
        failures.append(("auth", str(e)))

    ws_id = None
    try:
        ws_id = test_workspaces()
    except AssertionError as e:
        failures.append(("workspaces", str(e)))

    if ws_id:
        space_id = None
        try:
            space_id = test_spaces(ws_id)
        except AssertionError as e:
            failures.append(("spaces", str(e)))

        if space_id:
            try:
                test_projects(space_id)
            except AssertionError as e:
                failures.append(("projects", str(e)))

    try:
        test_search()
    except AssertionError as e:
        failures.append(("search", str(e)))

    # CRUD tests (requires list ID)
    if args.list_id and not args.skip_crud:
        try:
            test_tasks_list(args.list_id)
        except AssertionError as e:
            failures.append(("tasks", str(e)))

        try:
            test_create_update_delete(args.list_id)
        except AssertionError as e:
            failures.append(("crud", str(e)))
    elif not args.skip_crud:
        print("\n⚠️  Skipping CRUD tests (pass --list LIST_ID to enable)")

    # Summary
    print(f"\n{'─'*50}")
    if failures:
        print(f"❌ {len(failures)} test(s) failed:")
        for name, err in failures:
            print(f"   {name}: {err}")
        sys.exit(1)
    else:
        print("✅ All tests passed!")
        sys.exit(0)


if __name__ == "__main__":
    main()
