"""Core comparison logic for analyzing differences between data versions."""

import json
from pathlib import Path
from typing import Any, Dict, List, Set, Tuple

from .load_profiles import get_version_summary


def compare_id_sets(old_ids: Set[str], new_ids: Set[str]) -> Tuple[Set[str], Set[str], Set[str]]:
    """Compare two ID sets and return added, removed, and common IDs.

    Args:
        old_ids: Set of IDs from the older version
        new_ids: Set of IDs from the newer version

    Returns:
        Tuple of (added_ids, removed_ids, common_ids)
    """
    added = new_ids - old_ids
    removed = old_ids - new_ids
    common = old_ids & new_ids

    return added, removed, common


def compare_tag_distributions(
    old_analysis: Dict[str, Any], new_analysis: Dict[str, Any], top_n: int = 50
) -> Dict[str, Any]:
    """Compare tag distributions between two versions.

    Args:
        old_analysis: Analysis results from older version
        new_analysis: Analysis results from newer version
        top_n: Number of top tags to analyze

    Returns:
        Dictionary with tag comparison results
    """
    old_tags = old_analysis.get("tags", {})
    new_tags = new_analysis.get("tags", {})

    old_top = dict(old_tags.get("top_tags", [])[:top_n])
    new_top = dict(new_tags.get("top_tags", [])[:top_n])

    # Find tags that appeared or disappeared from top N
    old_tag_set = set(old_top.keys())
    new_tag_set = set(new_top.keys())

    new_tags_in_top = new_tag_set - old_tag_set
    removed_tags_from_top = old_tag_set - new_tag_set

    # Calculate count changes for common tags
    common_tags = old_tag_set & new_tag_set
    tag_count_changes = {}
    for tag in common_tags:
        old_count = old_top[tag]
        new_count = new_top[tag]
        change = new_count - old_count
        pct_change = (change / old_count * 100) if old_count > 0 else 0
        tag_count_changes[tag] = {
            "old_count": old_count,
            "new_count": new_count,
            "change": change,
            "pct_change": round(pct_change, 2),
        }

    # Sort by absolute change
    top_movers = sorted(tag_count_changes.items(), key=lambda x: abs(x[1]["change"]), reverse=True)[:20]

    return {
        "old_unique_tags": old_tags.get("summary", {}).get("unique_tags", 0),
        "new_unique_tags": new_tags.get("summary", {}).get("unique_tags", 0),
        "new_tags_in_top_n": list(new_tags_in_top),
        "removed_tags_from_top_n": list(removed_tags_from_top),
        "top_movers": dict(top_movers),
        "new_tag_examples": {tag: new_top[tag] for tag in list(new_tags_in_top)[:10]},
    }


def compare_language_distributions(
    old_analysis: Dict[str, Any], new_analysis: Dict[str, Any]
) -> Dict[str, Any]:
    """Compare language distributions between two versions.

    Args:
        old_analysis: Analysis results from older version
        new_analysis: Analysis results from newer version

    Returns:
        Dictionary with language comparison results
    """
    old_lang = old_analysis.get("language", {})
    new_lang = new_analysis.get("language", {})

    old_dist = dict(old_lang.get("language_distribution", []))
    new_dist = dict(new_lang.get("language_distribution", []))

    # Find new and removed languages
    old_lang_set = set(old_dist.keys())
    new_lang_set = set(new_dist.keys())

    new_languages = new_lang_set - old_lang_set
    removed_languages = old_lang_set - new_lang_set

    # Calculate changes for common languages
    common_langs = old_lang_set & new_lang_set
    lang_changes = {}
    for lang in common_langs:
        old_count = old_dist[lang]
        new_count = new_dist[lang]
        change = new_count - old_count
        pct_change = (change / old_count * 100) if old_count > 0 else 0
        lang_changes[lang] = {
            "old_count": old_count,
            "new_count": new_count,
            "change": change,
            "pct_change": round(pct_change, 2),
        }

    # Top movers
    top_movers = sorted(lang_changes.items(), key=lambda x: abs(x[1]["change"]), reverse=True)[:10]

    return {
        "old_unique_languages": old_lang.get("summary", {}).get("unique_languages", 0),
        "new_unique_languages": new_lang.get("summary", {}).get("unique_languages", 0),
        "old_mismatch_rate": old_lang.get("summary", {}).get("mismatch_rate", 0.0),
        "new_mismatch_rate": new_lang.get("summary", {}).get("mismatch_rate", 0.0),
        "new_languages": list(new_languages),
        "removed_languages": list(removed_languages),
        "top_movers": dict(top_movers),
    }


