"""Process extracted data tool (Phase 2)."""

import json
from datetime import datetime, timezone
from uuid import uuid4

import structlog
from mcp.types import TextContent, Tool

from maeris_mcp.schemas.registry import SchemaRegistry
from maeris_mcp.storage.api_store import APIStore
from maeris_mcp.types.api import (
    APICallSite,
    APISummary,
    AuthDefinition,
    AuthType,
    ExtractedAPI,
    ExtractedAPICollection,
    HeaderDefinition,
    HTTPMethod,
    PathParam,
    PayloadDefinition,
    PayloadField,
    QueryParam,
)
logger = structlog.get_logger()

from maeris_mcp.types.protocol import (
    AnalysisResult,
    DocumentationResult,
    MatchContext,
    OperationType,
    ProcessingResponse,
    SearchMatch,
    SearchResult,
)


def process_extracted_data_tool() -> Tool:
    """Return the tool definition for process_extracted_data."""
    return Tool(
        name="process_extracted_data",
        description=(
            "Processes structured data extracted from the codebase according to a schema. "
            "This is Phase 2 of the schema-first protocol. The data should conform to the "
            "schema returned by get_extraction_schema."
        ),
        inputSchema={
            "type": "object",
            "required": ["schema_id", "operation", "extracted_data"],
            "properties": {
                "schema_id": {
                    "type": "string",
                    "description": "The schema ID returned from get_extraction_schema",
                },
                "operation": {
                    "type": "string",
                    "description": (
                        "The operation: analyze_component, analyze_project, generate_docs, "
                        "search_components, search_functions, find_patterns, extract_apis"
                    ),
                },
                "extracted_data": {
                    "type": "string",
                    "description": "JSON string containing the extracted data conforming to the schema",
                },
                "files_analyzed": {
                    "type": "string",
                    "description": "Comma-separated list of files that were analyzed",
                },
            },
        },
    )


async def handle_process_extracted_data(
    registry: SchemaRegistry,
    api_store: APIStore,
    arguments: dict,
) -> list[TextContent]:
    """Handle the process_extracted_data tool call."""
    schema_id = arguments.get("schema_id")
    if not schema_id:
        return [TextContent(type="text", text="Error: schema_id is required")]

    schema = registry.get(schema_id)
    if not schema:
        return [TextContent(type="text", text=f"Error: unknown schema_id: {schema_id}")]

    operation = arguments.get("operation")
    if not operation:
        return [TextContent(type="text", text="Error: operation is required")]

    extracted_data_str = arguments.get("extracted_data")
    if not extracted_data_str:
        return [TextContent(type="text", text="Error: extracted_data is required")]

    try:
        extracted_data = json.loads(extracted_data_str)
    except json.JSONDecodeError as e:
        return [TextContent(type="text", text=f"Error: failed to parse extracted_data JSON: {e}")]

    try:
        op_type = OperationType(operation)
    except ValueError:
        return [TextContent(type="text", text=f"Error: unsupported operation: {operation}")]

    # Process based on operation type
    result: AnalysisResult | DocumentationResult | SearchResult | dict
    match op_type:
        case OperationType.ANALYZE_COMPONENT:
            result = _process_component_analysis(extracted_data)
        case OperationType.ANALYZE_PROJECT:
            result = _process_project_analysis(extracted_data)
        case OperationType.GENERATE_DOCS:
            result = _process_documentation(extracted_data)
        case OperationType.SEARCH_COMPONENTS | OperationType.SEARCH_FUNCTIONS | OperationType.FIND_PATTERNS:
            result = _process_search(extracted_data)
        case OperationType.EXTRACT_APIS:
            result = _process_api_extraction(extracted_data, api_store)
        case _:
            return [TextContent(type="text", text=f"Error: unsupported operation: {operation}")]

    response = ProcessingResponse(success=True, result=result)
    response_json = json.dumps(response.model_dump(by_alias=True, exclude_none=True), indent=2)
    return [TextContent(type="text", text=response_json)]


