from typing import Optional
"""
Cross-dataset coverage analysis tools.

DEPRECATED: This module provides legacy coverage analysis between separate
reference and usage-reference datasets. With the unified reference data
architecture, all data is now stored in a single reference.parquet file,
making cross-dataset analysis unnecessary.

Use 'czbg data explore' commands instead for modern reference data exploration.
"""

from pathlib import Path

import polars as pl
import typer

from ..common import BaseCommand, display_summary_table

app = typer.Typer(help="Cross-dataset coverage analysis")


class CoverageAnalysisCommand(BaseCommand):
    """Coverage analysis between reference and usage-reference datasets."""

    def analyze_usage_reference_orphans(
        self,
        cloud_provider: Optional[str] = None,
        service: Optional[str] = None,
        usage_family: Optional[str] = None,
        resource_type: Optional[str] = None,
        operation: Optional[str] = None,
        usage_type: Optional[str] = None,
        pricing_unit: Optional[str] = None,
        limit: int = 100,
        export_csv: Optional[str] = None,
        reference_file: str = "data/providers.csv",
        usage_reference_file: str = "data/usage_reference/usage_standards.parquet",
    ):
        """
        Show usage-reference records that have no matching reference data.
        These are pricing records you have but can't use for generation.
        """
        try:
            # Load both datasets
            ref_path = Path(reference_file)
            usage_ref_path = Path(usage_reference_file)

            if not usage_ref_path.exists():
                self.console.print(
                    f"[red]Usage-reference file not found: {usage_ref_path}[/red]"
                )
                return

            usage_df = pl.read_parquet(usage_ref_path)

            if ref_path.exists():
                ref_df = pl.read_csv(ref_path)
            else:
                self.console.print(
                    f"[yellow]Reference file not found: {ref_path}[/yellow]"
                )
                ref_df = pl.DataFrame()

            # Find orphans (usage-reference records without reference matches)
            orphans = self._find_usage_reference_orphans(usage_df, ref_df)

            if orphans.is_empty():
                self.console.print(
                    "[green]No orphaned usage-reference records found![/green]"
                )
                return

            # Handle global summary
            if not any(
                [
                    cloud_provider,
                    service,
                    usage_family,
                    resource_type,
                    operation,
                    usage_type,
                    pricing_unit,
                ]
            ):
                self._show_orphan_summary(orphans)
                return

            # Apply filters for drilldown
            filtered_orphans = self._apply_filters(
                orphans,
                {
                    "cloud_provider": cloud_provider,
                    "service": service,
                    "usage_family": usage_family,
                    "resource_type": resource_type,
                    "operation": operation,
                    "usage_type": usage_type,
                    "pricing_unit": pricing_unit,
                },
            )

            if filtered_orphans.is_empty():
                self.console.print(
                    "[yellow]No orphaned records match the filters.[/yellow]"
                )
                return

            # Determine grouping for progressive drilldown
            group_cols = self._determine_orphan_group_columns(
                {
                    "cloud_provider": cloud_provider,
                    "service": service,
                    "usage_family": usage_family,
                    "resource_type": resource_type,
                    "operation": operation,
                    "usage_type": usage_type,
                    "pricing_unit": pricing_unit,
                }
            )

            # Group and display
            grouped_result = self._group_orphans(filtered_orphans, group_cols)
            result = grouped_result.head(limit)

            self._display_orphan_results(result, group_cols)

            # Show truncation warning
            if grouped_result.height > limit:
                self.console.print(
                    f"\n[yellow]⚠️ Showing first {limit} rows out of {grouped_result.height} total matches. Use --limit to see more.[/yellow]"
                )

            # Export if requested
            if export_csv:
                result.write_csv(export_csv)
                self.console.print(f"[green]Results exported to {export_csv}[/green]")

        except Exception as e:
            self.handle_exception(e, "usage-reference orphan analysis")

    def analyze_match_quality(
        self,
        cloud_provider: Optional[str] = None,
        service: Optional[str] = None,
        usage_family: Optional[str] = None,
        reference_file: str = "data/providers.csv",
        usage_reference_file: str = "data/usage_reference/usage_standards.parquet",
    ):
        """
        Analyze match quality between reference and usage-reference datasets.
        Shows coverage statistics for different combinations.
        """
        try:
            # Load both datasets
            ref_path = Path(reference_file)
            usage_ref_path = Path(usage_reference_file)

            if not ref_path.exists():
                self.console.print(f"[red]Reference file not found: {ref_path}[/red]")
                return

            ref_df = pl.read_csv(ref_path)

            usage_df = pl.DataFrame()
            if usage_ref_path.exists():
                usage_df = pl.read_parquet(usage_ref_path)

            # Apply filters to reference data
            filtered_ref = self._apply_filters(
                ref_df,
                {
                    "cloud_provider": cloud_provider,
                    "service": service,
                    "usage_family": usage_family,
                },
            )

            # Calculate match quality
            quality_report = self._calculate_match_quality(filtered_ref, usage_df)

            # Display results
            self._display_match_quality_report(
                quality_report,
                {
                    "cloud_provider": cloud_provider,
                    "service": service,
                    "usage_family": usage_family,
                },
            )

        except Exception as e:
            self.handle_exception(e, "match quality analysis")

    def recommend_combinations(
        self,
        cloud_provider: Optional[str] = None,
        service: Optional[str] = None,
        with_usage_reference: bool = False,
        limit: int = 20,
        reference_file: str = "data/providers.csv",
        usage_reference_file: str = "data/usage_reference/usage_standards.parquet",
    ):
        """
        Recommend high-quality combinations for generation configs.
        Prioritizes combinations that have both metadata richness AND pricing data.
        """
        try:
            # Load reference data
            ref_path = Path(reference_file)
            if not ref_path.exists():
                self.console.print(f"[red]Reference file not found: {ref_path}[/red]")
                return

            ref_df = pl.read_csv(ref_path)

            # Load usage-reference data if needed
            usage_coverage = set()
            if with_usage_reference:
                usage_ref_path = Path(usage_reference_file)
                if usage_ref_path.exists():
                    usage_df = pl.read_parquet(usage_ref_path)
                    usage_coverage = self._build_usage_coverage_set(usage_df)

            # Apply filters
            filtered_ref = self._apply_filters(
                ref_df, {"cloud_provider": cloud_provider, "service": service}
            )

            # Score and recommend combinations
            recommendations = self._score_combinations(
                filtered_ref, usage_coverage, with_usage_reference
            )

            # Display top recommendations
            top_recommendations = recommendations.head(limit)
            self._display_recommendations(top_recommendations, with_usage_reference)

        except Exception as e:
            self.handle_exception(e, "combination recommendations")

    def _find_usage_reference_orphans(
        self, usage_df: pl.DataFrame, ref_df: pl.DataFrame
    ) -> pl.DataFrame:
        """Find usage-reference records with no reference matches."""
        if ref_df.is_empty():
            return usage_df

        # Key columns for matching
        key_cols = [
            "cloud_provider",
            "service",
            "usage_family",
            "resource_type",
            "operation",
            "usage_type",
        ]

        # Get available columns in both datasets
        usage_cols = [col for col in key_cols if col in usage_df.columns]
        ref_cols = [col for col in key_cols if col in ref_df.columns]
        common_cols = [col for col in usage_cols if col in ref_cols]

        if not common_cols:
            return usage_df

        # Left anti-join to find orphans
        orphans = usage_df.join(
            ref_df.select(common_cols).unique(), on=common_cols, how="anti"
        )

        return orphans

    def _apply_filters(self, df: pl.DataFrame, filters: dict) -> pl.DataFrame:
        """Apply filters to dataframe."""
        for column, value in filters.items():
            if value and column in df.columns:
                df = df.filter(pl.col(column).str.contains(value))
        return df

    def _determine_orphan_group_columns(self, filters: dict) -> list:
        """Determine grouping columns for orphan analysis."""
        if filters["pricing_unit"]:
            return [
                "cloud_provider",
                "service",
                "usage_family",
                "resource_type",
                "operation",
                "usage_type",
                "pricing_unit",
            ]
        elif filters["usage_type"]:
            return [
                "cloud_provider",
                "service",
                "usage_family",
                "resource_type",
                "operation",
                "usage_type",
            ]
        elif filters["operation"]:
            return [
                "cloud_provider",
                "service",
                "usage_family",
                "resource_type",
                "operation",
            ]
        elif filters["resource_type"]:
            return ["cloud_provider", "service", "usage_family", "resource_type"]
        elif filters["usage_family"]:
            return ["cloud_provider", "service", "usage_family"]
        elif filters["service"]:
            return ["cloud_provider", "service"]
        else:
            return ["cloud_provider"]

    def _group_orphans(self, orphans: pl.DataFrame, group_cols: list) -> pl.DataFrame:
        """Group orphaned records."""
        available_cols = [col for col in group_cols if col in orphans.columns]

        if not available_cols:
            return pl.DataFrame()

        return (
            orphans.group_by(available_cols)
            .agg(pl.len().alias("orphaned_records"))
            .sort(available_cols)
        )

    def _show_orphan_summary(self, orphans: pl.DataFrame):
        """Show summary of orphaned usage-reference records."""
        total_orphans = orphans.height

        # Provider breakdown
        if "cloud_provider" in orphans.columns:
            provider_breakdown = (
                orphans.group_by("cloud_provider")
                .agg(pl.len().alias("orphans"))
                .sort("orphans", descending=True)
            )
            provider_dict = provider_breakdown.to_dicts()
        else:
            provider_dict = []

        self.console.print("\n[yellow]Usage-Reference Orphan Summary:[/yellow]")
        self.console.print(f"Total orphaned records: {total_orphans}")
        self.console.print(
            "These are pricing records with no matching reference metadata"
        )

        if provider_dict:
            self.console.print("\nOrphans by Provider:")
            display_summary_table(
                self.console,
                "Orphaned Records by Provider",
                provider_dict,
                ["cloud_provider", "orphans"],
            )

    def _display_orphan_results(self, result: pl.DataFrame, group_cols: list):
        """Display orphan analysis results."""
        if result.is_empty():
            self.console.print("[yellow]No orphaned records found.[/yellow]")
            return

        result_dict = result.to_dicts()
        display_cols = [col for col in group_cols if col in result.columns] + [
            "orphaned_records"
        ]

        display_summary_table(
            self.console, "Usage-Reference Orphans", result_dict, display_cols
        )

        total_orphans = sum(row["orphaned_records"] for row in result_dict)
        self.console.print(
            f"\n[blue]Total orphaned records in view: {total_orphans}[/blue]"
        )

    def _calculate_match_quality(
        self, ref_df: pl.DataFrame, usage_df: pl.DataFrame
    ) -> dict:
        """Calculate match quality statistics."""
        total_ref_records = ref_df.height

        if usage_df.is_empty():
            return {
                "total_reference_records": total_ref_records,
                "exact_matches": 0,
                "partial_matches": 0,
                "no_matches": total_ref_records,
                "coverage_percentage": 0.0,
            }

        # Build coverage set
        usage_coverage = self._build_usage_coverage_set(usage_df)

        # Count matches
        exact_matches = 0
        key_cols = [
            "cloud_provider",
            "service",
            "usage_family",
            "resource_type",
            "operation",
            "usage_type",
        ]
        available_cols = [col for col in key_cols if col in ref_df.columns]

        for row in ref_df.select(available_cols).iter_rows(named=True):
            key = tuple(row[col] for col in available_cols)
            if key in usage_coverage:
                exact_matches += 1

        no_matches = total_ref_records - exact_matches
        coverage_percentage = (
            (exact_matches / total_ref_records * 100) if total_ref_records > 0 else 0
        )

        return {
            "total_reference_records": total_ref_records,
            "exact_matches": exact_matches,
            "no_matches": no_matches,
            "coverage_percentage": round(coverage_percentage, 1),
        }

    def _build_usage_coverage_set(self, usage_df: pl.DataFrame) -> set:
        """Build set of usage-reference combinations."""
        coverage_set = set()
        key_cols = [
            "cloud_provider",
            "service",
            "usage_family",
            "resource_type",
            "operation",
            "usage_type",
        ]
        available_cols = [col for col in key_cols if col in usage_df.columns]

        for row in usage_df.select(available_cols).iter_rows(named=True):
            key = tuple(row[col] for col in available_cols)
            coverage_set.add(key)

        return coverage_set

    def _display_match_quality_report(self, quality_report: dict, filters: dict):
        """Display match quality analysis."""
        filter_desc = ", ".join([f"{k}={v}" for k, v in filters.items() if v])
        title = "Match Quality Report"
        if filter_desc:
            title += f" ({filter_desc})"

        summary_data = [
            {
                "metric": "Total reference records",
                "value": quality_report["total_reference_records"],
            },
            {
                "metric": "Exact matches with usage-reference",
                "value": quality_report["exact_matches"],
            },
            {
                "metric": "No usage-reference data",
                "value": quality_report["no_matches"],
            },
            {
                "metric": "Coverage percentage",
                "value": f"{quality_report['coverage_percentage']}%",
            },
        ]

        display_summary_table(self.console, title, summary_data, ["metric", "value"])

    def _score_combinations(
        self, ref_df: pl.DataFrame, usage_coverage: set, require_usage: bool
    ) -> pl.DataFrame:
        """Score reference combinations for recommendation."""
        scores = []

        for row in ref_df.iter_rows(named=True):
            score = 0

            # Base score for having data
            score += 1

            # Bonus for specific resource type (not service-usage)
            if row.get("resource_type") != "service-usage":
                score += 2

            # Bonus for having instance type
            if row.get("instance_type") and row.get("instance_type") != "":
                score += 1

            # Bonus for having usage-reference data
            key_cols = [
                "cloud_provider",
                "service",
                "usage_family",
                "resource_type",
                "operation",
                "usage_type",
            ]
            key = tuple(row.get(col) for col in key_cols)
            has_usage_data = key in usage_coverage

            if has_usage_data:
                score += 3
            elif require_usage:
                continue  # Skip if usage data required but not available

            scores.append(
                {**row, "quality_score": score, "has_usage_data": has_usage_data}
            )

        if not scores:
            return pl.DataFrame()

        return pl.DataFrame(scores).sort("quality_score", descending=True)

    def _display_recommendations(
        self, recommendations: pl.DataFrame, show_usage_indicator: bool
    ):
        """Display combination recommendations."""
        if recommendations.is_empty():
            self.console.print(
                "[yellow]No recommendations found with current criteria.[/yellow]"
            )
            return

        # Convert to display format
        result_dict = recommendations.to_dicts()

        # Format for display
        for row in result_dict:
            if show_usage_indicator:
                row["usage_data"] = "✓" if row.get("has_usage_data", False) else "✗"

            # Clean up extra columns
            row.pop("has_usage_data", None)

        # Display columns
        display_cols = [
            "cloud_provider",
            "service",
            "usage_family",
            "resource_type",
            "quality_score",
        ]
        if show_usage_indicator:
            display_cols.append("usage_data")

        display_summary_table(
            self.console, "Recommended Combinations", result_dict, display_cols
        )