def compare_source_distributions(
    old_analysis: Dict[str, Any], new_analysis: Dict[str, Any]
) -> Dict[str, Any]:
    """Compare data source distributions between two versions.

    Args:
        old_analysis: Analysis results from older version
        new_analysis: Analysis results from newer version

    Returns:
        Dictionary with source comparison results
    """
    old_paths = old_analysis.get("paths", {})
    new_paths = new_analysis.get("paths", {})

    old_local = dict(old_paths.get("local_directories", []))
    new_local = dict(new_paths.get("local_directories", []))

    # Find new and removed source directories
    old_dirs = set(old_local.keys())
    new_dirs = set(new_local.keys())

    new_directories = new_dirs - old_dirs
    removed_directories = old_dirs - new_dirs

    # Calculate changes for common directories
    common_dirs = old_dirs & new_dirs
    dir_changes = {}
    for dir_path in common_dirs:
        old_count = old_local[dir_path]
        new_count = new_local[dir_path]
        change = new_count - old_count
        pct_change = (change / old_count * 100) if old_count > 0 else 0
        dir_changes[dir_path] = {
            "old_count": old_count,
            "new_count": new_count,
            "change": change,
            "pct_change": round(pct_change, 2),
        }

    # Top movers
    top_movers = sorted(dir_changes.items(), key=lambda x: abs(x[1]["change"]), reverse=True)[:10]

    return {
        "old_unique_dirs": old_paths.get("summary", {}).get("unique_local_dirs", 0),
        "new_unique_dirs": new_paths.get("summary", {}).get("unique_local_dirs", 0),
        "old_anomalies": old_paths.get("summary", {}).get("total_anomalies", 0),
        "new_anomalies": new_paths.get("summary", {}).get("total_anomalies", 0),
        "new_directories": list(new_directories),
        "removed_directories": list(removed_directories),
        "top_movers": dict(top_movers),
        "new_directory_examples": {d: new_local[d] for d in list(new_directories)[:5]},
    }


def compare_stem_distributions(
    old_analysis: Dict[str, Any], new_analysis: Dict[str, Any]
) -> Dict[str, Any]:
    """Compare stem distributions between two versions.

    Args:
        old_analysis: Analysis results from older version
        new_analysis: Analysis results from newer version

    Returns:
        Dictionary with stem comparison results
    """
    old_stems = old_analysis.get("stems", {})
    new_stems = new_analysis.get("stems", {})

    old_freq = dict(old_stems.get("stem_names_frequency", []))
    new_freq = dict(new_stems.get("stem_names_frequency", []))

    # Find new and removed stem types
    old_stem_set = set(old_freq.keys())
    new_stem_set = set(new_freq.keys())

    new_stem_types = new_stem_set - old_stem_set
    removed_stem_types = old_stem_set - new_stem_set

    # Calculate changes for common stems
    common_stems = old_stem_set & new_stem_set
    stem_changes = {}
    for stem in common_stems:
        old_count = old_freq[stem]
        new_count = new_freq[stem]
        change = new_count - old_count
        pct_change = (change / old_count * 100) if old_count > 0 else 0
        stem_changes[stem] = {
            "old_count": old_count,
            "new_count": new_count,
            "change": change,
            "pct_change": round(pct_change, 2),
        }

    # Top movers
    top_movers = sorted(stem_changes.items(), key=lambda x: abs(x[1]["change"]), reverse=True)[:10]

    # Check for stems_captions changes (important for v4→v5, v8→v9)
    old_captions = old_stems.get("summary", {}).get("records_with_stems_captions", 0)
    new_captions = new_stems.get("summary", {}).get("records_with_stems_captions", 0)

    return {
        "old_records_with_stems": old_stems.get("summary", {}).get("records_with_stems", 0),
        "new_records_with_stems": new_stems.get("summary", {}).get("records_with_stems", 0),
        "old_stems_captions": old_captions,
        "new_stems_captions": new_captions,
        "stems_captions_change": new_captions - old_captions,
        "old_unique_stem_names": old_stems.get("summary", {}).get("unique_stem_names", 0),
        "new_unique_stem_names": new_stems.get("summary", {}).get("unique_stem_names", 0),
        "new_stem_types": list(new_stem_types),
        "removed_stem_types": list(removed_stem_types),
        "top_movers": dict(top_movers),
    }


def compare_quality_metrics(
    old_analysis: Dict[str, Any], new_analysis: Dict[str, Any]
) -> Dict[str, Any]:
    """Compare quality metrics between two versions.

    Args:
        old_analysis: Analysis results from older version
        new_analysis: Analysis results from newer version

    Returns:
        Dictionary with quality metric comparison
    """
    old_id = old_analysis.get("id", {}).get("summary", {})
    new_id = new_analysis.get("id", {}).get("summary", {})

    old_lang = old_analysis.get("language", {}).get("summary", {})
    new_lang = new_analysis.get("language", {}).get("summary", {})

    old_paths = old_analysis.get("paths", {}).get("summary", {})
    new_paths = new_analysis.get("paths", {}).get("summary", {})

    old_text = old_analysis.get("text", {}).get("summary", {})
    new_text = new_analysis.get("text", {}).get("summary", {})

    return {
        "duplicates": {
            "old": old_id.get("duplicate_ids", 0),
            "new": new_id.get("duplicate_ids", 0),
            "change": new_id.get("duplicate_ids", 0) - old_id.get("duplicate_ids", 0),
        },
        "language_mismatch_rate": {
            "old": old_lang.get("mismatch_rate", 0.0),
            "new": new_lang.get("mismatch_rate", 0.0),
            "change": new_lang.get("mismatch_rate", 0.0) - old_lang.get("mismatch_rate", 0.0),
        },
        "path_anomalies": {
            "old": old_paths.get("total_anomalies", 0),
            "new": new_paths.get("total_anomalies", 0),
            "change": new_paths.get("total_anomalies", 0) - old_paths.get("total_anomalies", 0),
        },
        "empty_texts": {
            "old": old_text.get("empty_texts", 0),
            "new": new_text.get("empty_texts", 0),
            "change": new_text.get("empty_texts", 0) - old_text.get("empty_texts", 0),
        },
        "missing_tags": {
            "old": old_analysis.get("tags", {}).get("summary", {}).get("records_without_tags", 0),
            "new": new_analysis.get("tags", {}).get("summary", {}).get("records_without_tags", 0),
            "change": new_analysis.get("tags", {}).get("summary", {}).get("records_without_tags", 0)
            - old_analysis.get("tags", {}).get("summary", {}).get("records_without_tags", 0),
        },
    }


