"""
Organization folder CRUD operations
"""

from typing import Dict, Any, List, Optional
from ..models import Folder, TallyfyError
from ..validation import validate_id


class FolderManager:
    """Handles organization folder CRUD operations"""

    def __init__(self, sdk):
        self.sdk = sdk

    def _validate_org_id(self, org_id: str) -> None:
        validate_id(org_id, "org_id")

    def _validate_folder_id(self, folder_id: str) -> None:
        validate_id(folder_id, "folder_id")

    def _handle_api_error(self, error: Exception, operation: str, **context) -> None:
        context_str = ", ".join(f"{k}={v}" for k, v in context.items())
        msg = f"Failed to {operation}"
        if context_str:
            msg += f" ({context_str})"
        msg += f": {error}"
        self.sdk.logger.error(msg)
        if isinstance(error, TallyfyError):
            raise error
        raise TallyfyError(msg)

    def _parse_folder(self, response_data: Any) -> Folder:
        """Extract a Folder from a wrapped or bare API response."""
        if isinstance(response_data, dict) and 'data' in response_data:
            data = response_data['data']
            if isinstance(data, dict):
                return Folder.from_dict(data)
        if isinstance(response_data, dict):
            return Folder.from_dict(response_data)
        raise TallyfyError("Unexpected response format")

    # ── READ ──────────────────────────────────────────────────────────────

    def get_folders(self, org_id: str, page: int = 1, per_page: int = 100,
                    q: Optional[str] = None) -> List[Folder]:
        """
        List all folders in the organization.

        Args:
            org_id: Organization ID
            page: Page number (default: 1)
            per_page: Results per page (default: 100)
            q: Search query

        Returns:
            List of Folder objects

        Raises:
            TallyfyError: If the request fails
        """
        self._validate_org_id(org_id)

        try:
            endpoint = f"organizations/{org_id}/folders"
            params: Dict[str, Any] = {"page": page, "per_page": per_page}
            if q is not None:
                params["q"] = q

            response_data = self.sdk._make_request('GET', endpoint, params=params)

            if isinstance(response_data, dict) and 'data' in response_data:
                items = response_data['data']
            elif isinstance(response_data, list):
                items = response_data
            else:
                items = []

            return [Folder.from_dict(item) for item in items if isinstance(item, dict)]

        except TallyfyError:
            raise
        except Exception as e:
            self._handle_api_error(e, "get folders", org_id=org_id)

    def get_folder(self, org_id: str, folder_id: str) -> Folder:
        """
        Get a single folder by ID.

        Args:
            org_id: Organization ID
            folder_id: Folder ID

        Returns:
            Folder object

        Raises:
            TallyfyError: If the request fails
        """
        self._validate_org_id(org_id)
        self._validate_folder_id(folder_id)

        try:
            endpoint = f"organizations/{org_id}/folders/{folder_id}"
            response_data = self.sdk._make_request('GET', endpoint)
            return self._parse_folder(response_data)

        except TallyfyError:
            raise
        except Exception as e:
            self._handle_api_error(e, "get folder", org_id=org_id, folder_id=folder_id)

    # ── CREATE ────────────────────────────────────────────────────────────

    def create_folder(self, org_id: str, name: str,
                      parent_id: Optional[str] = None) -> Folder:
        """
        Create a new folder in the organization.

        Args:
            org_id: Organization ID
            name: Folder name (required)
            parent_id: Optional parent folder ID for nested folders

        Returns:
            Created Folder object

        Raises:
            TallyfyError: If the request fails
            ValueError: If required parameters are missing
        """
        self._validate_org_id(org_id)

        if not name or not isinstance(name, str):
            raise ValueError("name must be a non-empty string")

        try:
            endpoint = f"organizations/{org_id}/folders"
            body: Dict[str, Any] = {"name": name}
            if parent_id is not None:
                body["parent_id"] = parent_id

            response_data = self.sdk._make_request('POST', endpoint, data=body)
            return self._parse_folder(response_data)

        except TallyfyError:
            raise
        except Exception as e:
            self._handle_api_error(e, "create folder", org_id=org_id, name=name)

    # ── UPDATE ────────────────────────────────────────────────────────────

    def update_folder(self, org_id: str, folder_id: str,
                      name: Optional[str] = None,
                      parent_id: Optional[str] = None) -> Folder:
        """
        Update a folder's properties.

        Args:
            org_id: Organization ID
            folder_id: Folder ID to update
            name: New folder name
            parent_id: New parent folder ID

        Returns:
            Updated Folder object

        Raises:
            TallyfyError: If the request fails
            ValueError: If required parameters are missing
        """
        self._validate_org_id(org_id)
        self._validate_folder_id(folder_id)

        if name is None and parent_id is None:
            raise ValueError("At least one of name or parent_id must be provided")

        try:
            endpoint = f"organizations/{org_id}/folders/{folder_id}"
            body: Dict[str, Any] = {}
            if name is not None:
                body["name"] = name
            if parent_id is not None:
                body["parent_id"] = parent_id

            response_data = self.sdk._make_request('PUT', endpoint, data=body)
            return self._parse_folder(response_data)

        except TallyfyError:
            raise
        except Exception as e:
            self._handle_api_error(e, "update folder", org_id=org_id, folder_id=folder_id)

    # ── FOLDER OBJECTS ────────────────────────────────────────────────────

    def add_object_to_folder(self, org_id: str, folder_id: str,
                             object_id: str, object_type: str) -> bool:
        """
        Add an object (process, template) to a folder.

        Args:
            org_id: Organization ID
            folder_id: Folder ID to add to
            object_id: ID of the object to add
            object_type: Type of object ('run' or 'checklist')

        Returns:
            True if added successfully

        Raises:
            TallyfyError: If the request fails
            ValueError: If required parameters are missing
        """
        self._validate_org_id(org_id)
        self._validate_folder_id(folder_id)

        if not object_id or not isinstance(object_id, str):
            raise ValueError("object_id must be a non-empty string")
        if not object_type or object_type not in ('run', 'checklist'):
            raise ValueError("object_type must be 'run' or 'checklist'")

        try:
            if object_type == 'checklist':
                # GET current template to satisfy the required `title` field in UpdateChecklistRequest
                tmpl_resp = self.sdk._make_request('GET', f"organizations/{org_id}/checklists/{object_id}")
                tmpl_data = tmpl_resp.get('data', tmpl_resp) if isinstance(tmpl_resp, dict) else {}
                title = tmpl_data.get('title', '')
                if not title:
                    raise TallyfyError("Could not retrieve template title for folder assignment")
                # PUT with title (required) + folder_id (correct field for template folder membership)
                endpoint = f"organizations/{org_id}/checklists/{object_id}"
                body: Dict[str, Any] = {"title": title, "folder_id": folder_id}
                self.sdk._make_request('PUT', endpoint, data=body)
            else:
                # Processes (runs) use the add-object endpoint
                endpoint = f"organizations/{org_id}/folders/add-object"
                body = {
                    "folder_id": folder_id,
                    "subject_id": object_id,
                    "subject_type": "Run",
                }
                self.sdk._make_request('POST', endpoint, data=body)
            return True

        except TallyfyError:
            raise
        except Exception as e:
            self._handle_api_error(e, "add object to folder", org_id=org_id, folder_id=folder_id)

    def remove_object_from_folder(self, org_id: str, folder_object_id: str) -> bool:
        """
        Remove an object from a folder.

        Args:
            org_id: Organization ID
            folder_object_id: Folder-object relationship ID

        Returns:
            True if removed successfully

        Raises:
            TallyfyError: If the request fails
        """
        self._validate_org_id(org_id)

        if not folder_object_id or not isinstance(folder_object_id, str):
            raise ValueError("folder_object_id must be a non-empty string")

        try:
            endpoint = f"organizations/{org_id}/folders-objects/{folder_object_id}"
            self.sdk._make_request('DELETE', endpoint)
            return True

        except TallyfyError:
            raise
        except Exception as e:
            self._handle_api_error(e, "remove object from folder", org_id=org_id, folder_object_id=folder_object_id)

    # ── DELETE ────────────────────────────────────────────────────────────

    def delete_folder(self, org_id: str, folder_id: str) -> bool:
        """
        Delete a folder.

        Args:
            org_id: Organization ID
            folder_id: Folder ID to delete

        Returns:
            True if deleted successfully

        Raises:
            TallyfyError: If the request fails
            ValueError: If required parameters are missing
        """
        self._validate_org_id(org_id)
        self._validate_folder_id(folder_id)

        try:
            endpoint = f"organizations/{org_id}/folders/{folder_id}"
            self.sdk._make_request('DELETE', endpoint)
            return True

        except TallyfyError:
            raise
        except Exception as e:
            self._handle_api_error(e, "delete folder", org_id=org_id, folder_id=folder_id)
