"""Combined scoring engine that merges pattern-based and AI-based results.

Produces a unified score that leverages both approaches:
- Pattern scan: fast, deterministic, catches known signatures
- AI analysis: semantic, contextual, catches architectural patterns

The combined score uses the higher of the two for each criterion,
since the AI may catch things patterns miss and vice versa.
"""

from ..scanner.analyzer import ScanResult, LayerResult, CriterionResult


def combine_results(pattern_result: ScanResult, ai_result: ScanResult) -> ScanResult:
    """Merge pattern-based and AI-based scan results.

    For each criterion, takes the higher score between the two methods.
    Evidence from both sources is combined.

    Args:
        pattern_result: Result from pattern-based scanner.
        ai_result: Result from AI-powered checker.

    Returns:
        Combined ScanResult with merged scores.
    """
    combined_layers = _merge_component_results(
        pattern_result.layer_results,
        ai_result.layer_results,
    )

    combined_contracts = _merge_component_results(
        pattern_result.contract_results,
        ai_result.contract_results,
    )

    # Merge frameworks detected
    all_frameworks = dict(pattern_result.frameworks_detected)
    for fw, conf in ai_result.frameworks_detected.items():
        if fw in all_frameworks:
            all_frameworks[fw] = max(all_frameworks[fw], conf)
        else:
            all_frameworks[fw] = conf

    return ScanResult(
        project_path=pattern_result.project_path,
        frameworks_detected=all_frameworks,
        layer_results=combined_layers,
        contract_results=combined_contracts,
        files_scanned=pattern_result.files_scanned,
        total_lines=pattern_result.total_lines,
    )


def _merge_component_results(
    pattern_results: list[LayerResult],
    ai_results: list[LayerResult],
) -> list[LayerResult]:
    """Merge layer/contract results from both sources."""
    # Build lookup by component ID
    ai_lookup: dict[str, LayerResult] = {lr.id: lr for lr in ai_results}

    merged = []
    for plr in pattern_results:
        air = ai_lookup.get(plr.id)
        if air is None:
            merged.append(plr)
            continue

        # Build AI criterion lookup
        ai_criteria: dict[str, CriterionResult] = {
            cr.criterion_id: cr for cr in air.criteria_results
        }

        merged_criteria = []
        for pcr in plr.criteria_results:
            acr = ai_criteria.get(pcr.criterion_id)
            if acr is None:
                merged_criteria.append(pcr)
                continue

            # Take the better result
            if acr.covered and not pcr.covered:
                # AI found it, pattern didn't
                evidence = list(acr.evidence) + ["[AI detected]"]
                merged_criteria.append(CriterionResult(
                    criterion_id=pcr.criterion_id,
                    criterion_name=pcr.criterion_name,
                    covered=True,
                    confidence=acr.confidence,
                    evidence=evidence,
                ))
            elif pcr.covered and not acr.covered:
                # Pattern found it, AI didn't
                evidence = list(pcr.evidence) + ["[Pattern detected]"]
                merged_criteria.append(CriterionResult(
                    criterion_id=pcr.criterion_id,
                    criterion_name=pcr.criterion_name,
                    covered=True,
                    confidence=pcr.confidence,
                    evidence=evidence,
                ))
            elif pcr.covered and acr.covered:
                # Both found it — merge evidence, take higher confidence
                evidence = list(set(pcr.evidence + acr.evidence))
                merged_criteria.append(CriterionResult(
                    criterion_id=pcr.criterion_id,
                    criterion_name=pcr.criterion_name,
                    covered=True,
                    confidence=max(pcr.confidence, acr.confidence),
                    evidence=evidence[:5],
                ))
            else:
                # Neither found it
                merged_criteria.append(pcr)

        merged.append(LayerResult(
            id=plr.id,
            name=plr.name,
            criteria_results=merged_criteria,
        ))

    return merged