def compare_versions(
    old_analysis: Dict[str, Any],
    new_analysis: Dict[str, Any],
    old_ids: Set[str],
    new_ids: Set[str],
    old_version: str,
    new_version: str,
) -> Dict[str, Any]:
    """Perform comprehensive comparison between two versions.

    Args:
        old_analysis: Analysis results from older version
        new_analysis: Analysis results from newer version
        old_ids: Set of IDs from older version
        new_ids: Set of IDs from newer version
        old_version: Version string (e.g., "v0")
        new_version: Version string (e.g., "v1")

    Returns:
        Dictionary with comprehensive comparison results
    """
    # Get summaries
    old_summary = get_version_summary(old_analysis)
    new_summary = get_version_summary(new_analysis)

    # Compare ID sets
    added_ids, removed_ids, common_ids = compare_id_sets(old_ids, new_ids)

    # Calculate summary statistics
    summary = {
        "old_version": old_version,
        "new_version": new_version,
        "old_total_records": old_summary["total_records"],
        "new_total_records": new_summary["total_records"],
        "record_change": new_summary["total_records"] - old_summary["total_records"],
        "added_ids_count": len(added_ids),
        "removed_ids_count": len(removed_ids),
        "common_ids_count": len(common_ids),
        "net_change": len(added_ids) - len(removed_ids),
    }

    # Perform detailed comparisons
    return {
        "summary": summary,
        "old_summary": old_summary,
        "new_summary": new_summary,
        "id_changes": {
            "added_count": len(added_ids),
            "removed_count": len(removed_ids),
            "common_count": len(common_ids),
            "added_ids": list(added_ids),
            "removed_ids": list(removed_ids),
        },
        "tag_changes": compare_tag_distributions(old_analysis, new_analysis),
        "language_changes": compare_language_distributions(old_analysis, new_analysis),
        "source_changes": compare_source_distributions(old_analysis, new_analysis),
        "stem_changes": compare_stem_distributions(old_analysis, new_analysis),
        "quality_metrics": compare_quality_metrics(old_analysis, new_analysis),
    }


def save_comparison_results(results: Dict[str, Any], output_dir: Path) -> None:
    """Save comparison results to JSON files.

    Args:
        results: Dictionary with comparison results
        output_dir: Directory to save results
    """
    output_dir.mkdir(parents=True, exist_ok=True)

    # Save summary
    with open(output_dir / "summary.json", "w") as f:
        json.dump(
            {
                "summary": results["summary"],
                "old_summary": results["old_summary"],
                "new_summary": results["new_summary"],
            },
            f,
            indent=2,
        )

    # Save ID changes (without full ID lists, which can be huge)
    id_changes_summary = {
        "added_count": results["id_changes"]["added_count"],
        "removed_count": results["id_changes"]["removed_count"],
        "common_count": results["id_changes"]["common_count"],
    }

    with open(output_dir / "id_changes.json", "w") as f:
        json.dump(id_changes_summary, f, indent=2)

    # Save full ID lists to separate text files
    with open(output_dir / "added_ids.txt", "w") as f:
        for id_str in sorted(results["id_changes"]["added_ids"]):
            f.write(f"{id_str}\n")

    with open(output_dir / "removed_ids.txt", "w") as f:
        for id_str in sorted(results["id_changes"]["removed_ids"]):
            f.write(f"{id_str}\n")

    # Save other comparisons
    with open(output_dir / "tag_changes.json", "w") as f:
        json.dump(results["tag_changes"], f, indent=2)

    with open(output_dir / "language_changes.json", "w") as f:
        json.dump(results["language_changes"], f, indent=2)

    with open(output_dir / "source_changes.json", "w") as f:
        json.dump(results["source_changes"], f, indent=2)

    with open(output_dir / "stem_changes.json", "w") as f:
        json.dump(results["stem_changes"], f, indent=2)

    with open(output_dir / "quality_metrics.json", "w") as f:
        json.dump(results["quality_metrics"], f, indent=2)

    print(f"Comparison results saved to {output_dir}")
