#!/usr/bin/env python3
"""
clickup-overdue-log.py — Query ClickUp for overdue tasks and log to monthly CSV

Queries the Boules Consulting workspace for all tasks past their due date.
Appends results to overdue-tasks-YYYY-MM.csv in the logs directory.

CSV Columns: Date Checked, Task Name, List, Due Date, Assignee, Days Overdue

Usage:
    python3 clickup-overdue-log.py                    # Run normally
    python3 clickup-overdue-log.py --dry-run          # Show what would be logged (don't write)
    python3 clickup-overdue-log.py --workspace-id 123 # Override workspace ID
"""

import argparse
import csv
import json
import os
import sys
import time
from datetime import datetime
from pathlib import Path
from typing import List, Dict, Optional

# Add skills directory to path for clickup module
sys.path.insert(0, str(Path(__file__).parent.parent / "skills" / "clickup"))

try:
    from clickup import ClickUpAPI
except ImportError:
    print(json.dumps({
        "error": "ClickUp skill not found",
        "fix": "Ensure clickup.py is in ~/.openclaw/workspace/skills/clickup/"
    }), file=sys.stderr)
    sys.exit(1)

# ─────────────────────────────────────────────────────────
# Configuration
# ─────────────────────────────────────────────────────────

BOULES_WORKSPACE_ID = "9009180266"
LOGS_DIR = Path.home() / ".openclaw" / "workspace" / "projects" / "boules-clickup" / "overdue-logs"
CSV_COLUMNS = ["Date Checked", "Task Name", "List", "Due Date", "Assignee", "Days Overdue"]


def get_api_key() -> str:
    """Get ClickUp API key from environment or exit."""
    key = os.environ.get("CLICKUP_API_KEY") or os.environ.get("CLICKUP_API_KEY")
    if not key:
        print(json.dumps({
            "error": "CLICKUP_API_KEY not set",
            "fix": "Set CLICKUP_API_KEY environment variable"
        }), file=sys.stderr)
        sys.exit(1)
    return key


def init_client(api_key: str, workspace_id: str) -> ClickUpAPI:
    """Initialize ClickUp client."""
    config = {"workspace_id": workspace_id}
    return ClickUpAPI(api_key, config)


def get_all_lists(client: ClickUpAPI, workspace_id: str) -> List[Dict]:
    """Recursively get all lists from all spaces and folders."""
    lists = []

    try:
        spaces_resp = client.get_spaces(workspace_id)
        spaces = spaces_resp.get("spaces", [])

        for space in spaces:
            space_id = space["id"]
            space_name = space["name"]

            # Get folderless lists
            try:
                folderless = client.get_folderless_lists(space_id)
                for lst in folderless.get("lists", []):
                    lst["_space_name"] = space_name
                    lists.append(lst)
            except Exception as e:
                print(f"Warning: Could not fetch folderless lists from space {space_name}: {e}",
                      file=sys.stderr)

            # Get lists from folders
            try:
                folders_resp = client.get_folders(space_id)
                folders = folders_resp.get("folders", [])

                for folder in folders:
                    folder_id = folder["id"]
                    try:
                        folder_lists = client.get_lists(folder_id)
                        for lst in folder_lists.get("lists", []):
                            lst["_space_name"] = space_name
                            lst["_folder_name"] = folder["name"]
                            lists.append(lst)
                    except Exception as e:
                        print(f"Warning: Could not fetch lists from folder {folder['name']}: {e}",
                              file=sys.stderr)
            except Exception as e:
                print(f"Warning: Could not fetch folders from space {space_name}: {e}",
                      file=sys.stderr)

        return lists
    except Exception as e:
        print(json.dumps({"error": f"Failed to fetch lists: {e}"}), file=sys.stderr)
        sys.exit(1)


def get_overdue_tasks(client: ClickUpAPI, list_id: str) -> List[Dict]:
    """Get all overdue tasks from a list."""
    tasks = []
    try:
        # Get current time in milliseconds (ClickUp uses unix timestamps)
        now_ms = int(time.time() * 1000)

        # Query with due_date_lt (less than now) to get overdue
        all_tasks = client.get_tasks(
            list_id,
            due_date_lt=now_ms,
            page=0,
            subtasks=False
        )

        tasks = all_tasks.get("tasks", [])
    except Exception as e:
        print(f"Warning: Could not fetch tasks from list {list_id}: {e}", file=sys.stderr)

    return tasks


