"""AI-Trackdown adapter implementation."""

import builtins
import json
import logging
from datetime import datetime
from pathlib import Path
from typing import Any

from ..core.adapter import BaseAdapter
from ..core.models import (
    Attachment,
    Comment,
    Epic,
    Priority,
    SearchQuery,
    Task,
    TicketState,
)
from ..core.registry import AdapterRegistry

logger = logging.getLogger(__name__)

# Import ai-trackdown-pytools when available
try:
    from ai_trackdown_pytools import AITrackdown
    from ai_trackdown_pytools import Ticket as AITicket

    HAS_AITRACKDOWN = True
except ImportError:
    HAS_AITRACKDOWN = False
    AITrackdown = None
    AITicket = None


class AITrackdownAdapter(BaseAdapter[Task]):
    """Adapter for AI-Trackdown ticket system."""

    def __init__(self, config: dict[str, Any]):
        """Initialize AI-Trackdown adapter.

        Args:
            config: Configuration with 'base_path' for tickets directory

        """
        super().__init__(config)
        self.base_path = Path(config.get("base_path", ".aitrackdown"))
        self.tickets_dir = self.base_path / "tickets"
        self._comment_counter = 0  # Counter for unique comment IDs

        # Initialize AI-Trackdown if available
        # Always create tickets directory (needed for both modes)
        self.tickets_dir.mkdir(parents=True, exist_ok=True)

        if HAS_AITRACKDOWN:
            self.tracker = AITrackdown(str(self.base_path))
        else:
            # Fallback to direct file operations
            self.tracker = None

    def validate_credentials(self) -> tuple[bool, str]:
        """Validate that required credentials are present.

        AITrackdown is file-based and doesn't require credentials.

        Returns:
            (is_valid, error_message) - Always returns (True, "") for AITrackdown

        """
        # AITrackdown is file-based and doesn't require API credentials
        # Just verify the base_path is accessible
        if not self.base_path:
            return False, "AITrackdown base_path is required in configuration"
        return True, ""

    def _get_state_mapping(self) -> dict[TicketState, str]:
        """Map universal states to AI-Trackdown states.

        Note: We use the exact enum values (snake_case) to match what
        Pydantic's use_enum_values=True produces. This ensures consistency
        between what's written to files and what's read back.
        """
        return {
            TicketState.OPEN: "open",
            TicketState.IN_PROGRESS: "in_progress",  # snake_case, not kebab-case
            TicketState.READY: "ready",
            TicketState.TESTED: "tested",
            TicketState.DONE: "done",
            TicketState.WAITING: "waiting",
            TicketState.BLOCKED: "blocked",
            TicketState.CLOSED: "closed",
        }

    def _priority_to_ai(self, priority: Priority | str) -> str:
        """Convert universal priority to AI-Trackdown priority."""
        if isinstance(priority, Priority):
            return priority.value
        return priority  # Already a string due to use_enum_values=True

    def _priority_from_ai(self, ai_priority: str) -> Priority:
        """Convert AI-Trackdown priority to universal priority."""
        try:
            return Priority(ai_priority.lower())
        except ValueError:
            return Priority.MEDIUM

    def _task_from_ai_ticket(self, ai_ticket: dict[str, Any]) -> Task:
        """Convert AI-Trackdown ticket to universal Task."""
        # Get user metadata from ticket file
        user_metadata = ai_ticket.get("metadata", {})

        # Create adapter metadata
        adapter_metadata = {
            "ai_ticket_id": ai_ticket.get("id"),
            "source": "aitrackdown",
        }

        # Merge user metadata with adapter metadata (user takes priority)
        combined_metadata = {**adapter_metadata, **user_metadata}

        return Task(
            id=ai_ticket.get("id"),
            title=ai_ticket.get("title", ""),
            description=ai_ticket.get("description"),
            state=self.map_state_from_system(ai_ticket.get("status", "open")),
            priority=self._priority_from_ai(ai_ticket.get("priority", "medium")),
            tags=ai_ticket.get("tags", []),
            parent_issue=ai_ticket.get("parent_issue"),
            parent_epic=ai_ticket.get("parent_epic"),
            assignee=ai_ticket.get("assignee"),
            created_at=(
                datetime.fromisoformat(ai_ticket["created_at"])
                if "created_at" in ai_ticket
                else None
            ),
            updated_at=(
                datetime.fromisoformat(ai_ticket["updated_at"])
                if "updated_at" in ai_ticket
                else None
            ),
            metadata=combined_metadata,  # Use merged metadata
        )

    def _epic_from_ai_ticket(self, ai_ticket: dict[str, Any]) -> Epic:
        """Convert AI-Trackdown ticket to universal Epic."""
        # Get user metadata from ticket file
        user_metadata = ai_ticket.get("metadata", {})

        # Create adapter metadata
        adapter_metadata = {
            "ai_ticket_id": ai_ticket.get("id"),
            "source": "aitrackdown",
        }

        # Merge user metadata with adapter metadata (user takes priority)
        combined_metadata = {**adapter_metadata, **user_metadata}

        return Epic(
            id=ai_ticket.get("id"),
            title=ai_ticket.get("title", ""),
            description=ai_ticket.get("description"),
            state=self.map_state_from_system(ai_ticket.get("status", "open")),
            priority=self._priority_from_ai(ai_ticket.get("priority", "medium")),
            tags=ai_ticket.get("tags", []),
            child_issues=ai_ticket.get("child_issues", []),
            created_at=(
                datetime.fromisoformat(ai_ticket["created_at"])
                if "created_at" in ai_ticket and ai_ticket["created_at"]
                else None
            ),
            updated_at=(
                datetime.fromisoformat(ai_ticket["updated_at"])
                if "updated_at" in ai_ticket and ai_ticket["updated_at"]
                else None
            ),
            metadata=combined_metadata,  # Use merged metadata
        )

    def _task_to_ai_ticket(self, task: Task) -> dict[str, Any]:
        """Convert universal Task to AI-Trackdown ticket."""
        # Handle enum values that may be stored as strings due to use_enum_values=True
        # Note: task.state is always a string due to ConfigDict(use_enum_values=True)
        state_value = task.state
        if isinstance(task.state, TicketState):
            state_value = self._get_state_mapping()[task.state]
        elif isinstance(task.state, str):
            # Already a string - keep as-is (don't convert to kebab-case)
            # The state is already in snake_case format from the enum value
            state_value = task.state

        return {
            "id": task.id,
            "title": task.title,
            "description": task.description,
            "status": state_value,
            "priority": self._priority_to_ai(task.priority),
            "tags": task.tags,
            "parent_issue": task.parent_issue,
            "parent_epic": task.parent_epic,
            "assignee": task.assignee,
            "created_at": task.created_at.isoformat() if task.created_at else None,
            "updated_at": task.updated_at.isoformat() if task.updated_at else None,
            "metadata": task.metadata or {},  # Serialize user metadata
            "type": "task",
        }

    def _epic_to_ai_ticket(self, epic: Epic) -> dict[str, Any]:
        """Convert universal Epic to AI-Trackdown ticket."""
        # Handle enum values that may be stored as strings due to use_enum_values=True
        # Note: epic.state is always a string due to ConfigDict(use_enum_values=True)
        state_value = epic.state
        if isinstance(epic.state, TicketState):
            state_value = self._get_state_mapping()[epic.state]
        elif isinstance(epic.state, str):
            # Already a string - keep as-is (don't convert to kebab-case)
            # The state is already in snake_case format from the enum value
            state_value = epic.state

        return {
            "id": epic.id,
            "title": epic.title,
            "description": epic.description,
            "status": state_value,
            "priority": self._priority_to_ai(epic.priority),
            "tags": epic.tags,
            "child_issues": epic.child_issues,
            "created_at": epic.created_at.isoformat() if epic.created_at else None,
            "updated_at": epic.updated_at.isoformat() if epic.updated_at else None,
            "metadata": epic.metadata or {},  # Serialize user metadata
            "type": "epic",
        }

    def _read_ticket_file(self, ticket_id: str) -> dict[str, Any] | None:
        """Read ticket from file system."""
        ticket_file = self.tickets_dir / f"{ticket_id}.json"
        if ticket_file.exists():
            with open(ticket_file) as f:
                return json.load(f)
        return None

    def _write_ticket_file(self, ticket_id: str, data: dict[str, Any]) -> None:
        """Write ticket to file system."""
        ticket_file = self.tickets_dir / f"{ticket_id}.json"
        with open(ticket_file, "w") as f:
            json.dump(data, f, indent=2, default=str)

    async def create(self, ticket: Task | Epic) -> Task | Epic:
        """Create a new task."""
        # Generate ID if not provided
        if not ticket.id:
            # Use microseconds to ensure uniqueness
            timestamp = datetime.now().strftime("%Y%m%d%H%M%S%f")
            prefix = "epic" if isinstance(ticket, Epic) else "task"
            ticket.id = f"{prefix}-{timestamp}"

        # Set timestamps
        now = datetime.now()
        ticket.created_at = now
        ticket.updated_at = now

        # Convert to AI-Trackdown format
        if isinstance(ticket, Epic):
            ai_ticket = self._epic_to_ai_ticket(ticket)
        else:
            ai_ticket = self._task_to_ai_ticket(ticket)

        if self.tracker:
            # Use AI-Trackdown library
            created = self.tracker.create_ticket(
                title=ticket.title,
                description=ticket.description,
                priority=ai_ticket["priority"],
                tags=ticket.tags,
                ticket_type="task",
            )
            ticket.id = created.id
        else:
            # Direct file operation
            self._write_ticket_file(ticket.id, ai_ticket)

        return ticket

    async def create_epic(
        self, title: str, description: str = None, **kwargs: Any
    ) -> Epic:
        """Create a new epic.

        Args:
            title: Epic title
            description: Epic description
            **kwargs: Additional epic properties

        Returns:
            Created Epic instance

        """
        epic = Epic(title=title, description=description, **kwargs)
        return await self.create(epic)

    async def create_issue(
        self,
        title: str,
        parent_epic: str = None,
        description: str = None,
        **kwargs: Any,
    ) -> Task:
        """Create a new issue.

        Args:
            title: Issue title
            parent_epic: Parent epic ID
            description: Issue description
            **kwargs: Additional issue properties

        Returns:
            Created Task instance (representing an issue)

        """
        task = Task(
            title=title, description=description, parent_epic=parent_epic, **kwargs
        )
        return await self.create(task)

    async def create_task(
        self, title: str, parent_id: str, description: str = None, **kwargs: Any
    ) -> Task:
        """Create a new task under an issue.

        Args:
            title: Task title
            parent_id: Parent issue ID
            description: Task description
            **kwargs: Additional task properties

        Returns:
            Created Task instance

        """
        task = Task(
            title=title, description=description, parent_issue=parent_id, **kwargs
        )
        return await self.create(task)

    async def read(self, ticket_id: str) -> Task | Epic | None:
        """Read a task by ID."""
        if self.tracker:
            ai_ticket = self.tracker.get_ticket(ticket_id)
            if ai_ticket:
                return self._task_from_ai_ticket(ai_ticket.__dict__)
        else:
            ai_ticket = self._read_ticket_file(ticket_id)
            if ai_ticket:
                if ai_ticket.get("type") == "epic":
                    return self._epic_from_ai_ticket(ai_ticket)
                else:
                    return self._task_from_ai_ticket(ai_ticket)
        return None

    async def update(
        self, ticket_id: str, updates: dict[str, Any] | Task
    ) -> Task | Epic | None:
        """Update a task or epic.

        Args:
            ticket_id: ID of ticket to update
            updates: Dictionary of updates or Task object with new values

        Returns:
            Updated Task or Epic, or None if ticket not found

        Raises:
            AttributeError: If update fails due to invalid fields

        """
        # Read existing ticket
        existing = await self.read(ticket_id)
        if not existing:
            return None

        # Apply updates
        if isinstance(updates, Task):
            # If updates is a Task object, copy all fields except frozen ones
            for field in updates.__fields__:
                if (
                    field not in ["ticket_type"]
                    and hasattr(updates, field)
                    and getattr(updates, field) is not None
                ):
                    setattr(existing, field, getattr(updates, field))
        else:
            # If updates is a dictionary
            for key, value in updates.items():
                if hasattr(existing, key):
                    setattr(existing, key, value)

        existing.updated_at = datetime.now()

        # Write back - use appropriate converter based on ticket type
        if isinstance(existing, Epic):
            ai_ticket = self._epic_to_ai_ticket(existing)
        else:
            ai_ticket = self._task_to_ai_ticket(existing)

        if self.tracker:
            self.tracker.update_ticket(ticket_id, **updates)
        else:
            self._write_ticket_file(ticket_id, ai_ticket)

        return existing

    async def delete(self, ticket_id: str) -> bool:
        """Delete a task."""
        if self.tracker:
            return self.tracker.delete_ticket(ticket_id)
        else:
            ticket_file = self.tickets_dir / f"{ticket_id}.json"
            if ticket_file.exists():
                ticket_file.unlink()
                return True
        return False

    async def list(
        self, limit: int = 10, offset: int = 0, filters: dict[str, Any] | None = None
    ) -> list[Task]:
        """List tasks with pagination."""
        tasks = []

        if self.tracker:
            # Use AI-Trackdown library
            tickets = self.tracker.list_tickets(
                status=filters.get("state") if filters else None,
                limit=limit,
                offset=offset,
            )
            tasks = [self._task_from_ai_ticket(t.__dict__) for t in tickets]
        else:
            # Direct file operation - read all files, filter, then paginate
            ticket_files = sorted(self.tickets_dir.glob("*.json"))
            for ticket_file in ticket_files:
                with open(ticket_file) as f:
                    ai_ticket = json.load(f)
                    task = self._task_from_ai_ticket(ai_ticket)

                    # Apply filters
                    if filters:
                        if "state" in filters:
                            filter_state = filters["state"]
                            # Handle state comparison - task.state might be string, filter_state might be enum
                            if isinstance(filter_state, TicketState):
                                filter_state = filter_state.value
                            if task.state != filter_state:
                                continue
                        if "priority" in filters:
                            filter_priority = filters["priority"]
                            # Handle priority comparison
                            if isinstance(filter_priority, Priority):
                                filter_priority = filter_priority.value
                            if task.priority != filter_priority:
                                continue

                    tasks.append(task)

            # Apply pagination after filtering
            tasks = tasks[offset : offset + limit]

        return tasks

    async def search(self, query: SearchQuery) -> builtins.list[Task]:
        """Search tasks using query parameters."""
        filters = {}
        if query.state:
            filters["state"] = query.state
        if query.priority:
            filters["priority"] = query.priority

        # Get all matching tasks
        all_tasks = await self.list(limit=100, filters=filters)

        # Additional filtering
        results = []
        for task in all_tasks:
            # Text search in title and description
            if query.query:
                search_text = query.query.lower()
                if (
                    search_text not in (task.title or "").lower()
                    and search_text not in (task.description or "").lower()
                ):
                    continue

            # Tag filtering
            if query.tags:
                if not any(tag in task.tags for tag in query.tags):
                    continue

            # Assignee filtering
            if query.assignee and task.assignee != query.assignee:
                continue

            results.append(task)

        # Apply pagination
        return results[query.offset : query.offset + query.limit]

    async def transition_state(
        self, ticket_id: str, target_state: TicketState
    ) -> Task | None:
        """Transition task to new state."""
        # Validate transition
        if not await self.validate_transition(ticket_id, target_state):
            return None

        # Update state
        return await self.update(ticket_id, {"state": target_state})

    async def add_comment(self, comment: Comment) -> Comment:
        """Add comment to a task."""
        # Generate ID with counter to ensure uniqueness
        if not comment.id:
            timestamp = datetime.now().strftime("%Y%m%d%H%M%S%f")
            self._comment_counter += 1
            comment.id = f"comment-{timestamp}-{self._comment_counter:04d}"

        comment.created_at = datetime.now()

        # Store comment (simplified - in real implementation would be linked to ticket)
        comment_file = self.base_path / "comments" / f"{comment.id}.json"
        comment_file.parent.mkdir(parents=True, exist_ok=True)

        with open(comment_file, "w") as f:
            json.dump(comment.model_dump(), f, indent=2, default=str)

        return comment

    async def get_comments(
        self, ticket_id: str, limit: int = 10, offset: int = 0
    ) -> builtins.list[Comment]:
        """Get comments for a task."""
        comments = []
        comments_dir = self.base_path / "comments"

        if comments_dir.exists():
            # Get all comment files and filter by ticket_id first
            comment_files = sorted(comments_dir.glob("*.json"))
            for comment_file in comment_files:
                with open(comment_file) as f:
                    data = json.load(f)
                    if data.get("ticket_id") == ticket_id:
                        comments.append(Comment(**data))

        # Apply limit and offset AFTER filtering
        return comments[offset : offset + limit]

    async def get_epic(self, epic_id: str) -> Epic | None:
        """Get epic by ID.

        Args:
            epic_id: Epic ID to retrieve

        Returns:
            Epic if found, None otherwise

        """
        ticket = await self.read(epic_id)
        if ticket:
            # Check if it's an Epic (can be Epic instance or have epic ticket_type)
            if isinstance(ticket, Epic):
                return ticket
            # Check ticket_type (may be string or enum)
            ticket_type_str = (
                str(ticket.ticket_type).lower()
                if hasattr(ticket, "ticket_type")
                else None
            )
            if ticket_type_str and "epic" in ticket_type_str:
                return Epic(**ticket.model_dump())
        return None

    async def list_epics(self, limit: int = 10, offset: int = 0) -> builtins.list[Epic]:
        """List all epics.

        Args:
            limit: Maximum number of epics to return
            offset: Number of epics to skip

        Returns:
            List of epics

        """
        all_tickets = await self.list(limit=100, offset=0, filters={"type": "epic"})
        epics = []
        for ticket in all_tickets:
            if ticket.ticket_type == "epic":
                epics.append(Epic(**ticket.model_dump()))
        return epics[offset : offset + limit]

    async def list_issues_by_epic(self, epic_id: str) -> builtins.list[Task]:
        """List all issues belonging to an epic.

        Args:
            epic_id: Epic ID to get issues for

        Returns:
            List of issues (tasks with parent_epic set)

        """
        all_tickets = await self.list(limit=1000, offset=0, filters={})
        issues = []
        for ticket in all_tickets:
            if hasattr(ticket, "parent_epic") and ticket.parent_epic == epic_id:
                issues.append(ticket)
        return issues

    async def list_tasks_by_issue(self, issue_id: str) -> builtins.list[Task]:
        """List all tasks belonging to an issue.

        Args:
            issue_id: Issue ID (parent task) to get child tasks for

        Returns:
            List of tasks

        """
        all_tickets = await self.list(limit=1000, offset=0, filters={})
        tasks = []
        for ticket in all_tickets:
            # Check if this ticket has parent_issue matching the issue
            if hasattr(ticket, "parent_issue") and ticket.parent_issue == issue_id:
                tasks.append(ticket)
        return tasks

    def _sanitize_filename(self, filename: str) -> str:
        """Sanitize filename to prevent security issues.

        Args:
            filename: Original filename

        Returns:
            Sanitized filename safe for filesystem

        """
        # Remove path separators and other dangerous characters
        safe_chars = set(
            "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789._- "
        )
        sanitized = "".join(c if c in safe_chars else "_" for c in filename)

        # Ensure filename is not empty
        if not sanitized.strip():
            return "unnamed_file"

        return sanitized.strip()

    def _guess_content_type(self, file_path: Path) -> str:
        """Guess MIME type from file extension.

        Args:
            file_path: Path to file

        Returns:
            MIME type string

        """
        import mimetypes

        content_type, _ = mimetypes.guess_type(str(file_path))
        return content_type or "application/octet-stream"

    def _calculate_checksum(self, file_path: Path) -> str:
        """Calculate SHA256 checksum of file.

        Args:
            file_path: Path to file

        Returns:
            Hexadecimal checksum string

        """
        import hashlib

        sha256 = hashlib.sha256()
        with open(file_path, "rb") as f:
            # Read in chunks to handle large files
            for chunk in iter(lambda: f.read(4096), b""):
                sha256.update(chunk)

        return sha256.hexdigest()

    async def add_attachment(
        self,
        ticket_id: str,
        file_path: str,
        description: str | None = None,
    ) -> Attachment:
        """Attach a file to a ticket (local filesystem storage).

        Args:
            ticket_id: Ticket identifier
            file_path: Local file path to attach
            description: Optional attachment description

        Returns:
            Attachment metadata

        Raises:
            ValueError: If ticket doesn't exist
            FileNotFoundError: If file doesn't exist

        """
        import shutil

        # Validate ticket exists
        ticket = await self.read(ticket_id)
        if not ticket:
            raise ValueError(f"Ticket {ticket_id} not found")

        # Validate file exists
        source_path = Path(file_path).resolve()
        if not source_path.exists():
            raise FileNotFoundError(f"File not found: {file_path}")

        # Check file size (max 100MB for local storage)
        size_mb = source_path.stat().st_size / (1024 * 1024)
        if size_mb > 100:
            raise ValueError(f"File too large: {size_mb:.2f}MB (max: 100MB)")

        # Create attachments directory for this ticket
        attachments_dir = self.base_path / "attachments" / ticket_id
        attachments_dir.mkdir(parents=True, exist_ok=True)

        # Generate unique filename with timestamp
        timestamp = datetime.now().strftime("%Y%m%d%H%M%S%f")
        safe_filename = self._sanitize_filename(source_path.name)
        attachment_id = f"{timestamp}-{safe_filename}"
        dest_path = attachments_dir / attachment_id

        # Copy file to attachments directory
        shutil.copy2(source_path, dest_path)

        # Create attachment metadata
        attachment = Attachment(
            id=attachment_id,
            ticket_id=ticket_id,
            filename=source_path.name,
            url=f"file://{dest_path.absolute()}",
            content_type=self._guess_content_type(source_path),
            size_bytes=source_path.stat().st_size,
            created_at=datetime.now(),
            description=description,
            metadata={
                "original_path": str(source_path),
                "storage_path": str(dest_path),
                "checksum": self._calculate_checksum(dest_path),
            },
        )

        # Save metadata to JSON file
        metadata_file = attachments_dir / f"{attachment_id}.json"
        with open(metadata_file, "w") as f:
            # Convert to dict and handle datetime serialization
            data = attachment.model_dump()
            json.dump(data, f, indent=2, default=str)

        return attachment

    async def get_attachments(self, ticket_id: str) -> builtins.list[Attachment]:
        """Get all attachments for a ticket with path traversal protection.

        Args:
            ticket_id: Ticket identifier

        Returns:
            List of attachments (empty if none)

        """
        # Resolve and validate attachments directory
        attachments_dir = (self.base_path / "attachments" / ticket_id).resolve()

        # CRITICAL SECURITY CHECK: Ensure ticket directory is within base attachments
        base_attachments = (self.base_path / "attachments").resolve()
        if not str(attachments_dir).startswith(str(base_attachments)):
            raise ValueError("Invalid ticket_id: path traversal detected")

        if not attachments_dir.exists():
            return []

        attachments = []
        for metadata_file in attachments_dir.glob("*.json"):
            try:
                with open(metadata_file) as f:
                    data = json.load(f)
                    # Convert ISO datetime strings back to datetime objects
                    if isinstance(data.get("created_at"), str):
                        data["created_at"] = datetime.fromisoformat(
                            data["created_at"].replace("Z", "+00:00")
                        )
                    attachment = Attachment(**data)
                    attachments.append(attachment)
            except (json.JSONDecodeError, ValueError) as e:
                # Log error but continue processing other attachments
                logger.warning(
                    "Failed to load attachment metadata from %s: %s",
                    metadata_file,
                    e,
                )
                continue

        # Sort by creation time (newest first)
        return sorted(
            attachments,
            key=lambda a: a.created_at or datetime.min,
            reverse=True,
        )

    async def delete_attachment(
        self,
        ticket_id: str,
        attachment_id: str,
    ) -> bool:
        """Delete an attachment and its metadata with path traversal protection.

        Args:
            ticket_id: Ticket identifier
            attachment_id: Attachment identifier

        Returns:
            True if deleted, False if not found

        """
        # Resolve base directory
        attachments_dir = (self.base_path / "attachments" / ticket_id).resolve()

        # Validate attachments directory exists
        if not attachments_dir.exists():
            return False

        # Resolve file paths
        attachment_file = (attachments_dir / attachment_id).resolve()
        metadata_file = (attachments_dir / f"{attachment_id}.json").resolve()

        # CRITICAL SECURITY CHECK: Ensure paths are within attachments_dir
        base_resolved = attachments_dir.resolve()
        if not str(attachment_file).startswith(str(base_resolved)):
            raise ValueError(
                "Invalid attachment path: path traversal detected in attachment_id"
            )
        if not str(metadata_file).startswith(str(base_resolved)):
            raise ValueError(
                "Invalid attachment path: path traversal detected in attachment_id"
            )

        # Delete files if they exist
        deleted = False
        if attachment_file.exists():
            attachment_file.unlink()
            deleted = True

        if metadata_file.exists():
            metadata_file.unlink()
            deleted = True

        return deleted


# Register the adapter
AdapterRegistry.register("aitrackdown", AITrackdownAdapter)