def _process_component_analysis(data: dict) -> AnalysisResult:
    """Analyze extracted component data."""
    component_name = data.get("componentName", "")
    component_type = data.get("componentType", "")

    props_count = len(data.get("props", []) or [])
    hooks_count = len(data.get("hooks", []) or [])
    state_count = len(data.get("localState", []) or [])
    child_count = len(data.get("childComponents", []) or [])

    patterns: list[str] = []
    if p := data.get("patterns"):
        patterns = [ps for ps in p if isinstance(ps, str)]

    complexity_score = props_count + hooks_count * 2 + state_count * 2 + child_count
    complexity = "low" if complexity_score <= 7 else "medium" if complexity_score <= 15 else "high"

    summary = f"{component_name} is a {component_type} React component"
    if props_count > 0:
        summary += f" with {props_count} props"
    if hooks_count > 0:
        summary += f", uses {hooks_count} hooks"
    if patterns:
        summary += f". Detected patterns: {patterns}"

    return AnalysisResult(
        type="analysis",
        summary=summary,
        details={
            "componentName": component_name,
            "componentType": component_type,
            "propsCount": props_count,
            "hooksCount": hooks_count,
            "stateCount": state_count,
            "childCount": child_count,
            "patterns": patterns,
            "complexity": complexity,
        },
        metrics={
            "propsCount": float(props_count),
            "hooksCount": float(hooks_count),
            "stateCount": float(state_count),
            "childCount": float(child_count),
            "complexityScore": float(complexity_score),
        },
    )


def _process_project_analysis(data: dict) -> AnalysisResult:
    """Analyze extracted project data."""
    project_name = data.get("projectName", "")

    component_count = len(data.get("components", []) or [])
    hook_count = len(data.get("customHooks", []) or [])

    tech_stack: dict[str, str] = {}
    if ts := data.get("techStack"):
        if isinstance(ts, dict):
            for k, v in ts.items():
                if isinstance(v, str):
                    tech_stack[k] = v

    summary = f"Project '{project_name}' contains {component_count} components and {hook_count} custom hooks"
    if tech_stack:
        summary += f". Tech stack: {tech_stack}"

    return AnalysisResult(
        type="analysis",
        summary=summary,
        details={
            "projectName": project_name,
            "componentCount": component_count,
            "hookCount": hook_count,
            "techStack": tech_stack,
        },
        metrics={
            "componentCount": float(component_count),
            "hookCount": float(hook_count),
        },
    )


def _process_documentation(data: dict) -> DocumentationResult:
    """Generate documentation from extracted data."""
    component_name = data.get("componentName", "")
    jsdoc = data.get("jsdocComment", "")

    doc = f"# {component_name}\n\n"

    if jsdoc:
        doc += f"{jsdoc}\n\n"

    # Props section
    if props := data.get("props"):
        if isinstance(props, list) and props:
            doc += "## Props\n\n"
            doc += "| Name | Type | Required | Description |\n"
            doc += "|------|------|----------|-------------|\n"
            for p in props:
                if isinstance(p, dict):
                    name = p.get("name", "")
                    prop_type = p.get("type", "")
                    required = p.get("required", False)
                    desc = p.get("description", "")
                    req_str = "Yes" if required else "No"
                    doc += f"| {name} | `{prop_type}` | {req_str} | {desc} |\n"
            doc += "\n"

    # Hooks section
    if hooks := data.get("hooks"):
        if isinstance(hooks, list) and hooks:
            doc += "## Hooks Used\n\n"
            for h in hooks:
                if isinstance(h, dict):
                    name = h.get("hookName", "")
                    is_custom = h.get("isCustom", False)
                    custom_str = " (custom)" if is_custom else ""
                    doc += f"- `{name}`{custom_str}\n"
            doc += "\n"

    return DocumentationResult(type="documentation", content=doc, format="markdown")