def format_date(timestamp_ms: Optional[int]) -> str:
    """Format ClickUp timestamp (milliseconds) to YYYY-MM-DD."""
    if not timestamp_ms:
        return "No Due Date"
    return datetime.fromtimestamp(int(timestamp_ms) / 1000).strftime("%Y-%m-%d")


def calculate_days_overdue(due_date_ms: Optional[int]) -> str:
    """Calculate days overdue. Returns "-" if no due date."""
    if not due_date_ms:
        return "-"

    due_date = datetime.fromtimestamp(int(due_date_ms) / 1000)
    days = (datetime.now() - due_date).days

    return str(max(0, days))  # Don't show negative days


def get_assignee_names(task: Dict) -> str:
    """Extract assignee names from task."""
    assignees = task.get("assignees", [])
    if not assignees:
        return "Unassigned"
    names = [a.get("username", a.get("email", "Unknown")) for a in assignees]
    return ", ".join(names)


def build_csv_rows(lists: List[Dict], client: ClickUpAPI) -> List[Dict]:
    """Build CSV rows from all overdue tasks."""
    rows = []
    today = datetime.now().strftime("%Y-%m-%d")

    print(f"Querying {len(lists)} lists for overdue tasks...")

    for idx, lst in enumerate(lists, 1):
        list_id = lst["id"]
        list_name = lst["name"]

        # Print progress
        print(f"  [{idx}/{len(lists)}] {list_name}...", end=" ", flush=True)

        overdue_tasks = get_overdue_tasks(client, list_id)
        print(f"({len(overdue_tasks)} overdue)")

        for task in overdue_tasks:
            row = {
                "Date Checked": today,
                "Task Name": task.get("name", "Unnamed Task"),
                "List": list_name,
                "Due Date": format_date(task.get("due_date")),
                "Assignee": get_assignee_names(task),
                "Days Overdue": calculate_days_overdue(task.get("due_date"))
            }
            rows.append(row)

    return rows


def get_csv_path() -> Path:
    """Get the CSV file path for this month."""
    today = datetime.now()
    filename = f"overdue-tasks-{today.strftime('%Y-%m')}.csv"
    return LOGS_DIR / filename


def write_csv(rows: List[Dict], dry_run: bool = False) -> int:
    """Append rows to monthly CSV file. Returns count written."""
    if not rows:
        print("No overdue tasks found.")
        return 0

    csv_path = get_csv_path()

    # Ensure directory exists
    LOGS_DIR.mkdir(parents=True, exist_ok=True)

    # Check if file exists (need to write header if not)
    file_exists = csv_path.exists()

    if dry_run:
        print(f"\n[DRY RUN] Would write {len(rows)} rows to {csv_path}")
        print("\nSample rows:")
        for row in rows[:3]:
            print(f"  {row}")
        if len(rows) > 3:
            print(f"  ... and {len(rows) - 3} more")
        return len(rows)

    # Write CSV
    try:
        with open(csv_path, "a", newline="") as f:
            writer = csv.DictWriter(f, fieldnames=CSV_COLUMNS)

            # Write header only if file is new
            if not file_exists:
                writer.writeheader()

            writer.writerows(rows)

        print(f"✓ Wrote {len(rows)} overdue tasks to {csv_path}")
        return len(rows)
    except Exception as e:
        print(json.dumps({
            "error": f"Failed to write CSV: {e}",
            "path": str(csv_path)
        }), file=sys.stderr)
        sys.exit(1)


def main():
    parser = argparse.ArgumentParser(
        description="Query ClickUp for overdue tasks and log to monthly CSV"
    )
    parser.add_argument(
        "--workspace-id",
        default=BOULES_WORKSPACE_ID,
        help=f"ClickUp workspace ID (default: {BOULES_WORKSPACE_ID})"
    )
    parser.add_argument(
        "--dry-run",
        action="store_true",
        help="Show what would be logged without writing"
    )
    args = parser.parse_args()

    # Initialize
    api_key = get_api_key()
    client = init_client(api_key, args.workspace_id)

    # Fetch all lists
    lists = get_all_lists(client, args.workspace_id)
    print(f"Found {len(lists)} lists in workspace {args.workspace_id}\n")

    # Build rows from overdue tasks
    rows = build_csv_rows(lists, client)

    # Write to CSV
    count = write_csv(rows, dry_run=args.dry_run)

    print(f"\nCompleted. Total overdue tasks: {count}")

    return 0


if __name__ == "__main__":
    sys.exit(main())

# TONY-APPROVED: 2026-03-01 | sha:1bd3b492
