"""Merge XML pass/fail results with data results."""

from __future__ import annotations

from collections import defaultdict

from .._model import VerificationType
from .models import FunctionTestInvocation, TestResults


def merge_test_results(
    xml_test_results: TestResults,
    bin_test_results: TestResults,
) -> TestResults:
    """Merge XML pass/fail results with data results."""
    merged = TestResults()

    grouped_xml: defaultdict[tuple[str, str, str, int], TestResults] = (
        defaultdict(TestResults)
    )
    grouped_data: defaultdict[tuple[str, str, str, int], TestResults] = (
        defaultdict(TestResults)
    )

    for result in xml_test_results:
        test_function_name = result.id.test_function_name or ""
        if test_function_name.endswith("]") and "[" in test_function_name:
            test_function_name = test_function_name[
                : test_function_name.index("[")
            ]
        grouped_xml[
            (
                result.id.test_module_path or "",
                result.id.test_class_name or "",
                test_function_name,
                result.loop_index,
            )
        ].add(result)

    for result in bin_test_results:
        grouped_data[
            (
                result.id.test_module_path or "",
                result.id.test_class_name or "",
                result.id.test_function_name or "",
                result.loop_index,
            )
        ].add(result)

    for result_id, xml_results in grouped_xml.items():
        data_results = grouped_data.get(result_id)
        if not data_results:
            merged.merge(xml_results)
            continue

        if len(xml_results) == 1:
            _merge_single_xml(
                xml_results[0],
                data_results,
                merged,
            )
        elif xml_results.test_results[0].id.iteration_id:
            _merge_by_iteration_id(xml_results, data_results, merged)
        else:
            _merge_by_index(xml_results, data_results, merged)

    return merged


def _merge_single_xml(
    xml_result: FunctionTestInvocation,
    data_results: TestResults,
    merged: TestResults,
) -> None:
    """Merge a single XML result with data results."""
    for data_result in data_results:
        merged_runtime = data_result.runtime or xml_result.runtime
        merged.add(
            FunctionTestInvocation(
                loop_index=xml_result.loop_index,
                id=data_result.id,
                file_name=xml_result.file_name,
                runtime=merged_runtime,
                test_framework=xml_result.test_framework,
                did_pass=xml_result.did_pass,
                test_type=xml_result.test_type,
                return_value=data_result.return_value,
                timed_out=xml_result.timed_out,
                verification_type=(
                    VerificationType(data_result.verification_type)
                    if data_result.verification_type
                    else None
                ),
                stdout=xml_result.stdout,
            ),
        )


def _merge_by_iteration_id(
    xml_results: TestResults,
    data_results: TestResults,
    merged: TestResults,
) -> None:
    """Merge XML and data results by iteration id."""
    for xml_result in xml_results.test_results:
        data_result = data_results.get_by_unique_invocation_loop_id(
            xml_result.unique_invocation_loop_id,
        )
        if data_result is None:
            merged.add(xml_result)
            continue
        merged_runtime = data_result.runtime or xml_result.runtime
        merged.add(
            FunctionTestInvocation(
                loop_index=xml_result.loop_index,
                id=xml_result.id,
                file_name=xml_result.file_name,
                runtime=merged_runtime,
                test_framework=xml_result.test_framework,
                did_pass=data_result.did_pass,
                test_type=xml_result.test_type,
                return_value=data_result.return_value,
                timed_out=(
                    xml_result.timed_out if merged_runtime is None else False
                ),
                verification_type=(
                    VerificationType(data_result.verification_type)
                    if data_result.verification_type
                    else None
                ),
                stdout=xml_result.stdout,
            ),
        )


def _merge_by_index(
    xml_results: TestResults,
    data_results: TestResults,
    merged: TestResults,
) -> None:
    """Merge XML and data results by positional index."""
    for i, data_result in enumerate(
        data_results.test_results,
    ):
        xml_result = (
            xml_results.test_results[i]
            if i < len(xml_results.test_results)
            else None
        )
        if xml_result is None:
            merged.add(data_result)
            continue
        merged_runtime = data_result.runtime or xml_result.runtime
        merged.add(
            FunctionTestInvocation(
                loop_index=data_result.loop_index,
                id=data_result.id,
                file_name=data_result.file_name,
                runtime=merged_runtime,
                test_framework=data_result.test_framework,
                did_pass=data_result.did_pass,
                test_type=data_result.test_type,
                return_value=data_result.return_value,
                timed_out=xml_result.timed_out,
                verification_type=(
                    VerificationType(data_result.verification_type)
                    if data_result.verification_type
                    else None
                ),
                stdout=xml_result.stdout,
            ),
        )
