"""
Reputation Pipeline - Primitives-based classification and reputation analytics.

This pipeline uses the new primitives taxonomy (MANNER, SPEED, VALUE_FOR_MONEY, etc.)
instead of the legacy URT codes. It powers the Reputation Report product.

Stages:
- classify: LLM-powered span extraction with primitives (stored in detected_spans_v2)
- report: Generate reputation report JSON

Usage:
    pipeline = ReputationPipeline()
    await pipeline.initialize()
    result = await pipeline.process({"business_id": "Go Karts Mar Menor", "days": 365})
"""

from __future__ import annotations

import json
import logging
import time
import uuid
from datetime import datetime, timedelta
from typing import TYPE_CHECKING, Any

from pipeline_core import (
    BasePipeline,
    DashboardConfig,
    DashboardSection,
    PipelineMetadata,
    PipelineResult as BasePipelineResult,
    StageResult,
    WidgetConfig,
)

from reviewiq_pipeline.config import Config
from reviewiq_pipeline.db.connection import DatabasePool

if TYPE_CHECKING:
    import asyncpg

logger = logging.getLogger(__name__)

# Stage names
STAGE_NAMES = ["classify", "report"]

# Domain mapping for primitives
DOMAIN_MAP = {
    # Output/Product (O)
    "TASTE": "O", "CRAFT": "O", "FRESHNESS": "O", "TEMPERATURE": "O",
    "EFFECTIVENESS": "O", "ACCURACY": "O", "CONDITION": "O", "CONSISTENCY": "O",
    # People/Service (P)
    "MANNER": "P", "COMPETENCE": "P", "ATTENTIVENESS": "P", "COMMUNICATION": "P",
    # Journey/Process (J)
    "SPEED": "J", "FRICTION": "J", "RELIABILITY": "J", "AVAILABILITY": "J",
    # Environment (E)
    "CLEANLINESS": "E", "COMFORT": "E", "SAFETY": "E", "AMBIANCE": "E",
    "ACCESSIBILITY": "E", "DIGITAL_UX": "E",
    # Value (V)
    "PRICE_LEVEL": "V", "PRICE_FAIRNESS": "V", "PRICE_TRANSPARENCY": "V",
    "VALUE_FOR_MONEY": "V",
    # Meta
    "HONESTY": "meta", "ETHICS": "meta", "PROMISES": "meta",
    "ACKNOWLEDGMENT": "meta", "RESPONSE_QUALITY": "meta", "RECOVERY": "meta",
    "RETURN_INTENT": "meta", "RECOMMEND": "meta", "RECOGNITION": "meta",
    "UNMAPPED": "meta", "NON_INFORMATIVE": "meta",
}

DOMAIN_NAMES = {
    "O": "Output/Product",
    "P": "People/Service",
    "J": "Journey/Process",
    "E": "Environment",
    "V": "Value",
    "meta": "Meta",
}


