"""Debug and trace tools for investigating ServiceNow runtime behavior."""

import asyncio
from collections import Counter
from typing import Any

from mcp.server.fastmcp import FastMCP

from servicenow_mcp.auth import BasicAuthProvider
from servicenow_mcp.client import ServiceNowClient
from servicenow_mcp.config import Settings
from servicenow_mcp.decorators import tool_handler
from servicenow_mcp.policy import (
    INTERNAL_QUERY_LIMIT,
    check_table_access,
    mask_audit_entry,
    mask_sensitive_fields,
)
from servicenow_mcp.utils import (
    ServiceNowQuery,
    format_response,
    validate_identifier,
)


TOOL_NAMES: list[str] = [
    "debug_trace",
    "debug_flow_execution",
    "debug_email_trace",
    "debug_integration_health",
    "debug_importset_run",
    "debug_field_mutation_story",
]


# ---------------------------------------------------------------------------
# Module-level helpers (extracted from nested tool bodies to reduce complexity)
# ---------------------------------------------------------------------------


def _build_timeline_entries(
    audit_records: list[dict[str, Any]],
    syslog_records: list[dict[str, Any]],
    journal_records: list[dict[str, Any]],
) -> list[dict[str, str]]:
    """Build a sorted timeline from audit, syslog, and journal records."""
    timeline: list[dict[str, str]] = []

    for entry in audit_records:
        masked = mask_audit_entry(entry)
        timeline.append(
            {
                "source": "sys_audit",
                "timestamp": masked.get("sys_created_on", ""),
                "user": masked.get("user", ""),
                "detail": (
                    f"Field '{masked.get('fieldname', '')}' changed "
                    f"from '{masked.get('oldvalue', '')}' "
                    f"to '{masked.get('newvalue', '')}'"
                ),
            }
        )

    for entry in syslog_records:
        masked = mask_sensitive_fields(entry)
        timeline.append(
            {
                "source": "syslog",
                "timestamp": masked.get("sys_created_on", ""),
                "user": "",
                "detail": masked.get("message", ""),
            }
        )

    for entry in journal_records:
        masked = mask_sensitive_fields(entry)
        timeline.append(
            {
                "source": "sys_journal_field",
                "timestamp": masked.get("sys_created_on", ""),
                "user": masked.get("sys_created_by", ""),
                "detail": f"[{masked.get('element', '')}] {masked.get('value', '')[:200]}",
            }
        )

    timeline.sort(key=lambda e: e["timestamp"])
    return timeline


def _build_flow_steps(log_records: list[dict[str, Any]]) -> list[dict[str, str]]:
    """Transform flow log records into step summaries."""
    steps: list[dict[str, str]] = []
    for entry in log_records:
        masked = mask_sensitive_fields(entry)
        steps.append(
            {
                "step_label": masked.get("step_label", ""),
                "state": masked.get("state", ""),
                "timestamp": masked.get("sys_created_on", ""),
                "output_data": masked.get("output_data", ""),
                "error_message": masked.get("error_message", ""),
            }
        )
    return steps


def _build_email_entries(email_records: list[dict[str, Any]]) -> list[dict[str, str]]:
    """Transform email records into email chain entries."""
    emails: list[dict[str, str]] = []
    for entry in email_records:
        masked = mask_sensitive_fields(entry)
        emails.append(
            {
                "sys_id": masked.get("sys_id", ""),
                "type": masked.get("type", ""),
                "subject": masked.get("subject", ""),
                "recipients": masked.get("recipients", ""),
                "timestamp": masked.get("sys_created_on", ""),
                "body_preview": masked.get("body_text", "")[:300],
            }
        )
    return emails


def _build_ecc_errors(records: list[dict[str, Any]]) -> list[dict[str, str]]:
    """Transform ECC queue error records into error summaries."""
    errors: list[dict[str, str]] = []
    for entry in records:
        masked = mask_sensitive_fields(entry)
        errors.append(
            {
                "sys_id": masked.get("sys_id", ""),
                "name": masked.get("name", ""),
                "queue": masked.get("queue", ""),
                "error": masked.get("error_string", ""),
                "timestamp": masked.get("sys_created_on", ""),
            }
        )
    return errors


def _build_rest_errors(records: list[dict[str, Any]]) -> list[dict[str, str]]:
    """Transform REST transaction error records into error summaries."""
    errors: list[dict[str, str]] = []
    for entry in records:
        masked = mask_sensitive_fields(entry)
        errors.append(
            {
                "sys_id": masked.get("sys_id", ""),
                "rest_message": masked.get("rest_message", ""),
                "http_method": masked.get("http_method", ""),
                "http_status": masked.get("http_status", ""),
                "endpoint": masked.get("endpoint", ""),
                "timestamp": masked.get("sys_created_on", ""),
            }
        )
    return errors