def _process_search(data: dict) -> SearchResult:
    """Process search results."""
    matches: list[SearchMatch] = []

    if raw_matches := data.get("matches"):
        if isinstance(raw_matches, list):
            for m in raw_matches:
                if isinstance(m, dict):
                    name = m.get("name", "")
                    file_path = m.get("filePath", "")
                    match_type = m.get("matchType", "")
                    signature = m.get("signature", "")
                    description = m.get("description", "")

                    context = None
                    if signature or description:
                        context = MatchContext(signature=signature, description=description)

                    matches.append(
                        SearchMatch(
                            name=name,
                            file_path=file_path,
                            match_type=match_type,
                            context=context,
                        )
                    )

    return SearchResult(type="search", matches=matches, total_count=len(matches))


def _process_api_extraction(data: dict, store: APIStore) -> dict:
    """Process extracted API data and store it."""
    project_name = data.get("projectName", "")
    root_path = data.get("rootPath", "")

    apis: list[ExtractedAPI] = []
    if raw_apis := data.get("apis"):
        if isinstance(raw_apis, list):
            for raw_api in raw_apis:
                if isinstance(raw_api, dict):
                    api = _parse_extracted_api(raw_api)
                    apis.append(api)

    http_clients: list[str] = []
    if clients := data.get("httpClients"):
        if isinstance(clients, list):
            http_clients = [c for c in clients if isinstance(c, str)]

    files_analyzed: list[str] = []
    if files := data.get("filesAnalyzed"):
        if isinstance(files, list):
            files_analyzed = [f for f in files if isinstance(f, str)]

    summary = _build_api_summary(apis)

    collection = ExtractedAPICollection(
        project_name=project_name,
        root_path=root_path,
        apis=apis,
        extraction_time=datetime.now(timezone.utc).isoformat(),
        files_analyzed=files_analyzed,
        http_clients=http_clients if http_clients else None,
        summary=summary,
    )

    storage_id = store.store(project_name, collection)

    return {
        "type": "api_extraction",
        "storageId": storage_id,
        "summary": summary.model_dump(by_alias=True),
        "message": f"Successfully extracted {len(apis)} APIs from {project_name} and stored with ID: {storage_id}",
    }