class ReputationPipeline(BasePipeline):
    """
    Reputation Pipeline - Primitives-based classification and analytics.

    Uses the new primitives taxonomy (37 primitives across 5 domains + meta)
    for more actionable, business-friendly insights.
    """

    def __init__(self, config: Config | None = None):
        """Initialize the pipeline."""
        self._config = config or Config()
        self._db: DatabasePool | None = None
        self._initialized = False

    @property
    def config(self) -> Config:
        """Get pipeline configuration."""
        return self._config

    @property
    def metadata(self) -> PipelineMetadata:
        """Get pipeline metadata."""
        return PipelineMetadata(
            id="reputation",
            name="Reputation Analytics Pipeline",
            description="Primitives-based classification and reputation scoring. Generates business-facing analytics reports.",
            version="2.0.0",
            stages=STAGE_NAMES,
            input_type="BusinessInput",
        )

    async def initialize(self) -> None:
        """Initialize database connections."""
        if self._initialized:
            return

        logger.info("Initializing Reputation pipeline...")

        self._db = DatabasePool(self._config)
        await self._db.initialize()

        self._initialized = True
        logger.info("Reputation pipeline initialized")

    async def close(self) -> None:
        """Close all connections."""
        if self._db:
            await self._db.close()
            self._db = None

        self._initialized = False
        logger.info("Reputation pipeline closed")

    async def process(
        self,
        input_data: dict[str, Any],
        stages: list[str] | None = None,
    ) -> BasePipelineResult:
        """
        Process input data through the pipeline.

        Args:
            input_data: Must contain business_id OR job_id. Optional: days, start, end
            stages: List of stage names to run (default: all)

        Returns:
            BasePipelineResult with stage outputs
        """
        await self.initialize()

        stages = stages or STAGE_NAMES
        stages_run: list[str] = []
        stage_results: dict[str, StageResult] = {}

        business_id = input_data.get("business_id")
        job_id = input_data.get("job_id")

        # Resolve business_id from job_id if not provided directly
        if not business_id and job_id:
            try:
                async with self._db.pool.acquire() as conn:
                    row = await conn.fetchrow(
                        "SELECT business_name FROM jobs WHERE job_id = $1",
                        uuid.UUID(job_id) if isinstance(job_id, str) else job_id,
                    )
                    if row and row["business_name"]:
                        business_id = row["business_name"]
                        logger.info(f"Resolved business_id '{business_id}' from job_id '{job_id}'")
            except Exception as e:
                logger.warning(f"Failed to resolve business_id from job_id: {e}")

        if not business_id:
            return BasePipelineResult(
                pipeline_id="reputation",
                stages_run=[],
                stage_results={},
                success=False,
                error="business_id is required (provide business_id or job_id)",
            )

        # Parse time window
        days = input_data.get("days", 365)
        end_date = datetime.utcnow()
        start_date = end_date - timedelta(days=days)

        if input_data.get("start"):
            start_date = datetime.fromisoformat(input_data["start"])
        if input_data.get("end"):
            end_date = datetime.fromisoformat(input_data["end"])

        try:
            # Stage: Classify (uses existing spans from detected_spans_v2)
            if "classify" in stages:
                start = time.time()
                logger.info(f"Running Classification check for {business_id}")

                try:
                    classify_result = await self._check_classification(
                        business_id, start_date, end_date
                    )
                    duration_ms = int((time.time() - start) * 1000)
                    stages_run.append("classify")
                    stage_results["classify"] = StageResult(
                        stage="classify",
                        success=True,
                        data=classify_result,
                        error=None,
                        duration_ms=duration_ms,
                    )
                except Exception as e:
                    logger.exception("Classification check failed")
                    stage_results["classify"] = StageResult(
                        stage="classify",
                        success=False,
                        data={},
                        error=str(e),
                        duration_ms=int((time.time() - start) * 1000),
                    )

            # Stage: Report (generate reputation report)
            if "report" in stages:
                start = time.time()
                logger.info(f"Generating Reputation Report for {business_id}")

                try:
                    report_result = await self._generate_report(
                        business_id, start_date, end_date
                    )
                    duration_ms = int((time.time() - start) * 1000)
                    stages_run.append("report")
                    stage_results["report"] = StageResult(
                        stage="report",
                        success=True,
                        data=report_result,
                        error=None,
                        duration_ms=duration_ms,
                    )
                except Exception as e:
                    logger.exception("Report generation failed")
                    stage_results["report"] = StageResult(
                        stage="report",
                        success=False,
                        data={},
                        error=str(e),
                        duration_ms=int((time.time() - start) * 1000),
                    )

            return BasePipelineResult(
                pipeline_id="reputation",
                stages_run=stages_run,
                stage_results=stage_results,
                success=all(r["success"] for r in stage_results.values()),
            )

        except Exception as e:
            logger.exception("Pipeline failed with unexpected error")
            return BasePipelineResult(
                pipeline_id="reputation",
                stages_run=stages_run,
                stage_results=stage_results,
                success=False,
                error=str(e),
            )

    async def _check_classification(
        self,
        business_id: str,
        start_date: datetime,
        end_date: datetime,
    ) -> dict[str, Any]:
        """Check classification coverage for the business."""
        if not self._db:
            return {"error": "Database not initialized"}

        async with self._db.pool.acquire() as conn:
            # Get span counts
            row = await conn.fetchrow(
                """
                SELECT
                    COUNT(*) as total_spans,
                    COUNT(*) FILTER (WHERE valence = '+') as positive,
                    COUNT(*) FILTER (WHERE valence = '-') as negative,
                    COUNT(*) FILTER (WHERE valence = '0') as neutral,
                    COUNT(*) FILTER (WHERE valence = '±') as mixed,
                    COUNT(*) FILTER (WHERE primitive = 'UNMAPPED') as unmapped,
                    COUNT(*) FILTER (WHERE primitive = 'NON_INFORMATIVE') as non_informative,
                    COUNT(DISTINCT s.review_id) as reviews_with_spans
                FROM pipeline.detected_spans_v2 s
                JOIN pipeline.review_facts_v1 f
                    ON f.review_id = s.review_id AND f.business_id = s.business_id
                WHERE s.business_id = $1
                    AND f.review_time_utc >= $2
                    AND f.review_time_utc < $3
                """,
                business_id,
                start_date,
                end_date,
            )

            if not row or row["total_spans"] == 0:
                return {
                    "status": "no_data",
                    "message": "No classified spans found for this business/period",
                    "total_spans": 0,
                }

            total = row["total_spans"]
            unmapped_rate = row["unmapped"] / total if total > 0 else 0

            return {
                "status": "ok" if unmapped_rate < 0.10 else "needs_attention",
                "total_spans": total,
                "reviews_with_spans": row["reviews_with_spans"],
                "positive_count": row["positive"],
                "negative_count": row["negative"],
                "neutral_count": row["neutral"],
                "mixed_count": row["mixed"],
                "unmapped_count": row["unmapped"],
                "non_informative_count": row["non_informative"],
                "unmapped_rate": round(unmapped_rate * 100, 1),
            }

    async def _generate_report(
        self,
        business_id: str,
        start_date: datetime,
        end_date: datetime,
    ) -> dict[str, Any]:
        """Generate a reputation report summary."""
        if not self._db:
            return {"error": "Database not initialized"}

        async with self._db.pool.acquire() as conn:
            # Get overall scores
            row = await conn.fetchrow(
                """
                WITH span_data AS (
                    SELECT
                        s.primitive,
                        s.valence,
                        s.confidence,
                        s.intensity,
                        CASE s.valence
                            WHEN '+' THEN 1
                            WHEN '-' THEN -1
                            ELSE 0
                        END as valence_num
                    FROM pipeline.detected_spans_v2 s
                    JOIN pipeline.review_facts_v1 f
                        ON f.review_id = s.review_id AND f.business_id = s.business_id
                    WHERE s.business_id = $1
                        AND f.review_time_utc >= $2
                        AND f.review_time_utc < $3
                        AND s.primitive NOT IN ('UNMAPPED', 'NON_INFORMATIVE')
                )
                SELECT
                    COUNT(*) as content_spans,
                    ROUND(
                        100.0 * SUM(valence_num * confidence * intensity) /
                        NULLIF(SUM(confidence * intensity), 0),
                        1
                    ) as overall_score,
                    ROUND(100.0 * COUNT(*) FILTER (WHERE valence = '+') / NULLIF(COUNT(*), 0), 1) as positive_share
                FROM span_data
                """,
                business_id,
                start_date,
                end_date,
            )

            if not row or row["content_spans"] == 0:
                return {
                    "status": "no_data",
                    "message": "No content spans found",
                }

            # Get domain breakdown
            domain_rows = await conn.fetch(
                """
                SELECT
                    s.primitive,
                    COUNT(*) as count,
                    ROUND(
                        100.0 * SUM(
                            CASE s.valence WHEN '+' THEN 1 WHEN '-' THEN -1 ELSE 0 END
                            * s.confidence * s.intensity
                        ) / NULLIF(SUM(s.confidence * s.intensity), 0),
                        1
                    ) as score
                FROM pipeline.detected_spans_v2 s
                JOIN pipeline.review_facts_v1 f
                    ON f.review_id = s.review_id AND f.business_id = s.business_id
                WHERE s.business_id = $1
                    AND f.review_time_utc >= $2
                    AND f.review_time_utc < $3
                    AND s.primitive NOT IN ('UNMAPPED', 'NON_INFORMATIVE')
                GROUP BY s.primitive
                ORDER BY count DESC
                """,
                business_id,
                start_date,
                end_date,
            )

            # Aggregate by domain
            domain_scores = {}
            primitive_scores = {}
            for r in domain_rows:
                prim = r["primitive"]
                domain = DOMAIN_MAP.get(prim, "meta")

                primitive_scores[prim] = {
                    "domain": domain,
                    "score": float(r["score"]) if r["score"] else 0,
                    "volume": r["count"],
                }

                if domain not in domain_scores:
                    domain_scores[domain] = {"total_score": 0, "total_volume": 0}
                domain_scores[domain]["total_score"] += (r["score"] or 0) * r["count"]
                domain_scores[domain]["total_volume"] += r["count"]

            # Calculate domain averages
            domains = {}
            for domain, data in domain_scores.items():
                if data["total_volume"] > 0:
                    domains[domain] = {
                        "name": DOMAIN_NAMES.get(domain, domain),
                        "score": round(data["total_score"] / data["total_volume"], 1),
                        "volume": data["total_volume"],
                    }

            # Get top drivers
            top_positive = await conn.fetch(
                """
                SELECT
                    s.primitive,
                    COUNT(*) as count,
                    ROUND(100.0 * COUNT(*) / (
                        SELECT COUNT(*) FROM pipeline.detected_spans_v2 s2
                        JOIN pipeline.review_facts_v1 f2 ON f2.review_id = s2.review_id AND f2.business_id = s2.business_id
                        WHERE s2.business_id = $1 AND s2.valence = '+'
                            AND f2.review_time_utc >= $2 AND f2.review_time_utc < $3
                    ), 1) as impact
                FROM pipeline.detected_spans_v2 s
                JOIN pipeline.review_facts_v1 f
                    ON f.review_id = s.review_id AND f.business_id = s.business_id
                WHERE s.business_id = $1 AND s.valence = '+'
                    AND f.review_time_utc >= $2 AND f.review_time_utc < $3
                    AND s.primitive NOT IN ('UNMAPPED', 'NON_INFORMATIVE')
                GROUP BY s.primitive
                ORDER BY count DESC
                LIMIT 5
                """,
                business_id,
                start_date,
                end_date,
            )

            top_negative = await conn.fetch(
                """
                SELECT
                    s.primitive,
                    COUNT(*) as count,
                    ROUND(100.0 * COUNT(*) / NULLIF((
                        SELECT COUNT(*) FROM pipeline.detected_spans_v2 s2
                        JOIN pipeline.review_facts_v1 f2 ON f2.review_id = s2.review_id AND f2.business_id = s2.business_id
                        WHERE s2.business_id = $1 AND s2.valence = '-'
                            AND f2.review_time_utc >= $2 AND f2.review_time_utc < $3
                    ), 0), 1) as impact
                FROM pipeline.detected_spans_v2 s
                JOIN pipeline.review_facts_v1 f
                    ON f.review_id = s.review_id AND f.business_id = s.business_id
                WHERE s.business_id = $1 AND s.valence = '-'
                    AND f.review_time_utc >= $2 AND f.review_time_utc < $3
                    AND s.primitive NOT IN ('UNMAPPED', 'NON_INFORMATIVE')
                GROUP BY s.primitive
                ORDER BY count DESC
                LIMIT 5
                """,
                business_id,
                start_date,
                end_date,
            )

            return {
                "status": "ok",
                "business_id": business_id,
                "window": {
                    "start": start_date.isoformat(),
                    "end": end_date.isoformat(),
                },
                "scores": {
                    "overall": float(row["overall_score"]) if row["overall_score"] else 0,
                    "positive_share": float(row["positive_share"]) if row["positive_share"] else 0,
                    "content_spans": row["content_spans"],
                },
                "domains": domains,
                "primitives": primitive_scores,
                "drivers": {
                    "positives": [
                        {"primitive": r["primitive"], "count": r["count"], "impact": float(r["impact"]) if r["impact"] else 0}
                        for r in top_positive
                    ],
                    "negatives": [
                        {"primitive": r["primitive"], "count": r["count"], "impact": float(r["impact"]) if r["impact"] else 0}
                        for r in top_negative
                    ],
                },
            }

    def get_dashboard_config(self) -> DashboardConfig:
        """Get the dashboard configuration for Reputation Pipeline."""
        return DashboardConfig(
            pipeline_id="reputation",
            title="Reputation Analytics",
            description="Primitives-based reputation scoring and business insights",
            sections=[
                DashboardSection(
                    id="overview",
                    title="Reputation Overview",
                    description="Overall reputation score and key metrics",
                    widgets=[
                        WidgetConfig(
                            id="reputation_score",
                            type="stat_card",
                            title="Reputation Score",
                            grid={"x": 0, "y": 0, "w": 3, "h": 1},
                            config={
                                "value_key": "overall_score",
                                "format": "{value:.0f}",
                                "icon": "trending-up",
                                "color": "blue",
                            },
                        ),
                        WidgetConfig(
                            id="positive_share",
                            type="stat_card",
                            title="Positive Share",
                            grid={"x": 3, "y": 0, "w": 3, "h": 1},
                            config={
                                "value_key": "positive_share",
                                "format": "{value:.1f}%",
                                "icon": "thumbs-up",
                                "color": "green",
                            },
                        ),
                        WidgetConfig(
                            id="content_spans",
                            type="stat_card",
                            title="Content Spans",
                            grid={"x": 6, "y": 0, "w": 3, "h": 1},
                            config={
                                "value_key": "content_spans",
                                "format": "{value:,}",
                                "icon": "message-square",
                                "color": "purple",
                            },
                        ),
                        WidgetConfig(
                            id="unmapped_rate",
                            type="stat_card",
                            title="Unmapped Rate",
                            grid={"x": 9, "y": 0, "w": 3, "h": 1},
                            config={
                                "value_key": "unmapped_rate",
                                "format": "{value:.1f}%",
                                "icon": "alert-circle",
                                "color": "orange",
                            },
                        ),
                    ],
                    collapsed=False,
                ),
                DashboardSection(
                    id="domains",
                    title="Domain Breakdown",
                    description="Performance across experience domains",
                    widgets=[
                        WidgetConfig(
                            id="domain_scores",
                            type="bar_chart",
                            title="Domain Scores",
                            grid={"x": 0, "y": 0, "w": 6, "h": 2},
                            config={
                                "x_axis": {"key": "domain", "type": "category"},
                                "y_axis": {"key": "score", "label": "Score"},
                                "series": [{"key": "score", "name": "Score"}],
                            },
                        ),
                        WidgetConfig(
                            id="domain_volume",
                            type="pie_chart",
                            title="Mentions by Domain",
                            grid={"x": 6, "y": 0, "w": 6, "h": 2},
                            config={
                                "value_key": "volume",
                                "label_key": "name",
                                "show_legend": True,
                            },
                        ),
                    ],
                    collapsed=False,
                ),
                DashboardSection(
                    id="drivers",
                    title="Key Drivers",
                    description="Top positive and negative drivers",
                    widgets=[
                        WidgetConfig(
                            id="positive_drivers",
                            type="bar_chart",
                            title="Top Strengths",
                            grid={"x": 0, "y": 0, "w": 6, "h": 2},
                            config={
                                "x_axis": {"key": "primitive", "type": "category"},
                                "y_axis": {"key": "impact", "label": "Impact %"},
                                "series": [{"key": "impact", "name": "Impact", "color": "#22c55e"}],
                            },
                        ),
                        WidgetConfig(
                            id="negative_drivers",
                            type="bar_chart",
                            title="Top Weaknesses",
                            grid={"x": 6, "y": 0, "w": 6, "h": 2},
                            config={
                                "x_axis": {"key": "primitive", "type": "category"},
                                "y_axis": {"key": "impact", "label": "Impact %"},
                                "series": [{"key": "impact", "name": "Impact", "color": "#ef4444"}],
                            },
                        ),
                    ],
                    collapsed=False,
                ),
                DashboardSection(
                    id="primitives",
                    title="Primitive Analysis",
                    description="Detailed breakdown by primitive",
                    widgets=[
                        WidgetConfig(
                            id="primitives_table",
                            type="table",
                            title="All Primitives",
                            grid={"x": 0, "y": 0, "w": 12, "h": 3},
                            config={
                                "columns": [
                                    {"key": "primitive", "header": "Primitive", "width": 150},
                                    {"key": "domain", "header": "Domain", "width": 100},
                                    {"key": "score", "header": "Score", "width": 80, "align": "right"},
                                    {"key": "volume", "header": "Mentions", "width": 80, "align": "right"},
                                ],
                                "row_key": "primitive",
                                "page_size": 15,
                                "sortable": True,
                            },
                        ),
                    ],
                    collapsed=True,
                ),
            ],
            default_time_range="365d",
            refresh_interval=600,
        )

    async def get_widget_data(
        self,
        widget_id: str,
        params: dict[str, Any],
    ) -> dict[str, Any]:
        """Get data for a specific dashboard widget."""
        await self.initialize()

        business_id = params.get("business_id")
        if not business_id:
            return {"error": "business_id required"}

        days = 365
        time_range = params.get("time_range", "365d")
        if time_range.endswith("d"):
            days = int(time_range[:-1])

        end_date = datetime.utcnow()
        start_date = end_date - timedelta(days=days)

        # Get classification check data
        classify_data = await self._check_classification(business_id, start_date, end_date)

        # Get report data
        report_data = await self._generate_report(business_id, start_date, end_date)

        match widget_id:
            # Overview stats
            case "reputation_score":
                return {"overall_score": report_data.get("scores", {}).get("overall", 0)}
            case "positive_share":
                return {"positive_share": report_data.get("scores", {}).get("positive_share", 0)}
            case "content_spans":
                return {"content_spans": report_data.get("scores", {}).get("content_spans", 0)}
            case "unmapped_rate":
                return {"unmapped_rate": classify_data.get("unmapped_rate", 0)}

            # Domain charts
            case "domain_scores":
                domains = report_data.get("domains", {})
                return {"data": [{"domain": k, **v} for k, v in domains.items()]}
            case "domain_volume":
                domains = report_data.get("domains", {})
                return {"data": [{"name": v["name"], "volume": v["volume"]} for v in domains.values()]}

            # Driver charts
            case "positive_drivers":
                return {"data": report_data.get("drivers", {}).get("positives", [])}
            case "negative_drivers":
                return {"data": report_data.get("drivers", {}).get("negatives", [])}

            # Primitives table
            case "primitives_table":
                primitives = report_data.get("primitives", {})
                return {
                    "data": [
                        {"primitive": k, **v}
                        for k, v in primitives.items()
                    ],
                    "total": len(primitives),
                }

            case _:
                logger.warning(f"Unknown widget: {widget_id}")
                return {"error": f"Unknown widget: {widget_id}"}

    async def health_check(self) -> dict[str, Any]:
        """Check pipeline health."""
        await self.initialize()

        checks = {}
        healthy = True

        # Check database connection
        try:
            if self._db:
                async with self._db.pool.acquire() as conn:
                    await conn.fetchval("SELECT 1")
                checks["database"] = "ok"
            else:
                checks["database"] = "not_initialized"
                healthy = False
        except Exception as e:
            checks["database"] = str(e)
            healthy = False

        # Check spans table exists
        try:
            if self._db:
                async with self._db.pool.acquire() as conn:
                    count = await conn.fetchval(
                        "SELECT COUNT(*) FROM pipeline.detected_spans_v2 LIMIT 1"
                    )
                checks["spans_table"] = "ok"
        except Exception as e:
            checks["spans_table"] = str(e)
            healthy = False

        return {
            "healthy": healthy,
            "checks": checks,
        }