# Create command instance
coverage_cmd = CoverageAnalysisCommand()


@app.command()
def usage_reference_orphans(
    cloud_provider: Optional[str] = typer.Option(
        None, "--cloud-provider", help="Filter by cloud provider"
    ),
    service: Optional[str] = typer.Option(None, "--service", help="Filter by service"),
    usage_family: Optional[str] = typer.Option(
        None, "--usage-family", help="Filter by usage family"
    ),
    resource_type: Optional[str] = typer.Option(
        None, "--resource-type", help="Filter by resource type"
    ),
    operation: Optional[str] = typer.Option(
        None, "--operation", help="Filter by operation"
    ),
    usage_type: Optional[str] = typer.Option(
        None, "--usage-type", help="Filter by usage type"
    ),
    pricing_unit: Optional[str] = typer.Option(
        None, "--pricing-unit", help="Filter by pricing unit"
    ),
    limit: int = typer.Option(100, "--limit", help="Limit number of rows"),
    export_csv: Optional[str] = typer.Option(None, "--export", help="Export to CSV"),
    reference_file: str = typer.Option(
        "data/providers.csv", "--reference-file", help="Reference file path"
    ),
    usage_reference_file: str = typer.Option(
        "data/usage_reference/usage_standards.parquet",
        "--usage-reference-file",
        help="Usage-reference file path",
    ),
):
    """Show usage-reference records without matching reference data."""
    coverage_cmd.analyze_usage_reference_orphans(
        cloud_provider,
        service,
        usage_family,
        resource_type,
        operation,
        usage_type,
        pricing_unit,
        limit,
        export_csv,
        reference_file,
        usage_reference_file,
    )


