"""
Functions for parsing pytest test results and output.
"""

import json
from dataclasses import fields
from typing import Any, Dict

from mcp_tools_py.code_checker_pytest.models import (
    Collector,
    CollectorResult,
    Crash,
    Log,
    LogRecord,
    PytestReport,
    StageInfo,
    Summary,
    Test,
    TracebackEntry,
    Warning,
)

# Derive known fields from LogRecord dataclass to auto-sync if fields are added.
# The "extra" field is excluded as it's our container for unknown fields.
LOG_RECORD_FIELDS = {f.name for f in fields(LogRecord)} - {"extra"}


def parse_test_stage(stage_data: Dict[str, Any]) -> StageInfo:
    """
    Parse test stage data from the pytest JSON report.

    Args:
        stage_data: Dictionary containing test stage data from JSON

    Returns:
        StageInfo object populated with data from JSON
    """
    crash = None
    if "crash" in stage_data and stage_data["crash"]:
        crash = Crash(**stage_data["crash"])

    traceback = None
    if "traceback" in stage_data and stage_data["traceback"]:
        traceback = [TracebackEntry(**entry) for entry in stage_data["traceback"]]

    log = None
    if "log" in stage_data and stage_data["log"]:
        log_records = []
        for log_record_data in stage_data["log"]:
            known_fields = {
                k: v for k, v in log_record_data.items() if k in LOG_RECORD_FIELDS
            }
            extra_fields = {
                k: v for k, v in log_record_data.items() if k not in LOG_RECORD_FIELDS
            }
            log_records.append(LogRecord(**known_fields, extra=extra_fields))
        log = Log(logs=log_records)

    return StageInfo(
        duration=stage_data["duration"],
        outcome=stage_data["outcome"],
        crash=crash,
        traceback=traceback,
        stdout=stage_data.get("stdout"),
        stderr=stage_data.get("stderr"),
        log=log,
        longrepr=stage_data.get("longrepr"),
    )


def parse_pytest_report(json_data: str) -> PytestReport:
    """
    Parse a JSON string into a PytestReport object.

    Args:
        json_data: JSON string from pytest json report

    Returns:
        PytestReport object with test results
    """
    data = json.loads(json_data)

    summary = Summary(**data["summary"])

    environment = data["environment"]

    collectors = None
    if "collectors" in data and data["collectors"]:
        collectors = []
        for collector_data in data["collectors"]:
            result_data_list = []
            for result_data in collector_data["result"]:
                result_data_list.append(CollectorResult(**result_data))
            collector = Collector(
                nodeid=collector_data["nodeid"],
                outcome=collector_data["outcome"],
                result=result_data_list,
                longrepr=collector_data.get("longrepr"),
            )
            collectors.append(collector)

    tests = None
    if "tests" in data and data["tests"]:
        tests = []
        for test_data in data["tests"]:
            setup_stage = None
            call_stage = None
            teardown_stage = None

            if "setup" in test_data:
                setup_stage = parse_test_stage(test_data["setup"])
            if "call" in test_data:
                call_stage = parse_test_stage(test_data["call"])
            if "teardown" in test_data:
                teardown_stage = parse_test_stage(test_data["teardown"])

            test = Test(
                nodeid=test_data["nodeid"],
                lineno=test_data["lineno"],
                keywords=test_data["keywords"],
                outcome=test_data["outcome"],
                setup=setup_stage,
                call=call_stage,
                teardown=teardown_stage,
                metadata=test_data.get("metadata"),
            )
            tests.append(test)

    warnings = None
    if "warnings" in data and data["warnings"]:
        warnings = [Warning(**warning_data) for warning_data in data["warnings"]]

    return PytestReport(
        created=data["created"],
        duration=data["duration"],
        exitcode=data["exitcode"],
        root=data["root"],
        environment=environment,
        summary=summary,
        collectors=collectors,
        tests=tests,
        warnings=warnings,
    )
