import uuid
from datetime import datetime, timezone

from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy import select, func, case
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import selectinload

from app.api.deps import get_current_user
from app.database import get_db
from app.models.lifecycle import LifecycleProject, ProjectStage
from app.models.organization import Organization, OrgMember
from app.models.user import User
from app.schemas.lifecycle import (
    ProjectCreateRequest,
    ProjectResponse,
    ProjectStatsResponse,
    QuickStartResponse,
    StageDetailResponse,
    StageUpdateRequest,
    TimelineEvent,
)

router = APIRouter()

STAGE_NAMES = {
    1: "品牌基建",
    2: "内容生产",
    3: "AI适配优化",
    4: "权威信号构建",
    5: "持续运维",
}

STAGE_INT_TO_STR = {
    1: "diagnosis",
    2: "strategy",
    3: "content",
    4: "publishing",
    5: "monitoring",
}


# ---------- helpers ----------

async def _get_or_create_org(
    db: AsyncSession, user: User
) -> uuid.UUID:
    """Return the user's organization_id; create a default org if missing."""
    if user.organization_id:
        return user.organization_id

    slug = f"default-{user.id.hex[:8]}"
    org = Organization(
        name=f"{user.name or user.email.split('@')[0]}'s Organization",
        slug=slug,
        plan="free",
    )
    db.add(org)
    await db.flush()

    # link user
    user.organization_id = org.id
    db.add(user)

    # add owner membership
    membership = OrgMember(
        organization_id=org.id,
        user_id=user.id,
        role="owner",
    )
    db.add(membership)
    await db.flush()
    return org.id


async def _load_project_with_stages(
    db: AsyncSession, project_id: uuid.UUID, org_id: uuid.UUID
) -> LifecycleProject | None:
    stmt = (
        select(LifecycleProject)
        .where(
            LifecycleProject.id == project_id,
            LifecycleProject.organization_id == org_id,
        )
        .options(selectinload(LifecycleProject.stages))
    )
    result = await db.execute(stmt)
    return result.scalar_one_or_none()


# ---------- endpoints ----------