@app.command()
def match_quality(
    cloud_provider: Optional[str] = typer.Option(
        None, "--cloud-provider", help="Filter by cloud provider"
    ),
    service: Optional[str] = typer.Option(None, "--service", help="Filter by service"),
    usage_family: Optional[str] = typer.Option(
        None, "--usage-family", help="Filter by usage family"
    ),
    reference_file: str = typer.Option(
        "data/providers.csv", "--reference-file", help="Reference file path"
    ),
    usage_reference_file: str = typer.Option(
        "data/usage_reference/usage_standards.parquet",
        "--usage-reference-file",
        help="Usage-reference file path",
    ),
):
    """Analyze match quality between reference and usage-reference datasets."""
    coverage_cmd.analyze_match_quality(
        cloud_provider, service, usage_family, reference_file, usage_reference_file
    )


@app.command()
def recommend(
    cloud_provider: Optional[str] = typer.Option(
        None, "--cloud-provider", help="Filter by cloud provider"
    ),
    service: Optional[str] = typer.Option(None, "--service", help="Filter by service"),
    with_usage_reference: bool = typer.Option(
        False,
        "--with-usage-reference",
        help="Prioritize combinations with usage-reference data",
    ),
    limit: int = typer.Option(20, "--limit", help="Number of recommendations"),
    reference_file: str = typer.Option(
        "data/providers.csv", "--reference-file", help="Reference file path"
    ),
    usage_reference_file: str = typer.Option(
        "data/usage_reference/usage_standards.parquet",
        "--usage-reference-file",
        help="Usage-reference file path",
    ),
):
    """Recommend high-quality combinations for generation configs."""
    coverage_cmd.recommend_combinations(
        cloud_provider,
        service,
        with_usage_reference,
        limit,
        reference_file,
        usage_reference_file,
    )
