import logging
import uuid
from datetime import datetime

from fastapi import APIRouter, Depends, HTTPException, status
from pydantic import BaseModel
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession

from app.api.deps import get_current_user
from app.database import get_db
from app.models.attribution_record import AttributionRecord
from app.models.brand import Brand
from app.models.diagnosis_record import DiagnosisRecord
from app.models.user import User
from app.services.attribution.attribution_engine import AttributionEngine
from app.services.attribution.roi_calculator import ROICalculator

logger = logging.getLogger(__name__)

router = APIRouter()

PLAN_COSTS = {
    "free": 0.0,
    "starter": 99.0,
    "pro": 299.0,
    "enterprise": 999.0,
}


def _to_uuid(value: str | uuid.UUID) -> uuid.UUID:
    if isinstance(value, uuid.UUID):
        return value
    return uuid.UUID(str(value))


class StartTrackingRequest(BaseModel):
    brand_id: str
    content_id: str | None = None


class AttributionResponse(BaseModel):
    id: str
    brand_id: str
    content_id: str | None
    baseline_score: float
    current_score: float | None
    score_delta: float | None
    status: str
    roi_percentage: float | None
    created_at: datetime

    model_config = {"from_attributes": True}


class ROIReport(BaseModel):
    brand_id: str
    brand_name: str
    subscription_cost: float
    current_plan: str
    total_score_delta: float
    value_generated: float
    roi_percentage: float
    break_even_delta: float
    tracking_records: list[AttributionResponse]
    ab_comparison: dict | None


class ABComparisonResponse(BaseModel):
    brand_id: str
    brand_name: str
    overall_before: float
    overall_after: float
    overall_delta: float
    dimensions: list[dict]


def _record_to_response(record: AttributionRecord) -> AttributionResponse:
    return AttributionResponse(
        id=str(record.id),
        brand_id=str(record.brand_id),
        content_id=str(record.content_id) if record.content_id else None,
        baseline_score=record.baseline_score,
        current_score=record.current_score,
        score_delta=record.score_delta,
        status=record.status,
        roi_percentage=record.roi_percentage,
        created_at=record.created_at,
    )


async def _get_brand_or_404(
    brand_id: uuid.UUID,
    current_user: User,
    db: AsyncSession,
) -> Brand:
    user_uuid = _to_uuid(current_user.id)
    stmt = select(Brand).where(Brand.id == brand_id, Brand.user_id == user_uuid)
    result = await db.execute(stmt)
    brand = result.scalar_one_or_none()
    if not brand:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="品牌不存在",
        )
    return brand


