"""MCP tool for listing bugs with filtering and pagination.

This module implements the list_bugs tool following the service
layer pattern (ADR-006). The tool is a thin wrapper that:
1. Validates input with Pydantic
2. Extracts dependencies from server context (ADR-007)
3. Delegates to BugService
4. Converts exceptions to user-friendly error format

Supports two modes:
- test_ids mode: Query bugs for specific tests (original behavior)
- product_ids mode: Resolve tests from products, then query bugs
"""

from typing import Annotated, Any

from fastmcp import Context
from fastmcp.exceptions import ToolError
from pydantic import Field

from testio_mcp.exceptions import TestIOAPIError, ValidationError
from testio_mcp.schemas.api import ListBugsOutput
from testio_mcp.server import mcp
from testio_mcp.services.bug_service import BugService
from testio_mcp.utilities import (
    get_service_context,
)
from testio_mcp.utilities.parsing import parse_int_list_input, parse_list_input
from testio_mcp.utilities.schema_utils import inline_schema_refs


@mcp.tool(output_schema=inline_schema_refs(ListBugsOutput.model_json_schema()))
async def list_bugs(
    ctx: Context,
    test_ids: Annotated[
        list[int] | str | int | None,
        Field(
            description=(
                "Test IDs to scope query. Mutually exclusive with product_ids. "
                "Accepts: list of ints [123, 456], single int 123, "
                "comma-separated string '123,456', or JSON array '[123, 456]'"
            ),
            examples=[[123, 456], "123", "123,456"],
        ),
    ] = None,
    product_ids: Annotated[
        list[int] | str | int | None,
        Field(
            description=(
                "Product ID(s) to list bugs for. Mutually exclusive with test_ids. "
                "Accepts: list [598, 599], single int 598, "
                "comma-separated string '598,599', or JSON array '[598, 599]'"
            ),
            examples=[[598, 599], 598, "598,599", "[598, 599]"],
        ),
    ] = None,
    start_date: Annotated[
        str | None,
        Field(
            description=(
                "Start date for test filtering (ISO 8601 or business terms). "
                "Requires product_ids. Filters tests by end_at >= start_date."
            ),
            examples=["2025-07-01", "last 30 days", "this quarter"],
        ),
    ] = None,
    end_date: Annotated[
        str | None,
        Field(
            description=(
                "End date for test filtering (ISO 8601 or business terms). "
                "Requires product_ids. Filters tests by end_at <= end_date."
            ),
            examples=["2025-10-31", "today"],
        ),
    ] = None,
    statuses: Annotated[
        str | list[str] | None,
        Field(
            description=(
                "Filter tests by status. Only used with product_ids. "
                "Default: excludes initialized, cancelled (executed tests only). "
                "Values: running, locked, archived, cancelled, customer_finalized, initialized"
            ),
            examples=["locked", "running,locked", ["locked"], ["running", "locked"]],
        ),
    ] = None,
    testing_type: Annotated[
        str | list[str] | None,
        Field(
            description=(
                "Filter tests by type. Only used with product_ids. "
                "Values: coverage, focused, rapid, custom"
            ),
            examples=["coverage", "focused", "coverage,focused", ["coverage", "focused"]],
        ),
    ] = None,
    status: Annotated[
        str | list[str] | None,
        Field(
            description=(
                "Filter by bug status. "
                'Accepts: list ["rejected"], comma-separated "rejected,forwarded", '
                "or JSON array '[\"rejected\"]'"
            ),
            examples=["rejected", ["rejected", "forwarded"], "rejected,forwarded"],
        ),
    ] = None,
    severity: Annotated[
        str | list[str] | None,
        Field(
            description=(
                "Filter by severity. "
                'Accepts: list ["critical"], comma-separated "critical,high", '
                "or JSON array '[\"critical\"]'"
            ),
            examples=["critical", ["critical", "high"], "critical,high"],
        ),
    ] = None,
    rejection_reason: Annotated[
        str | list[str] | None,
        Field(
            description=(
                "Filter by rejection reason. "
                'Accepts: list ["test_is_invalid"], comma-separated, or JSON array'
            ),
            examples=["test_is_invalid", ["test_is_invalid", "no_reproduction"]],
        ),
    ] = None,
    reported_by_user_id: Annotated[
        int | None,
        Field(gt=0, description="Filter by reporting user ID", examples=[12345]),
    ] = None,
    feature_ids: Annotated[
        list[int] | str | int | None,
        Field(
            description=(
                "Filter by feature IDs (bugs reported for these features). "
                "Accepts: list [123, 456], single int 123, comma-separated '123,456', "
                "or JSON array '[123, 456]'"
            ),
            examples=[[196992], "196992", "196992,196993"],
        ),
    ] = None,
    page: Annotated[
        int,
        Field(ge=1, description="Page number (1-indexed)"),
    ] = 1,
    per_page: Annotated[
        int,
        Field(
            ge=1,
            le=200,
            description="Items per page (default: 100, max: 200)",
        ),
    ] = 0,  # 0 means use settings default
    offset: Annotated[
        int,
        Field(
            ge=0,
            description="Starting offset (0-indexed). Combines with page: "
            "offset + (page-1)*per_page. "
            "Example: To get items 50-99, use offset=50, per_page=50",
        ),
    ] = 0,
    sort_by: Annotated[
        str,
        Field(
            description="Sort field. Values: reported_at, severity, status, title",
            examples=["reported_at", "severity", "status", "title"],
        ),
    ] = "reported_at",
    sort_order: Annotated[
        str,
        Field(
            description="Sort order. Values: asc, desc",
            examples=["desc", "asc"],
        ),
    ] = "desc",
    include: Annotated[
        str | list[str] | None,
        Field(
            description=(
                "Detail fields to include inline in each bug. "
                "Without this, only minimal fields are returned. "
                "Allowed values: actual_result, expected_result, steps. "
                "Accepts: comma-separated 'actual_result,expected_result,steps' or list"
            ),
            examples=["actual_result,expected_result,steps", ["actual_result", "steps"]],
        ),
    ] = None,
) -> dict[str, Any]:
    """List bugs for specified tests with filters, pagination, and sorting.

    Returns minimal bug information for quick scanning and filtering.
    Results are scoped to the specified test_ids to prevent mass data fetch.

    Supports two modes:
    - test_ids mode: Query bugs for specific tests (original behavior)
    - product_ids mode: Resolve tests from products, then query bugs.
      When using product_ids, bugs are sorted by test end_at DESC first
      (most recent test cycles first), then by sort_by.
    """
    # Parse inputs from various formats (AI-friendly)
    parsed_test_ids = parse_int_list_input(test_ids)
    parsed_product_ids = parse_int_list_input(product_ids)

    # Validate mutual exclusivity: test_ids XOR product_ids
    if parsed_test_ids and parsed_product_ids:
        raise ToolError(
            "❌ Cannot specify both product_ids and test_ids\n"
            "ℹ️  These parameters are mutually exclusive\n"
            "💡 Use product_ids for product-wide queries, test_ids for specific tests"
        )

    if not parsed_test_ids and not parsed_product_ids:
        raise ToolError(
            "❌ Either product_ids or test_ids is required\n"
            "ℹ️  Provide one of these parameters to scope the query\n"
            "💡 Use product_ids for product-wide queries, test_ids for specific tests"
        )

    # Validate test filter params only valid with product_ids
    is_product_mode = parsed_product_ids is not None
    if not is_product_mode:
        if start_date or end_date:
            raise ToolError(
                "❌ Date filters require product_ids mode\n"
                "ℹ️  start_date and end_date cannot be used with test_ids\n"
                "💡 Use product_ids instead if you need date filtering"
            )
        if statuses or testing_type:
            raise ToolError(
                "❌ Test status/type filters require product_ids mode\n"
                "ℹ️  statuses and testing_type filter tests, not bugs\n"
                "💡 Use product_ids instead, or use status filter for bug status"
            )

    # Parse flexible input formats for filter parameters
    # Handles: list, comma-separated string, JSON array string
    parsed_status = parse_list_input(status)
    parsed_severity = parse_list_input(severity)
    parsed_rejection_reason = parse_list_input(rejection_reason)
    parsed_feature_ids = parse_int_list_input(feature_ids) if feature_ids else None
    parsed_statuses = parse_list_input(statuses)
    parsed_testing_type = parse_list_input(testing_type)
    parsed_include = parse_list_input(include)

    # Create service with managed AsyncSession lifecycle (STORY-033)
    async with get_service_context(ctx, BugService) as service:
        # Get settings for default page size
        from testio_mcp.config import settings

        # Use default per_page from settings if not specified (0 = use default)
        effective_per_page = per_page if per_page > 0 else settings.TESTIO_DEFAULT_PAGE_SIZE

        # Delegate to service and convert exceptions to MCP error format
        try:
            service_result = await service.list_bugs(
                test_ids=parsed_test_ids,
                product_ids=parsed_product_ids,
                start_date=start_date,
                end_date=end_date,
                statuses=parsed_statuses,
                testing_type=parsed_testing_type,
                status=parsed_status,
                severity=parsed_severity,
                rejection_reason=parsed_rejection_reason,
                reported_by_user_id=reported_by_user_id,
                feature_ids=parsed_feature_ids,
                page=page,
                per_page=effective_per_page,
                offset=offset,
                sort_by=sort_by,
                sort_order=sort_order,
                include=parsed_include,
            )

            # Extract components from service result
            bugs = service_result["bugs"]
            pagination = service_result["pagination"]
            filters_applied = service_result["filters_applied"]
            warnings = service_result.get("warnings")

            # Empty results guidance
            if len(bugs) == 0:
                import logging

                logger = logging.getLogger(__name__)
                has_filters = (
                    parsed_status
                    or parsed_severity
                    or parsed_rejection_reason
                    or reported_by_user_id
                )
                filter_desc = "with specified filters" if has_filters else "for these tests"
                logger.info(
                    f"ℹ️  No bugs found {filter_desc}\n"
                    f"💡 Try removing filters or checking other tests"
                )

            # Build validated output using BugListItem
            from testio_mcp.schemas.api import BugListItem

            bug_items = [
                BugListItem(
                    id=bug["id"],
                    title=bug["title"],
                    severity=bug["severity"],
                    status=bug["status"],
                    rejection_reason=bug.get("rejection_reason"),
                    test_id=bug["test_id"],
                    feature_id=bug.get("feature_id"),
                    reported_at=bug["reported_at"],
                    has_report=bug.get("has_report", False),
                    actual_result=bug.get("actual_result"),
                    expected_result=bug.get("expected_result"),
                    steps=bug.get("steps"),
                )
                for bug in bugs
            ]

            output = ListBugsOutput(
                bugs=bug_items,
                pagination=pagination,
                filters_applied=filters_applied,
                warnings=warnings,
            )

            return output.model_dump(by_alias=True, exclude_none=True)

        except ValidationError as e:
            # Convert validation error to ToolError with user-friendly message
            raise ToolError(
                f"❌ Validation error: {e.message}\n"
                f"ℹ️  Field: {e.field}\n"
                f"💡 Check your input parameters and try again"
            ) from e

        except TestIOAPIError as e:
            # Convert API error to ToolError with user-friendly message
            raise ToolError(
                f"❌ API error: {e.message}\n"
                f"ℹ️  HTTP status code: {e.status_code}\n"
                f"💡 Check API status and try again. If the problem persists, contact support."
            ) from e

        except Exception as e:
            # Catch-all for unexpected errors
            raise ToolError(
                f"❌ Unexpected error: {str(e)}\n"
                f"ℹ️  An unexpected error occurred while listing bugs\n"
                f"💡 Please try again or contact support if the problem persists"
            ) from e