def _build_importset_summary(rows: list[dict[str, Any]]) -> tuple[dict[str, int], list[dict[str, str]]]:
    """Build import set state counts and error details from masked rows."""
    state_counts: Counter[str] = Counter()
    error_details: list[dict[str, str]] = []
    for row in rows:
        state = row.get("sys_import_state", "unknown")
        state_counts[state] += 1
        if state == "error":
            error_details.append(
                {
                    "sys_id": row.get("sys_id", ""),
                    "comment": row.get("sys_import_state_comment", ""),
                }
            )
    summary: dict[str, int] = {"total": len(rows), **dict(state_counts)}
    return summary, error_details


def _build_mutation_entries(audit_records: list[dict[str, Any]]) -> list[dict[str, str]]:
    """Transform audit records into field mutation entries."""
    mutations: list[dict[str, str]] = []
    for entry in audit_records:
        masked = mask_audit_entry(entry)
        mutations.append(
            {
                "user": masked.get("user", ""),
                "old_value": masked.get("oldvalue", ""),
                "new_value": masked.get("newvalue", ""),
                "timestamp": masked.get("sys_created_on", ""),
            }
        )
    return mutations


# ---------------------------------------------------------------------------
# Tool registration
# ---------------------------------------------------------------------------


def register_tools(mcp: FastMCP, settings: Settings, auth_provider: BasicAuthProvider) -> None:
    """Register debug/trace tools on the MCP server."""

    @mcp.tool()
    @tool_handler
    async def debug_trace(
        record_sys_id: str,
        table: str,
        minutes: int = 60,
        *,
        correlation_id: str,
    ) -> str:
        """Build a merged timeline of events for a record from sys_audit, syslog, and sys_journal_field.

        Args:
            record_sys_id: The sys_id of the record to trace.
            table: The table the record belongs to.
            minutes: How many minutes of history to include (default 60).
        """
        validate_identifier(table)
        check_table_access(table)

        async with ServiceNowClient(settings, auth_provider) as client:
            audit_query = (
                ServiceNowQuery()
                .equals("tablename", table)
                .equals("documentkey", record_sys_id)
                .minutes_ago("sys_created_on", minutes)
                .build()
            )
            syslog_query_builder = ServiceNowQuery().equals("source", table)
            if record_sys_id:
                syslog_query_builder = syslog_query_builder.equals("documentkey", record_sys_id)
            syslog_query = syslog_query_builder.minutes_ago("sys_created_on", minutes).build()
            journal_query = (
                ServiceNowQuery().equals("element_id", record_sys_id).minutes_ago("sys_created_on", minutes).build()
            )

            audit_result, syslog_result, journal_result = await asyncio.gather(
                client.query_records(
                    "sys_audit",
                    audit_query,
                    fields=["sys_id", "user", "fieldname", "oldvalue", "newvalue", "sys_created_on"],
                    limit=INTERNAL_QUERY_LIMIT,
                    order_by="sys_created_on",
                ),
                client.query_records(
                    "syslog",
                    syslog_query,
                    fields=["sys_id", "message", "source", "level", "sys_created_on"],
                    limit=INTERNAL_QUERY_LIMIT,
                    order_by="sys_created_on",
                ),
                client.query_records(
                    "sys_journal_field",
                    journal_query,
                    fields=["sys_id", "element", "value", "sys_created_on", "sys_created_by"],
                    limit=INTERNAL_QUERY_LIMIT,
                    order_by="sys_created_on",
                ),
            )

        timeline = _build_timeline_entries(
            audit_result["records"],
            syslog_result["records"],
            journal_result["records"],
        )

        return format_response(
            data={
                "record_sys_id": record_sys_id,
                "table": table,
                "event_count": len(timeline),
                "timeline": timeline,
            },
            correlation_id=correlation_id,
        )

    @mcp.tool()
    @tool_handler
    async def debug_flow_execution(context_id: str, *, correlation_id: str) -> str:
        """Inspect a Flow Designer execution: context info and step-by-step log.

        Args:
            context_id: The sys_id of the flow context (sys_flow_context).
        """
        async with ServiceNowClient(settings, auth_provider) as client:
            context = mask_sensitive_fields(await client.get_record("sys_flow_context", context_id))

            log_result = await client.query_records(
                "sys_flow_log",
                ServiceNowQuery().equals("context", context_id).build(),
                fields=["sys_id", "step_label", "state", "sys_created_on", "output_data", "error_message"],
                limit=INTERNAL_QUERY_LIMIT,
                order_by="sys_created_on",
            )

        steps = _build_flow_steps(log_result["records"])

        return format_response(
            data={
                "context": {
                    "sys_id": context.get("sys_id", ""),
                    "name": context.get("name", ""),
                    "state": context.get("state", ""),
                    "started": context.get("started", ""),
                    "ended": context.get("ended", ""),
                },
                "step_count": len(steps),
                "steps": steps,
            },
            correlation_id=correlation_id,
        )

    @mcp.tool()
    @tool_handler
    async def debug_email_trace(record_sys_id: str, *, correlation_id: str) -> str:
        """Reconstruct the email chain for a record from sys_email.

        Args:
            record_sys_id: The sys_id of the record whose emails to trace.
        """
        async with ServiceNowClient(settings, auth_provider) as client:
            email_result = await client.query_records(
                "sys_email",
                ServiceNowQuery().equals("instance", record_sys_id).build(),
                fields=["sys_id", "type", "subject", "recipients", "sys_created_on", "direct", "body_text"],
                limit=INTERNAL_QUERY_LIMIT,
                order_by="sys_created_on",
            )

        emails = _build_email_entries(email_result["records"])

        return format_response(
            data={
                "record_sys_id": record_sys_id,
                "email_count": len(emails),
                "emails": emails,
            },
            correlation_id=correlation_id,
        )

    @mcp.tool()
    @tool_handler
    async def debug_integration_health(
        kind: str = "ecc_queue",
        hours: int = 24,
        *,
        correlation_id: str,
    ) -> str:
        """Summarize recent integration errors from ecc_queue or REST message transactions.

        Args:
            kind: The integration type to check - 'ecc_queue' or 'rest_message'.
            hours: How many hours of history to review (default 24).
        """
        if kind not in ("ecc_queue", "rest_message"):
            return format_response(
                data=None,
                correlation_id=correlation_id,
                status="error",
                error=f"Unknown kind '{kind}'. Use 'ecc_queue' or 'rest_message'.",
            )

        async with ServiceNowClient(settings, auth_provider) as client:
            if kind == "ecc_queue":
                ecc_query = ServiceNowQuery().equals("state", "error").hours_ago("sys_created_on", hours).build()
                result = await client.query_records(
                    "ecc_queue",
                    ecc_query,
                    fields=["sys_id", "name", "queue", "state", "error_string", "sys_created_on"],
                    limit=INTERNAL_QUERY_LIMIT,
                    order_by="sys_created_on",
                )
                errors = _build_ecc_errors(result["records"])
            else:
                rest_query = (
                    ServiceNowQuery().greater_or_equal("http_status", "400").hours_ago("sys_created_on", hours).build()
                )
                result = await client.query_records(
                    "sys_rest_transaction",
                    rest_query,
                    fields=["sys_id", "rest_message", "http_method", "http_status", "endpoint", "sys_created_on"],
                    limit=INTERNAL_QUERY_LIMIT,
                    order_by="sys_created_on",
                )
                errors = _build_rest_errors(result["records"])

        return format_response(
            data={
                "kind": kind,
                "error_count": len(errors),
                "errors": errors,
            },
            correlation_id=correlation_id,
        )

    @mcp.tool()
    @tool_handler
    async def debug_importset_run(import_set_sys_id: str, *, correlation_id: str) -> str:
        """Inspect an import set run: header info, row-level results, and error summary.

        Args:
            import_set_sys_id: The sys_id of the import set (sys_import_set).
        """
        async with ServiceNowClient(settings, auth_provider) as client:
            import_set = mask_sensitive_fields(await client.get_record("sys_import_set", import_set_sys_id))

            rows_result = await client.query_records(
                "sys_import_set_row",
                ServiceNowQuery().equals("sys_import_set", import_set_sys_id).build(),
                fields=["sys_id", "sys_import_state", "sys_target_sys_id", "sys_import_state_comment"],
                limit=INTERNAL_QUERY_LIMIT,
                order_by="sys_created_on",
            )

        rows = [mask_sensitive_fields(row) for row in rows_result["records"]]
        summary, error_details = _build_importset_summary(rows)

        return format_response(
            data={
                "import_set": {
                    "sys_id": import_set.get("sys_id", ""),
                    "table_name": import_set.get("table_name", ""),
                    "state": import_set.get("state", ""),
                },
                "summary": summary,
                "errors": error_details,
            },
            correlation_id=correlation_id,
        )

    @mcp.tool()
    @tool_handler
    async def debug_field_mutation_story(
        table: str,
        sys_id: str,
        field: str,
        limit: int = 50,
        *,
        correlation_id: str,
    ) -> str:
        """Show the chronological mutation history of a single field on a record.

        Args:
            table: The table name.
            sys_id: The sys_id of the record.
            field: The field name to trace mutations for.
            limit: Maximum number of audit entries to return (default 50).
        """
        validate_identifier(table)
        validate_identifier(field)
        check_table_access(table)

        async with ServiceNowClient(settings, auth_provider) as client:
            audit_query = (
                ServiceNowQuery()
                .equals("tablename", table)
                .equals("documentkey", sys_id)
                .equals("fieldname", field)
                .build()
            )
            audit_result = await client.query_records(
                "sys_audit",
                audit_query,
                fields=["sys_id", "user", "fieldname", "oldvalue", "newvalue", "sys_created_on"],
                limit=limit,
                order_by="sys_created_on",
            )

        mutations = _build_mutation_entries(audit_result["records"])

        return format_response(
            data={
                "table": table,
                "sys_id": sys_id,
                "field": field,
                "mutation_count": len(mutations),
                "mutations": mutations,
            },
            correlation_id=correlation_id,
        )