def _parse_extracted_api(data: dict) -> ExtractedAPI:
    """Parse a single API from the extracted data map."""
    api_id = str(uuid4())

    # Basic fields
    name = data.get("name", "")
    endpoint = data.get("endpoint", "")
    base_url = data.get("baseUrl")
    method_str = data.get("method", "GET")
    try:
        method = HTTPMethod(method_str)
    except ValueError:
        logger.warning("invalid_http_method", method=method_str, fallback="GET")
        method = HTTPMethod.GET

    description = data.get("description")
    error_handling = data.get("errorHandling")
    is_deprecated = data.get("isDeprecated", False)

    # Parse headers
    headers: list[HeaderDefinition] = []
    if raw_headers := data.get("headers"):
        if isinstance(raw_headers, list):
            for h in raw_headers:
                if isinstance(h, dict):
                    headers.append(
                        HeaderDefinition(
                            name=h.get("name", ""),
                            value=h.get("value"),
                            source=h.get("source"),
                            is_required=h.get("isRequired", False),
                        )
                    )

    # Parse query params
    query_params: list[QueryParam] = []
    if raw_qp := data.get("queryParams"):
        if isinstance(raw_qp, list):
            for qp in raw_qp:
                if isinstance(qp, dict):
                    query_params.append(
                        QueryParam(
                            name=qp.get("name", ""),
                            type=qp.get("type", ""),
                            required=qp.get("required", False),
                            description=qp.get("description"),
                        )
                    )

    # Parse path params
    path_params: list[PathParam] = []
    if raw_pp := data.get("pathParams"):
        if isinstance(raw_pp, list):
            for pp in raw_pp:
                if isinstance(pp, dict):
                    path_params.append(
                        PathParam(
                            name=pp.get("name", ""),
                            type=pp.get("type", ""),
                            description=pp.get("description"),
                        )
                    )

    # Parse request payload
    request_payload = None
    if raw_req := data.get("requestPayload"):
        if isinstance(raw_req, dict):
            request_payload = _parse_payload_definition(raw_req)

    # Parse response payload
    response_payload = None
    if raw_res := data.get("responsePayload"):
        if isinstance(raw_res, dict):
            response_payload = _parse_payload_definition(raw_res)

    # Parse auth
    auth = None
    if raw_auth := data.get("auth"):
        if isinstance(raw_auth, dict):
            auth_type_str = raw_auth.get("type", "none")
            try:
                auth_type = AuthType(auth_type_str)
            except ValueError:
                logger.warning("invalid_auth_type", auth_type=auth_type_str, fallback="none")
                auth_type = AuthType.NONE
            auth = AuthDefinition(
                type=auth_type,
                header_name=raw_auth.get("headerName"),
                token_source=raw_auth.get("tokenSource"),
            )

    # Parse call sites
    call_sites: list[APICallSite] = []
    if raw_cs := data.get("callSites"):
        if isinstance(raw_cs, list):
            for cs in raw_cs:
                if isinstance(cs, dict):
                    line_num = cs.get("lineNumber")
                    if isinstance(line_num, float):
                        line_num = int(line_num)
                    call_sites.append(
                        APICallSite(
                            file_path=cs.get("filePath", ""),
                            component_name=cs.get("componentName"),
                            function_name=cs.get("functionName"),
                            hook_name=cs.get("hookName"),
                            line_number=line_num,
                        )
                    )

    # Parse tags
    tags: list[str] = []
    if raw_tags := data.get("tags"):
        if isinstance(raw_tags, list):
            tags = [t for t in raw_tags if isinstance(t, str)]

    return ExtractedAPI(
        id=api_id,
        name=name,
        endpoint=endpoint,
        base_url=base_url,
        method=method,
        headers=headers if headers else None,
        query_params=query_params if query_params else None,
        path_params=path_params if path_params else None,
        request_payload=request_payload,
        response_payload=response_payload,
        auth=auth,
        call_sites=call_sites,
        description=description,
        tags=tags if tags else None,
        is_deprecated=is_deprecated,
        error_handling=error_handling,
    )


def _parse_payload_definition(data: dict) -> PayloadDefinition:
    """Parse a payload definition from map."""
    content_type = data.get("contentType", "")
    type_name = data.get("typeName")
    is_array = data.get("isArray", False)

    fields: list[PayloadField] = []
    if raw_fields := data.get("fields"):
        if isinstance(raw_fields, list):
            fields = _parse_payload_fields(raw_fields)

    return PayloadDefinition(
        content_type=content_type,
        fields=fields if fields else None,
        is_array=is_array,
        type_name=type_name,
    )


def _parse_payload_fields(fields: list) -> list[PayloadField]:
    """Recursively parse payload fields."""
    result: list[PayloadField] = []

    for f in fields:
        if isinstance(f, dict):
            children = None
            if raw_children := f.get("children"):
                if isinstance(raw_children, list):
                    children = _parse_payload_fields(raw_children)

            result.append(
                PayloadField(
                    name=f.get("name", ""),
                    type=f.get("type", ""),
                    required=f.get("required", False),
                    description=f.get("description"),
                    children=children if children else None,
                )
            )

    return result


def _build_api_summary(apis: list[ExtractedAPI]) -> APISummary:
    """Create a summary from extracted APIs."""
    by_method: dict[str, int] = {}
    by_auth_type: dict[str, int] = {}
    endpoints: set[str] = set()

    for api in apis:
        by_method[api.method] = by_method.get(api.method, 0) + 1

        auth_type = api.auth.type if api.auth else "none"
        by_auth_type[auth_type] = by_auth_type.get(auth_type, 0) + 1

        endpoints.add(api.endpoint)

    return APISummary(
        total_apis=len(apis),
        by_method=by_method,
        by_auth_type=by_auth_type,
        unique_endpoints=len(endpoints),
    )
