# Copyright (c) 2025 Patrick Motsch
# All rights reserved.
"""ClickUp connector for CRUD operations (compatible with TicketInterface).

This module defines its own minimal abstractions to avoid coupling.
"""

from typing import Optional
import logging
import aiohttp
from modules.datamodels.datamodelTickets import TicketBase, TicketFieldAttribute


logger = logging.getLogger(__name__)


class ConnectorTicketClickup(TicketBase):
    def __init__(
        self,
        *,
        apiToken: str,
        teamId: str,
        listId: Optional[str] = None,
        apiUrl: str = "https://api.clickup.com/api/v2",
    ) -> None:
        self.apiToken = apiToken
        self.teamId = teamId
        self.listId = listId
        self.apiUrl = apiUrl

    def _headers(self) -> dict:
        return {
            "Authorization": self.apiToken,
            "Content-Type": "application/json",
        }

    async def readAttributes(self) -> list[TicketFieldAttribute]:
        """Fetch field attributes. Uses list custom fields if listId provided; else basic fields."""
        attributes: list[TicketFieldAttribute] = []
        try:
            async with aiohttp.ClientSession() as session:
                if self.listId:
                    url = f"{self.apiUrl}/list/{self.listId}/field"
                    async with session.get(url, headers=self._headers()) as response:
                        if response.status != 200:
                            logger.warning(f"ClickUp fields fetch status: {response.status}")
                        else:
                            data = await response.json()
                            for field in data.get("fields", []):
                                fieldId = field.get("id")
                                fieldName = field.get("name", fieldId)
                                if fieldId:
                                    attributes.append(TicketFieldAttribute(fieldName=fieldName, field=fieldId))

                # Add common top-level fields
                core_fields = [
                    ("ID", "id"),
                    ("Name", "name"),
                    ("Status", "status.status"),
                    ("Assignees", "assignees"),
                    ("DateCreated", "date_created"),
                    ("DueDate", "due_date"),
                ]
                for name, fid in core_fields:
                    attributes.append(TicketFieldAttribute(fieldName=name, field=fid))
        except Exception as e:
            logger.error(f"ClickUp read_attributes error: {e}")
        return attributes

    async def readTasks(self, *, limit: int = 0) -> list[dict]:
        """Read tasks from ClickUp, always returning full task records.
        If list_id is set, read from that list; otherwise read from team.
        """
        tasks: list[dict] = []
        try:
            async with aiohttp.ClientSession() as session:
                page = 0
                pageSize = 100
                while True:
                    if self.listId:
                        url = f"{self.apiUrl}/list/{self.listId}/task?subtasks=true&page={page}&order_by=created&reverse=true"
                    else:
                        # Team-level search for open tasks
                        url = f"{self.apiUrl}/team/{self.teamId}/task?subtasks=true&page={page}&order_by=created&reverse=true"

                    # Request with parameters to include all fields where possible
                    async with session.get(url, headers=self._headers()) as response:
                        if response.status != 200:
                            errorText = await response.text()
                            logger.error(f"ClickUp read_tasks failed: {response.status} {errorText}")
                            break

                        data = await response.json()
                        items = data.get("tasks", [])
                        for item in items:
                            tasks.append(item)
                            if limit and len(tasks) >= limit:
                                return tasks

                        if len(items) < pageSize:
                            break
                        page += 1
        except Exception as e:
            logger.error(f"ClickUp read_tasks error: {e}")
        return tasks

    async def writeTasks(self, tasklist: list[dict]) -> None:
        """Update tasks in ClickUp. Expects each item to contain {'ID' or 'id' or 'task_id', 'fields': {...}}"""
        try:
            async with aiohttp.ClientSession() as session:
                for data in tasklist:
                    taskId = data.get("ID") or data.get("id") or data.get("task_id")
                    fields = data.get("fields", {})
                    if not taskId or not isinstance(fields, dict) or not fields:
                        continue

                    # Map generic fields to ClickUp payload
                    payload: dict = {}
                    for fieldId, value in fields.items():
                        # Heuristics: map common field ids
                        if fieldId in ("name", "summary"):
                            payload["name"] = value
                        elif fieldId in ("status",):
                            payload["status"] = value
                        elif fieldId.startswith("customfield_") or fieldId.startswith("cf_"):
                            # ClickUp custom fields need separate endpoint; attempt inline update if supported
                            if "custom_fields" not in payload:
                                payload["custom_fields"] = []
                            payload["custom_fields"].append({"id": fieldId, "value": value})
                        else:
                            # Best-effort assign to description for unknown text fields
                            if isinstance(value, str) and value:
                                payload.setdefault("description", value)

                    url = f"{self.apiUrl}/task/{taskId}"
                    async with session.put(url, headers=self._headers(), json=payload) as response:
                        if response.status not in (200, 204):
                            err = await response.text()
                            logger.error(f"ClickUp update failed for {taskId}: {response.status} {err}")
        except Exception as e:
            logger.error(f"ClickUp write_tasks error: {e}")


