"""Module for Jira Forms REST API operations.

This module provides support for the Jira Forms REST API at
https://api.atlassian.com/jira/forms/cloud/{cloudId}.

Features:
- UUID-based form IDs
- Atlassian Document Format (ADF) for form layouts
- Direct field updates via PUT /form/{formId}
- Support for form templates, attachments, and exports
"""

import logging
import os
from typing import Any

import requests
from requests.auth import HTTPBasicAuth
from requests.exceptions import HTTPError

from ..models.jira import ProFormaForm
from .client import JiraClient
from .forms_common import handle_forms_http_error

logger = logging.getLogger("mcp-jira")


class FormsApiMixin(JiraClient):
    """Mixin for Jira Forms REST API operations.

    This uses the Forms API at https://api.atlassian.com/jira/forms/cloud/{cloudId}.
    The cloud_id is obtained from OAuth config or must be provided via environment variable.
    """

    def __init__(self, *args: Any, **kwargs: Any) -> None:
        """Initialize the Forms API mixin.

        Note:
            cloud_id is validated lazily when Forms API methods are called.
        """
        super().__init__(*args, **kwargs)

        # Get cloud_id from OAuth config if available, otherwise from environment variable
        if self.config.oauth_config and self.config.oauth_config.cloud_id:
            self._cloud_id: str | None = self.config.oauth_config.cloud_id
        else:
            # For non-OAuth auth types, check environment variable
            self._cloud_id = os.getenv("ATLASSIAN_OAUTH_CLOUD_ID")

    def _make_forms_api_request(
        self,
        method: str,
        endpoint: str,
        data: dict[str, Any] | None = None,
    ) -> dict[str, Any]:
        """Make a request to the Forms API.

        Supports all authentication types: OAuth, PAT, and Basic Auth.

        Args:
            method: HTTP method (GET, POST, PUT, DELETE)
            endpoint: API endpoint path (e.g., '/issue/PROJ-123/form')
            data: Optional request body data

        Returns:
            Response data from the API as a dictionary

        Raises:
            ValueError: If cloud_id is not configured
            MCPAtlassianAuthenticationError: For 403 permission errors
            ValueError: For 404 not found errors
            Exception: For other HTTP errors
        """
        # Validate cloud_id is available
        if not self._cloud_id:
            error_msg = (
                "Forms API requires a cloud_id. "
                "For OAuth, this is automatically retrieved. "
                "For other auth types, provide it via ATLASSIAN_OAUTH_CLOUD_ID environment variable "
                "or X-Atlassian-Cloud-Id header."
            )
            raise ValueError(error_msg)

        url = f"https://api.atlassian.com/jira/forms/cloud/{self._cloud_id}{endpoint}"
        headers = {"Accept": "application/json", "Content-Type": "application/json"}

        # Determine authentication method based on config
        auth = None
        if self.config.auth_type == "oauth":
            # For OAuth, use the session from the Jira client which has OAuth configured
            if hasattr(self.jira, "session") and self.jira.session:
                try:
                    response = self.jira.session.request(
                        method=method,
                        url=url,
                        headers=headers,
                        json=data,
                        timeout=30,
                    )
                    response.raise_for_status()

                    # Handle empty responses (like DELETE)
                    if not response.content:
                        return {}

                    json_response: dict[str, Any] = response.json()
                    return json_response
                except HTTPError as e:
                    logger.error(
                        f"HTTP error in Forms API (OAuth): {e} - Response: {e.response.text[:500]}"
                    )
                    raise handle_forms_http_error(
                        e, "Forms API request", endpoint
                    ) from e
                except requests.RequestException as e:
                    logger.error(
                        f"Request error making Forms API request to {endpoint}: {str(e)}"
                    )
                    raise
            else:
                error_msg = "OAuth session not available in Jira client"
                raise ValueError(error_msg)
        elif self.config.auth_type == "pat":
            # For PAT, use Bearer token authentication
            if self.config.personal_token:
                headers["Authorization"] = f"Bearer {self.config.personal_token}"
            else:
                error_msg = "Personal access token not configured"
                raise ValueError(error_msg)
        else:  # basic auth
            # For basic auth, use HTTPBasicAuth with username and API token
            username = self.jira.username or ""
            password = self.jira.password or ""
            auth = HTTPBasicAuth(username, password)

        # For PAT and basic auth, make the request with requests library
        if self.config.auth_type in ["pat", "basic"]:
            try:
                response = requests.request(
                    method=method,
                    url=url,
                    auth=auth,
                    headers=headers,
                    json=data,
                    timeout=30,
                )
                response.raise_for_status()

                # Handle empty responses (like DELETE)
                if not response.content:
                    return {}

                return response.json()

            except HTTPError as e:
                logger.error(
                    f"HTTP error in Forms API ({self.config.auth_type}): {e} - Response: {e.response.text[:500]}"
                )
                raise handle_forms_http_error(e, "Forms API request", endpoint) from e
            except requests.RequestException as e:
                logger.error(
                    f"Request error making Forms API request to {endpoint}: {str(e)}"
                )
                raise

        error_msg = f"Unsupported auth type: {self.config.auth_type}"
        raise ValueError(error_msg)

    def get_issue_forms(self, issue_key: str) -> list[ProFormaForm]:
        """Get all forms associated with an issue.

        Args:
            issue_key: The issue key (e.g. 'PROJ-123')

        Returns:
            List of ProFormaForm objects

        Raises:
            Exception: If there is an error getting forms
        """
        try:
            response = self._make_forms_api_request("GET", f"/issue/{issue_key}/form")

            # API returns a plain array of forms
            forms_data = (
                response if isinstance(response, list) else response.get("forms", [])
            )

            forms = []
            for form_data in forms_data:
                try:
                    # API returns a simplified list format
                    # We'll need to fetch details for each form to get full data
                    form = ProFormaForm.from_api_response(
                        form_data, issue_key=issue_key
                    )
                    forms.append(form)
                except (KeyError, TypeError, ValueError) as e:
                    logger.error(f"Error parsing form data: {str(e)}")
                    continue

            return forms

        except ValueError as e:
            # Re-raise configuration errors (e.g., missing cloud_id) to fail fast
            if "cloud_id" in str(e).lower() or "forms api requires" in str(e).lower():
                raise
            # 404 - no forms found, return empty list
            return []
        except Exception as e:
            logger.error(f"Error getting forms for issue {issue_key}: {str(e)}")
            raise

    def get_form_details(self, issue_key: str, form_id: str) -> ProFormaForm | None:
        """Get detailed information about a specific form.

        Args:
            issue_key: The issue key (e.g. 'PROJ-123')
            form_id: The form UUID (e.g. '1946b8b7-8f03-4dc0-ac2d-5fac0d960c6a')

        Returns:
            ProFormaForm object or None if not found

        Raises:
            Exception: If there is an error getting form details
        """
        try:
            response = self._make_forms_api_request(
                "GET", f"/issue/{issue_key}/form/{form_id}"
            )

            # API returns ADF (Atlassian Document Format) structure
            form = ProFormaForm.from_api_response(response, issue_key=issue_key)
            return form

        except ValueError as e:
            # Re-raise configuration errors (e.g., missing cloud_id) to fail fast
            if "cloud_id" in str(e).lower() or "forms api requires" in str(e).lower():
                raise
            # 404 - form not found, return None
            return None
        except Exception as e:
            logger.error(
                f"Error getting form details for {issue_key}/{form_id}: {str(e)}"
            )
            raise

    def update_form_answers(
        self, issue_key: str, form_id: str, answers: list[dict[str, Any]]
    ) -> dict[str, Any]:
        """Update form field answers directly via the Forms API.

        **Known Limitation - DATETIME fields:**
        The Jira Forms API does not properly preserve time components in DATETIME fields.
        When updating DATETIME fields through this method, only the date is stored and the
        time is reset to midnight (00:00:00).

        **Workaround for DATETIME fields:**
        Use the regular Jira API to update the underlying custom fields directly:
        ```python
        jira.update_issue(issue_key, fields={
            "customfield_XXXXX": "2026-01-09T11:50:00-08:00"
        })
        ```
        The custom field ID can be found in the form's question definition (jiraField property).

        Args:
            issue_key: The issue key (e.g. 'PROJ-123')
            form_id: The form UUID
            answers: List of answer objects, each with:
                - questionId: ID of the question to answer
                - type: Answer type (TEXT, NUMBER, DATE, etc.)
                - value: The answer value

        Returns:
            Response data from the API

        Raises:
            Exception: If there is an error updating the form
        """
        try:
            # Transform answers from list format to the API's expected object format
            # API expects: {"answers": {"questionId": {"type": value}, ...}}
            # We receive: [{"questionId": "1", "type": "TEXT", "value": "foo"}, ...]
            answers_dict = {}
            for answer in answers:
                question_id = answer.get("questionId")
                answer_type = answer.get("type", "TEXT")
                value = answer.get("value")

                # Map answer types to API field names
                # NOTE: DATETIME maps to "date" because the Forms API doesn't support
                # separate datetime fields. This is a known API limitation - only the
                # date portion is stored and time is lost. For datetime fields that need
                # time precision, use the regular Jira API to update the custom field directly.
                type_mapping = {
                    "TEXT": "text",
                    "NUMBER": "number",
                    "DATE": "date",
                    "DATETIME": "date",  # ⚠️ API limitation: loses time component
                    "TIME": "time",
                    "SELECT": "choices",
                    "MULTI_SELECT": "choices",
                    "CHECKBOX": "choices",
                    "USER": "users",
                    "MULTI_USER": "users",
                }

                field_name = type_mapping.get(answer_type, "text")

                # For choices/users, ensure value is an array
                if field_name in ("choices", "users") and not isinstance(value, list):
                    value = [value] if value else []

                answers_dict[question_id] = {field_name: value}

            request_body = {"answers": answers_dict}

            response = self._make_forms_api_request(
                "PUT", f"/issue/{issue_key}/form/{form_id}", data=request_body
            )

            logger.info(f"Successfully updated form {form_id} for issue {issue_key}")
            return response

        except Exception as e:
            logger.error(f"Error updating form {form_id} for {issue_key}: {str(e)}")
            raise

    def add_form_template(self, issue_key: str, template_id: str) -> dict[str, Any]:
        """Add a form template to an issue.

        Args:
            issue_key: The issue key (e.g. 'PROJ-123')
            template_id: The form template UUID

        Returns:
            Response data from the API

        Raises:
            Exception: If there is an error adding the template
        """
        try:
            request_body = {"formTemplateId": template_id}

            response = self._make_forms_api_request(
                "POST", f"/issue/{issue_key}/form", data=request_body
            )

            logger.info(f"Successfully added form template to issue {issue_key}")
            return response

        except Exception as e:
            logger.error(f"Error adding form template to {issue_key}: {str(e)}")
            raise

    def delete_form(self, issue_key: str, form_id: str) -> None:
        """Delete a form from an issue.

        Args:
            issue_key: The issue key (e.g. 'PROJ-123')
            form_id: The form UUID

        Raises:
            Exception: If there is an error deleting the form
        """
        try:
            self._make_forms_api_request("DELETE", f"/issue/{issue_key}/form/{form_id}")

            logger.info(f"Successfully deleted form {form_id} from issue {issue_key}")

        except Exception as e:
            logger.error(f"Error deleting form {form_id} from {issue_key}: {str(e)}")
            raise

    def get_form_attachments(
        self, issue_key: str, form_id: str
    ) -> list[dict[str, Any]]:
        """Get attachment metadata for a form.

        Args:
            issue_key: The issue key (e.g. 'PROJ-123')
            form_id: The form UUID

        Returns:
            List of attachment metadata

        Raises:
            Exception: If there is an error getting attachments
        """
        try:
            response = self._make_forms_api_request(
                "GET", f"/issue/{issue_key}/form/{form_id}/attachment"
            )

            # API should return attachments wrapped in an 'attachments' key
            attachments = response.get("attachments", [])
            if not isinstance(attachments, list):
                return []

            return attachments

        except ValueError:
            # 404 - no attachments
            return []
        except Exception as e:
            logger.error(f"Error getting attachments for form {form_id}: {str(e)}")
            raise