@router.get("/projects/", response_model=list[ProjectResponse])
async def list_projects(
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    org_id = current_user.organization_id
    if not org_id:
        return []
    stmt = (
        select(LifecycleProject)
        .where(LifecycleProject.organization_id == org_id)
        .options(selectinload(LifecycleProject.stages))
        .order_by(LifecycleProject.created_at.desc())
    )
    result = await db.execute(stmt)
    projects = result.scalars().all()
    return projects


@router.get("/projects/stats", response_model=ProjectStatsResponse)
async def project_stats(
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    org_id = current_user.organization_id
    if not org_id:
        return ProjectStatsResponse(
            total_projects=0,
            active_projects=0,
            completed_projects=0,
            contents_produced=0,
            avg_ai_citation_rate=None,
            current_stage_distribution={},
            stage_distribution={},
            completion_rate=0.0,
        )

    # total & active counts
    count_stmt = select(
        func.count().label("total"),
        func.count().filter(LifecycleProject.status == "active").label("active"),
    ).where(LifecycleProject.organization_id == org_id)
    result = await db.execute(count_stmt)
    row = result.one()
    total = row.total
    active = row.active

    # stage distribution
    dist_stmt = (
        select(
            ProjectStage.status,
            func.count().label("cnt"),
        )
        .join(LifecycleProject, ProjectStage.project_id == LifecycleProject.id)
        .where(LifecycleProject.organization_id == org_id)
        .group_by(ProjectStage.status)
    )
    dist_result = await db.execute(dist_stmt)
    stage_distribution = {r.status: r.cnt for r in dist_result.all()}

    # completion rate: projects where current_stage >= 5
    comp_stmt = select(
        func.count().filter(LifecycleProject.current_stage >= 5).label("done"),
    ).where(LifecycleProject.organization_id == org_id)
    comp_result = await db.execute(comp_stmt)
    done = comp_result.scalar() or 0
    completion_rate = round(done / total, 4) if total > 0 else 0.0

    # completed projects
    completed_stmt = select(
        func.count().filter(LifecycleProject.status == "completed").label("completed"),
    ).where(LifecycleProject.organization_id == org_id)
    completed_result = await db.execute(completed_stmt)
    completed = completed_result.scalar() or 0

    # contents produced (count from content table if available)
    try:
        from app.models.content import Content
        contents_stmt = select(func.count()).where(Content.organization_id == org_id)
        contents_result = await db.execute(contents_stmt)
        contents_produced = contents_result.scalar() or 0
    except Exception:
        contents_produced = 0

    # avg AI citation rate
    try:
        from app.models.citation_record import CitationRecord
        from app.models.query import Query as QueryModel
        # Query uses user_id, so join through users table to get org members
        from app.models.organization import OrgMember
        org_user_ids_stmt = select(OrgMember.user_id).where(OrgMember.organization_id == org_id)
        org_user_ids_result = await db.execute(org_user_ids_stmt)
        org_user_ids = [r.user_id for r in org_user_ids_result.all()]
        if org_user_ids:
            citation_stmt = select(
                func.count().label("total_citations"),
                func.count().filter(CitationRecord.cited == True).label("cited_count"),
            ).join(QueryModel, CitationRecord.query_id == QueryModel.id).where(
                QueryModel.user_id.in_(org_user_ids),
            )
            citation_result = await db.execute(citation_stmt)
            citation_row = citation_result.one()
            total_citations = citation_row.total_citations or 0
            cited_count = citation_row.cited_count or 0
            avg_ai_citation_rate = round(cited_count / total_citations, 4) if total_citations > 0 else None
        else:
            avg_ai_citation_rate = None
    except Exception:
        avg_ai_citation_rate = None

    # current stage distribution (map int stage to string)
    current_stage_dist_stmt = (
        select(
            LifecycleProject.current_stage,
            func.count().label("cnt"),
        )
        .where(LifecycleProject.organization_id == org_id)
        .group_by(LifecycleProject.current_stage)
    )
    current_stage_dist_result = await db.execute(current_stage_dist_stmt)
    current_stage_distribution = {}
    for r in current_stage_dist_result.all():
        stage_key = STAGE_INT_TO_STR.get(r.current_stage, str(r.current_stage))
        current_stage_distribution[stage_key] = current_stage_distribution.get(stage_key, 0) + r.cnt

    return ProjectStatsResponse(
        total_projects=total,
        active_projects=active,
        completed_projects=completed,
        contents_produced=contents_produced,
        avg_ai_citation_rate=avg_ai_citation_rate,
        current_stage_distribution=current_stage_distribution,
        stage_distribution=stage_distribution,
        completion_rate=completion_rate,
    )


@router.get("/projects/{project_id}/timeline", response_model=list[TimelineEvent])
async def project_timeline(
    project_id: uuid.UUID,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    org_id = current_user.organization_id
    if not org_id:
        return []

    project = await _load_project_with_stages(db, project_id, org_id)
    if not project:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Project not found")

    events: list[TimelineEvent] = []

    # project creation event
    events.append(
        TimelineEvent(
            event_type="project_created",
            description=f"项目「{project.brand_name}」创建",
            timestamp=project.created_at,
            stage_number=None,
        )
    )

    # stage events
    for stage in sorted(project.stages, key=lambda s: s.stage_number):
        stage_label = STAGE_NAMES.get(stage.stage_number, f"阶段 {stage.stage_number}")
        if stage.started_at:
            events.append(
                TimelineEvent(
                    event_type="stage_started",
                    description=f"{stage_label} 开始",
                    timestamp=stage.started_at,
                    stage_number=stage.stage_number,
                )
            )
        if stage.completed_at:
            events.append(
                TimelineEvent(
                    event_type="stage_completed",
                    description=f"{stage_label} 完成",
                    timestamp=stage.completed_at,
                    stage_number=stage.stage_number,
                )
            )

    events.sort(key=lambda e: e.timestamp)
    return events


@router.post("/projects/quick-start", response_model=QuickStartResponse, status_code=status.HTTP_201_CREATED)
async def quick_start(
    body: ProjectCreateRequest,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    org_id = await _get_or_create_org(db, current_user)

    # create project
    project = LifecycleProject(
        organization_id=org_id,
        brand_name=body.brand_name,
        brand_aliases=[],
        current_stage=1,
        status="active",
        created_by=current_user.id,
    )
    db.add(project)
    await db.flush()

    # create 5 stages
    now = datetime.utcnow()
    for i in range(1, 6):
        stage = ProjectStage(
            project_id=project.id,
            stage_number=i,
            status="active" if i == 1 else "pending",
            started_at=now if i == 1 else None,
        )
        db.add(stage)

    await db.flush()
    await db.commit()

    # reload with stages
    project = await _load_project_with_stages(db, project.id, org_id)

    return QuickStartResponse(
        project=ProjectResponse.model_validate(project),
        message=f"项目「{body.brand_name}」已创建，从品牌基建阶段开始",
    )


@router.get("/projects/{project_id}/stages", response_model=list[StageDetailResponse])
async def list_stages(
    project_id: uuid.UUID,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    org_id = current_user.organization_id
    if not org_id:
        return []

    project = await _load_project_with_stages(db, project_id, org_id)
    if not project:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Project not found")

    return sorted(project.stages, key=lambda s: s.stage_number)


@router.put("/projects/{project_id}/stages/{stage_number}", response_model=StageDetailResponse)
async def update_stage(
    project_id: uuid.UUID,
    stage_number: int,
    body: StageUpdateRequest,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    org_id = current_user.organization_id
    if not org_id:
        raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="用户未关联组织，无法修改项目阶段")

    # verify project ownership
    project = await _load_project_with_stages(db, project_id, org_id)
    if not project:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Project not found")

    # find the stage
    stage: ProjectStage | None = None
    for s in project.stages:
        if s.stage_number == stage_number:
            stage = s
            break
    if not stage:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Stage not found")

    now = datetime.utcnow()

    # update fields
    if body.status is not None:
        stage.status = body.status
        if body.status == "active" and stage.started_at is None:
            stage.started_at = now
        if body.status == "completed":
            stage.completed_at = now
            # advance project current_stage
            if stage_number >= project.current_stage:
                project.current_stage = stage_number + 1

    if body.notes is not None:
        stage.notes = body.notes
    if body.metrics is not None:
        stage.metrics = body.metrics

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