import logging
import uuid
from datetime import datetime, timedelta, timezone

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

from app.models.monitoring import MonitoringRecord, ContentBaseline
from app.models.query import Query
from app.models.citation_record import CitationRecord
from app.models.brand import Brand

logger = logging.getLogger(__name__)


class MonitorService:

    async def create_monitoring_record(
        self,
        db: AsyncSession,
        brand_id: uuid.UUID,
        content_id: str | None = None,
        query_keywords: str | None = None,
        platform: str | None = None,
        check_interval_hours: int = 24,
    ) -> MonitoringRecord:
        now = datetime.now(timezone.utc)

        stmt = select(Brand).where(Brand.id == brand_id)
        result = await db.execute(stmt)
        brand = result.scalar_one_or_none()
        brand_name = brand.name if brand else ""

        baseline_data = await self._get_current_metrics(db, brand_id, query_keywords, platform)

        record = MonitoringRecord(
            brand_id=brand_id,
            content_id=content_id,
            query_keywords=query_keywords,
            platform=platform,
            baseline_citation_count=baseline_data.get("citation_count", 0),
            baseline_sentiment=baseline_data.get("positive_ratio"),
            baseline_rank=baseline_data.get("avg_rank"),
            current_citation_count=baseline_data.get("citation_count", 0),
            current_sentiment=baseline_data.get("positive_ratio"),
            current_rank=baseline_data.get("avg_rank"),
            change_type="neutral",
            change_details=None,
            check_interval_hours=check_interval_hours,
            last_checked_at=now,
            next_check_at=now + timedelta(hours=check_interval_hours),
            status="active",
        )
        db.add(record)
        await db.flush()

        await self._create_baseline_snapshot(
            db=db,
            record_id=record.id,
            brand_name=brand_name,
            query_keywords=query_keywords,
            platform=platform,
            metrics=baseline_data,
        )

        await db.commit()
        await db.refresh(record)
        return record

    async def _create_baseline_snapshot(
        self,
        db: AsyncSession,
        record_id: uuid.UUID,
        brand_name: str,
        query_keywords: str | None,
        platform: str | None,
        metrics: dict,
    ) -> ContentBaseline:
        baseline = ContentBaseline(
            monitoring_record_id=record_id,
            brand_name=brand_name,
            keyword=query_keywords or "",
            platform=platform or "",
            citation_count=metrics.get("citation_count", 0),
            sentiment_score=metrics.get("positive_ratio"),
            rank_position=metrics.get("avg_rank"),
            snapshot_data=metrics,
        )
        db.add(baseline)
        await db.flush()
        return baseline

    async def get_brand_monitoring(
        self,
        db: AsyncSession,
        brand_id: uuid.UUID,
        skip: int = 0,
        limit: int = 20,
    ) -> tuple[list[MonitoringRecord], int]:
        count_stmt = select(func.count()).select_from(MonitoringRecord).where(
            MonitoringRecord.brand_id == brand_id,
        )
        count_result = await db.execute(count_stmt)
        total = count_result.scalar_one()

        stmt = (
            select(MonitoringRecord)
            .where(MonitoringRecord.brand_id == brand_id)
            .order_by(MonitoringRecord.created_at.desc())
            .offset(skip)
            .limit(limit)
        )
        result = await db.execute(stmt)
        records = list(result.scalars().all())

        return records, total

    async def check_and_compare(
        self,
        db: AsyncSession,
        record_id: uuid.UUID,
    ) -> MonitoringRecord | None:
        stmt = select(MonitoringRecord).where(MonitoringRecord.id == record_id)
        result = await db.execute(stmt)
        record = result.scalar_one_or_none()
        if not record:
            return None

        current_data = await self._get_current_metrics(
            db, record.brand_id, record.query_keywords, record.platform,
        )

        record.current_citation_count = current_data.get("citation_count", 0)
        record.current_sentiment = current_data.get("positive_ratio")
        record.current_rank = current_data.get("avg_rank")

        change_type = self.determine_change_type(
            baseline_citation=record.baseline_citation_count,
            current_citation=record.current_citation_count,
            baseline_sentiment=record.baseline_sentiment,
            current_sentiment=record.current_sentiment,
            baseline_rank=record.baseline_rank,
            current_rank=record.current_rank,
        )
        record.change_type = change_type

        change_details = self._build_change_details(record, current_data)
        record.change_details = change_details

        now = datetime.now(timezone.utc)
        record.last_checked_at = now
        record.next_check_at = now + timedelta(hours=record.check_interval_hours)

        await db.commit()
        await db.refresh(record)
        return record

    def determine_change_type(
        self,
        baseline_citation: int,
        current_citation: int,
        baseline_sentiment: float | None = None,
        current_sentiment: float | None = None,
        baseline_rank: int | None = None,
        current_rank: int | None = None,
    ) -> str:
        positive_signals = 0
        negative_signals = 0

        if current_citation > baseline_citation:
            positive_signals += 1
        elif current_citation < baseline_citation:
            negative_signals += 1

        if baseline_sentiment is not None and current_sentiment is not None:
            if current_sentiment > baseline_sentiment:
                positive_signals += 1
            elif current_sentiment < baseline_sentiment:
                negative_signals += 1

        if baseline_rank is not None and current_rank is not None:
            if current_rank < baseline_rank:
                positive_signals += 1
            elif current_rank > baseline_rank:
                negative_signals += 1

        if positive_signals > negative_signals:
            return "positive"
        elif negative_signals > positive_signals:
            return "negative"
        return "neutral"

    def _build_change_details(self, record: MonitoringRecord, current_data: dict) -> dict:
        details = {
            "citation_change": {
                "baseline": record.baseline_citation_count,
                "current": record.current_citation_count,
                "delta": record.current_citation_count - record.baseline_citation_count,
            },
        }

        if record.baseline_sentiment is not None and record.current_sentiment is not None:
            details["sentiment_change"] = {
                "baseline": record.baseline_sentiment,
                "current": record.current_sentiment,
                "delta": round(record.current_sentiment - record.baseline_sentiment, 4),
            }

        if record.baseline_rank is not None and record.current_rank is not None:
            details["rank_change"] = {
                "baseline": record.baseline_rank,
                "current": record.current_rank,
                "delta": record.current_rank - record.baseline_rank,
            }

        details["platform_data"] = current_data.get("platform_data", {})
        details["checked_at"] = datetime.now(timezone.utc).isoformat()

        return details

    async def generate_change_report(
        self,
        db: AsyncSession,
        record_id: uuid.UUID,
    ) -> dict | None:
        stmt = select(MonitoringRecord).where(MonitoringRecord.id == record_id)
        result = await db.execute(stmt)
        record = result.scalar_one_or_none()
        if not record:
            return None

        recommendations = self._generate_recommendations(record)

        baseline = {
            "citation_count": record.baseline_citation_count,
            "sentiment": record.baseline_sentiment,
            "rank": record.baseline_rank,
        }
        current = {
            "citation_count": record.current_citation_count,
            "sentiment": record.current_sentiment,
            "rank": record.current_rank,
        }

        return {
            "monitoring_record_id": str(record.id),
            "brand_id": str(record.brand_id),
            "change_type": record.change_type,
            "change_details": record.change_details,
            "baseline": baseline,
            "current": current,
            "recommendations": recommendations,
        }

    def _generate_recommendations(self, record: MonitoringRecord) -> list[str]:
        recommendations = []

        if record.change_type == "negative":
            if record.current_citation_count < record.baseline_citation_count:
                recommendations.append("引用量下降，建议增加高质量内容发布频率，提升品牌在AI搜索引擎中的曝光")
            if record.current_sentiment is not None and record.baseline_sentiment is not None:
                if record.current_sentiment < record.baseline_sentiment:
                    recommendations.append("情感倾向下降，建议关注负面评价并优化品牌形象内容")
            if record.current_rank is not None and record.baseline_rank is not None:
                if record.current_rank > record.baseline_rank:
                    recommendations.append("排名下降，建议优化GEO策略，提升内容在AI搜索中的引用优先级")

        elif record.change_type == "positive":
            if record.current_citation_count > record.baseline_citation_count:
                recommendations.append("引用量上升，建议继续保持当前内容策略")
            if record.current_sentiment is not None and record.baseline_sentiment is not None:
                if record.current_sentiment > record.baseline_sentiment:
                    recommendations.append("情感倾向改善，当前品牌内容策略效果良好")
        else:
            recommendations.append("各项指标保持稳定，建议持续监测")

        return recommendations

    async def _get_current_metrics(
        self,
        db: AsyncSession,
        brand_id: uuid.UUID,
        query_keywords: str | None = None,
        platform: str | None = None,
    ) -> dict:
        stmt = select(Brand).where(Brand.id == brand_id)
        result = await db.execute(stmt)
        brand = result.scalar_one_or_none()
        if not brand:
            return {
                "citation_count": 0,
                "positive_ratio": 0.0,
                "avg_rank": 0,
                "platform_data": {},
            }

        conditions = [Query.target_brand == brand.name]
        if query_keywords:
            conditions.append(Query.keyword.contains(query_keywords))

        queries_stmt = select(Query).where(*conditions)
        queries_result = await db.execute(queries_stmt)
        queries = list(queries_result.scalars().all())

        if not queries:
            return {
                "citation_count": 0,
                "positive_ratio": 0.0,
                "avg_rank": 0,
                "platform_data": {},
            }

        query_ids = [q.id for q in queries]
        citation_conditions = [CitationRecord.query_id.in_(query_ids)]
        if platform:
            citation_conditions.append(CitationRecord.platform == platform)

        citations_stmt = select(CitationRecord).where(*citation_conditions)
        citations_result = await db.execute(citations_stmt)
        all_citations = list(citations_result.scalars().all())

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

        sentiment_counts = {"positive": 0, "neutral": 0, "negative": 0}
        for citation in brand_citations:
            if citation.sentiment and citation.sentiment in sentiment_counts:
                sentiment_counts[citation.sentiment] += 1
            else:
                sentiment_counts["neutral"] += 1

        total_with_sentiment = sum(sentiment_counts.values())
        positive_ratio = (
            sentiment_counts["positive"] / total_with_sentiment
            if total_with_sentiment > 0
            else 0.0
        )

        positions = [c.citation_position for c in brand_citations if c.citation_position is not None]
        avg_rank = int(sum(positions) / len(positions)) if positions else 0

        platform_data = {}
        for citation in all_citations:
            p = citation.platform
            if p not in platform_data:
                platform_data[p] = {"total": 0, "cited": 0}
            platform_data[p]["total"] += 1
            if citation.cited:
                platform_data[p]["cited"] += 1

        platform_scores = {}
        for p, data in platform_data.items():
            platform_scores[p] = round(
                (data["cited"] / data["total"] * 100) if data["total"] > 0 else 0.0, 2
            )

        return {
            "citation_count": citation_count,
            "positive_ratio": round(positive_ratio, 4),
            "avg_rank": avg_rank,
            "platform_data": platform_scores,
        }