@router.post("/start", response_model=AttributionResponse)
async def start_tracking(
    body: StartTrackingRequest,
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    brand_id = _to_uuid(body.brand_id)
    brand = await _get_brand_or_404(brand_id, current_user, db)

    content_id = _to_uuid(body.content_id) if body.content_id else None

    engine = AttributionEngine()
    record = await engine.start_tracking(db, brand.id, content_id, current_user.id)
    return _record_to_response(record)


@router.get("/brand/{brand_id}")
async def get_brand_attribution(
    brand_id: uuid.UUID,
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    brand = await _get_brand_or_404(brand_id, current_user, db)

    engine = AttributionEngine()
    summary = await engine.get_brand_attribution_summary(db, brand.id)
    summary["records"] = [_record_to_response(r) for r in summary["records"]]
    return summary


@router.get("/{record_id}", response_model=AttributionResponse)
async def get_attribution_record(
    record_id: uuid.UUID,
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    stmt = select(AttributionRecord).where(AttributionRecord.id == record_id)
    result = await db.execute(stmt)
    record = result.scalar_one_or_none()
    if not record:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="归因记录不存在",
        )
    return _record_to_response(record)


@router.post("/{record_id}/check", response_model=AttributionResponse)
async def check_attribution(
    record_id: uuid.UUID,
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    engine = AttributionEngine()
    try:
        record = await engine.check_attribution(db, record_id)
    except ValueError:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="归因记录不存在",
        )
    return _record_to_response(record)


@router.get("/roi/{brand_id}", response_model=ROIReport)
async def get_roi_report(
    brand_id: uuid.UUID,
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    brand = await _get_brand_or_404(brand_id, current_user, db)

    engine = AttributionEngine()
    summary = await engine.get_brand_attribution_summary(db, brand.id)

    user_plan = getattr(current_user, "plan", "free") or "free"
    subscription_cost = PLAN_COSTS.get(user_plan, 0.0)

    calculator = ROICalculator()
    roi_data = calculator.calculate_roi(
        subscription_cost=subscription_cost,
        score_delta=summary["total_score_delta"],
        attribution_records=summary["records"],
    )

    ab_comparison = None
    baseline_record = (
        await db.execute(
            select(DiagnosisRecord)
            .where(
                DiagnosisRecord.brand_id == brand.id,
                DiagnosisRecord.status == "completed",
            )
            .order_by(DiagnosisRecord.completed_at.asc())
            .limit(1)
        )
    ).scalar_one_or_none()
    latest_record = (
        await db.execute(
            select(DiagnosisRecord)
            .where(
                DiagnosisRecord.brand_id == brand.id,
                DiagnosisRecord.status == "completed",
            )
            .order_by(DiagnosisRecord.completed_at.desc())
            .limit(1)
        )
    ).scalar_one_or_none()

    if baseline_record and latest_record and baseline_record.id != latest_record.id:
        before_dims = baseline_record.result_json.get("dimensions", []) if baseline_record.result_json else []
        after_dims = latest_record.result_json.get("dimensions", []) if latest_record.result_json else []
        before_map = {d.get("name"): d for d in before_dims}
        after_map = {d.get("name"): d for d in after_dims}
        ab_comparison = calculator.generate_ab_comparison(
            before_score=baseline_record.overall_score or 0,
            after_score=latest_record.overall_score or 0,
            before_dimensions=before_map,
            after_dimensions=after_map,
        )

    return ROIReport(
        brand_id=str(brand.id),
        brand_name=brand.name,
        subscription_cost=subscription_cost,
        current_plan=user_plan,
        total_score_delta=summary["total_score_delta"],
        value_generated=roi_data["value_generated"],
        roi_percentage=roi_data["roi_percentage"],
        break_even_delta=roi_data["break_even_delta"],
        tracking_records=[_record_to_response(r) for r in summary["records"]],
        ab_comparison=ab_comparison,
    )


@router.get("/ab-comparison/{brand_id}", response_model=ABComparisonResponse)
async def get_ab_comparison(
    brand_id: uuid.UUID,
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    brand = await _get_brand_or_404(brand_id, current_user, db)

    baseline_record = (
        await db.execute(
            select(DiagnosisRecord)
            .where(
                DiagnosisRecord.brand_id == brand.id,
                DiagnosisRecord.status == "completed",
            )
            .order_by(DiagnosisRecord.completed_at.asc())
            .limit(1)
        )
    ).scalar_one_or_none()
    latest_record = (
        await db.execute(
            select(DiagnosisRecord)
            .where(
                DiagnosisRecord.brand_id == brand.id,
                DiagnosisRecord.status == "completed",
            )
            .order_by(DiagnosisRecord.completed_at.desc())
            .limit(1)
        )
    ).scalar_one_or_none()

    if not baseline_record or not latest_record:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="暂无诊断数据，无法生成A/B对比",
        )

    calculator = ROICalculator()
    before_dims = baseline_record.result_json.get("dimensions", []) if baseline_record.result_json else []
    after_dims = latest_record.result_json.get("dimensions", []) if latest_record.result_json else []
    before_map = {d.get("name"): d for d in before_dims}
    after_map = {d.get("name"): d for d in after_dims}
    comparison = calculator.generate_ab_comparison(
        before_score=baseline_record.overall_score or 0,
        after_score=latest_record.overall_score or 0,
        before_dimensions=before_map,
        after_dimensions=after_map,
    )

    return ABComparisonResponse(
        brand_id=str(brand.id),
        brand_name=brand.name,
        overall_before=comparison["overall_before"],
        overall_after=comparison["overall_after"],
        overall_delta=comparison["overall_delta"],
        dimensions=comparison["dimensions"],
    )
