import uuid
from dataclasses import dataclass, field
from datetime import datetime, timedelta

from sqlalchemy import select, func, Integer
from sqlalchemy.ext.asyncio import AsyncSession

from app.models.brand import Brand
from app.models.competitor import Competitor
from app.models.query import Query as QueryModel
from app.models.citation_record import CitationRecord
from app.schemas.scoring import CitationResult
from app.services.scoring.scoring_service import ScoringService, ScoringResultV2
from app.services.analysis.sentiment_service import get_sentiment_service

REQUIRED_PLATFORMS = [
    "wenxin",
    "kimi",
    "tongyi",
    "doubao",
    "xinghuo",
    "tiangong",
    "qingyan",
]


@dataclass
class BrandScoringResult:
    v2_result: ScoringResultV2
    competitor_data: dict = field(default_factory=dict)
    sentiment_counts: dict = field(default_factory=lambda: {"positive": 0, "neutral": 0, "negative": 0})
    platform_scores: dict = field(default_factory=dict)
    total_queries: int = 0
    mentioned_count: int = 0
    all_citations: list = field(default_factory=list)
    brand_citations: list = field(default_factory=list)
    change_from_yesterday: float = 0.0


class BrandScoringDataService:

    async def get_brand_scoring_data(
        self,
        db: AsyncSession,
        user_id: uuid.UUID,
        brand: Brand,
    ) -> BrandScoringResult:
        queries_stmt = select(QueryModel).where(
            QueryModel.user_id == user_id,
            QueryModel.target_brand == brand.name,
        )
        queries_result = await db.execute(queries_stmt)
        queries = list(queries_result.scalars().all())

        if not queries:
            scoring_service = ScoringService()
            empty_result = scoring_service.calculate_v2(
                mentioned_count=0,
                total_queries=0,
                positions=[],
                sentiment_counts={"positive": 0, "neutral": 0, "negative": 0},
                citations=[],
                brand_mentions=0,
                competitor_mentions={},
            )
            return BrandScoringResult(
                v2_result=empty_result,
                competitor_data={
                    "brand_mentions": 0,
                    "competitor_mentions": {},
                    "ahead_count": 0,
                    "behind_count": 0,
                },
                sentiment_counts={"positive": 0, "neutral": 0, "negative": 0},
                platform_scores={platform: 0.0 for platform in REQUIRED_PLATFORMS},
                total_queries=0,
                mentioned_count=0,
                all_citations=[],
                brand_citations=[],
                change_from_yesterday=0.0,
            )

        query_ids = [q.id for q in queries]

        citations_stmt = select(CitationRecord).where(
            CitationRecord.query_id.in_(query_ids),
        )
        citations_result = await db.execute(citations_stmt)
        all_citations = list(citations_result.scalars().all())

        total_queries = len(all_citations)
        brand_citations = [c for c in all_citations if c.cited]

        competitor_stmt = select(Competitor).where(Competitor.brand_id == brand.id)
        competitor_result = await db.execute(competitor_stmt)
        competitors = list(competitor_result.scalars().all())
        competitor_names = [c.name for c in competitors]

        competitor_mentions: dict[str, int] = {}
        for comp_name in competitor_names:
            count = sum(
                1 for c in all_citations
                if c.cited and c.competitor_brands
                and comp_name in c.competitor_brands
            )
            if count > 0:
                competitor_mentions[comp_name] = count

        sentiment_service = get_sentiment_service()
        sentiment_counts = {"positive": 0, "neutral": 0, "negative": 0}
        for citation in brand_citations:
            if citation.sentiment and citation.sentiment in ("positive", "neutral", "negative"):
                sentiment_counts[citation.sentiment] += 1
            else:
                content = citation.raw_response or citation.citation_text or ""
                if content.strip():
                    try:
                        result = await sentiment_service.analyze(
                            brand_name=brand.name,
                            content=content,
                        )
                        sentiment_counts[result.sentiment] += 1
                    except Exception:
                        sentiment_counts["neutral"] += 1
                else:
                    sentiment_counts["neutral"] += 1

        citation_results = [
            CitationResult(
                cited=c.cited,
                position=c.citation_position,
                citation_text=c.citation_text,
                sentiment="neutral",
                confidence=c.confidence or 0.0,
            )
            for c in brand_citations
        ]

        positions = [c.citation_position for c in brand_citations if c.cited]

        scoring_service = ScoringService()
        v2_result = scoring_service.calculate_v2(
            mentioned_count=len(brand_citations),
            total_queries=total_queries,
            positions=positions,
            sentiment_counts=sentiment_counts,
            citations=citation_results,
            brand_mentions=len(brand_citations),
            competitor_mentions=competitor_mentions,
        )

        platform_scores = await self.get_platform_scores(db, user_id, brand.id)

        competitor_data = {
            "brand_mentions": len(brand_citations),
            "competitor_mentions": competitor_mentions,
            "ahead_count": sum(1 for count in competitor_mentions.values() if len(brand_citations) > count),
            "behind_count": sum(1 for count in competitor_mentions.values() if len(brand_citations) <= count),
        }

        today = datetime.now().date()
        yesterday = today - timedelta(days=1)

        today_citations = [
            c for c in all_citations
            if c.queried_at.date() == today
        ]
        yesterday_citations = [
            c for c in all_citations
            if c.queried_at.date() == yesterday
        ]

        today_cited = sum(1 for c in today_citations if c.cited)
        today_total = len(today_citations)
        today_score = (today_cited / today_total * 100) if today_total > 0 else 0.0

        yesterday_cited = sum(1 for c in yesterday_citations if c.cited)
        yesterday_total = len(yesterday_citations)
        yesterday_score = (yesterday_cited / yesterday_total * 100) if yesterday_total > 0 else 0.0

        change_from_yesterday = round(today_score - yesterday_score, 2)

        return BrandScoringResult(
            v2_result=v2_result,
            competitor_data=competitor_data,
            sentiment_counts=sentiment_counts,
            platform_scores=platform_scores,
            total_queries=total_queries,
            mentioned_count=len(brand_citations),
            all_citations=all_citations,
            brand_citations=brand_citations,
            change_from_yesterday=change_from_yesterday,
        )

    async def get_platform_scores(
        self,
        db: AsyncSession,
        user_id: uuid.UUID,
        brand_id: uuid.UUID,
    ) -> dict[str, float]:
        brand_stmt = select(Brand).where(
            Brand.id == brand_id,
            Brand.user_id == user_id,
        )
        brand_result = await db.execute(brand_stmt)
        brand = brand_result.scalar_one_or_none()

        if not brand:
            return {platform: 0.0 for platform in REQUIRED_PLATFORMS}

        queries_stmt = select(QueryModel).where(
            QueryModel.user_id == user_id,
            QueryModel.target_brand == brand.name,
        )
        queries_result = await db.execute(queries_stmt)
        queries = list(queries_result.scalars().all())

        if not queries:
            return {platform: 0.0 for platform in REQUIRED_PLATFORMS}

        query_ids = [q.id for q in queries]

        citation_stmt = select(
            CitationRecord.platform,
            func.count().label("total"),
            func.sum(
                func.cast(
                    func.case((CitationRecord.cited == True, 1), else_=0),
                    Integer,
                )
            ).label("cited"),
        ).where(
            CitationRecord.query_id.in_(query_ids),
        ).group_by(CitationRecord.platform)

        result = await db.execute(citation_stmt)
        rows = result.all()

        platform_data = {row.platform: (row.total or 0, row.cited or 0) for row in rows}

        platform_scores = {}
        for platform in REQUIRED_PLATFORMS:
            total, cited = platform_data.get(platform, (0, 0))
            if total > 0:
                platform_scores[platform] = round((cited / total) * 100, 2)
            else:
                platform_scores[platform] = 0.0

        return platform_scores

    async def get_competitor_platform_scores(
        self,
        db: AsyncSession,
        user_id: uuid.UUID,
        brand_id: uuid.UUID,
    ) -> dict[str, float]:
        competitor_stmt = select(Competitor).where(Competitor.brand_id == brand_id)
        competitor_result = await db.execute(competitor_stmt)
        competitors = list(competitor_result.scalars().all())

        if not competitors:
            return {platform: 0.0 for platform in REQUIRED_PLATFORMS}

        competitor_names = [c.name for c in competitors]

        competitor_queries_stmt = select(QueryModel).where(
            QueryModel.user_id == user_id,
            QueryModel.target_brand.in_(competitor_names),
        )
        competitor_queries_result = await db.execute(competitor_queries_stmt)
        competitor_queries = list(competitor_queries_result.scalars().all())

        if not competitor_queries:
            return {platform: 0.0 for platform in REQUIRED_PLATFORMS}

        competitor_query_ids = [q.id for q in competitor_queries]

        citation_stmt = select(
            CitationRecord.platform,
            func.count().label("total"),
            func.sum(
                func.cast(
                    func.case((CitationRecord.cited == True, 1), else_=0),
                    Integer,
                )
            ).label("cited"),
        ).where(
            CitationRecord.query_id.in_(competitor_query_ids),
        ).group_by(CitationRecord.platform)

        result = await db.execute(citation_stmt)
        rows = result.all()

        platform_data = {row.platform: (row.total or 0, row.cited or 0) for row in rows}

        platform_scores = {}
        for platform in REQUIRED_PLATFORMS:
            total, cited = platform_data.get(platform, (0, 0))
            if total > 0:
                platform_scores[platform] = round((cited / total) * 100, 2)
            else:
                platform_scores[platform] = 0.0

        return platform_scores


_brand_scoring_data_service = BrandScoringDataService()


def get_brand_scoring_data_service() -> BrandScoringDataService:
    return _brand_scoring_data_service
