"""
WBS (Work Breakdown Structure) 관리 API.

엔드포인트:
  GET    /api/si/projects/{pid}/wbs          — WBS 트리 조회
  POST   /api/si/projects/{pid}/wbs          — WBS 항목 생성
  GET    /api/si/projects/{pid}/wbs/{id}     — 단일 항목 조회
  PATCH  /api/si/projects/{pid}/wbs/{id}     — 항목 수정
  DELETE /api/si/projects/{pid}/wbs/{id}     — 항목 삭제
  PATCH  /api/si/projects/{pid}/wbs/{id}/progress — 리프 진척률 업데이트 (부모 자동 계산)
  POST   /api/si/projects/{pid}/wbs/bulk     — RFP 요구사항→WBS 일괄 자동 생성
  GET    /api/si/projects/{pid}/wbs/gantt    — 간트 차트용 데이터

WBS 계층:
  레벨 1 = 단계 (분석/설계/구현/인도)
  레벨 2 = 활동 (PhaseActivity)
  레벨 3 = 태스크
  레벨 4 = 서브태스크 (리프 노드, 진척 직접 입력)
"""
from __future__ import annotations

import logging
from datetime import date
from typing import Any, Dict, List, Optional

from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession

from core.auth import get_current_user
from database import get_db
from models import (
    SiProject, WbsItem, WbsItemCreate, WbsItemOut, WbsItemUpdate,
    WbsStatus, PHASE_DEFAULT_ACTIVITIES, ProjectPhase, PhaseActivity,
    User, UserRole,
)

logger = logging.getLogger(__name__)
router = APIRouter(prefix="/api/si/projects", tags=["si-wbs"])


# ── WBS 트리 조회 ─────────────────────────────────────────────────────────────

@router.get("/{project_id}/wbs")
async def get_wbs_tree(
    project_id: int,
    flat: bool = Query(False, description="True면 평면 리스트, False면 트리 구조"),
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    await _assert_project(project_id, db)
    items = (await db.execute(
        select(WbsItem).where(WbsItem.project_id == project_id)
        .order_by(WbsItem.wbs_code)
    )).scalars().all()

    if flat:
        return [WbsItemOut.model_validate(i) for i in items]

    return _build_tree(items)


@router.get("/{project_id}/wbs/gantt")
async def get_gantt_data(
    project_id: int,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    """간트 차트용 JSON (Frappe Gantt / dhtmlxGantt 호환)."""
    await _assert_project(project_id, db)
    items = (await db.execute(
        select(WbsItem).where(WbsItem.project_id == project_id)
        .order_by(WbsItem.wbs_code)
    )).scalars().all()

    tasks = []
    for item in items:
        tasks.append({
            "id":           str(item.id),
            "name":         f"{item.wbs_code} {item.title}",
            "start":        item.planned_start.isoformat() if item.planned_start else None,
            "end":          item.planned_end.isoformat()   if item.planned_end   else None,
            "actual_start": item.actual_start.isoformat()  if item.actual_start  else None,
            "actual_end":   item.actual_end.isoformat()    if item.actual_end    else None,
            "progress":     item.completion_pct,
            "status":       item.status,
            "assignee":     item.assignee,
            "dependencies": str(item.parent_id) if item.parent_id else "",
            "is_leaf":      item.is_leaf,
        })

    return {"project_id": project_id, "tasks": tasks}


# ── 단일 항목 ─────────────────────────────────────────────────────────────────

@router.get("/{project_id}/wbs/{item_id}", response_model=WbsItemOut)
async def get_wbs_item(
    project_id: int, item_id: int,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    return await _get_item_or_404(project_id, item_id, db)


# ── 생성 ──────────────────────────────────────────────────────────────────────

@router.post("/{project_id}/wbs", response_model=WbsItemOut, status_code=201)
async def create_wbs_item(
    project_id: int,
    body: WbsItemCreate,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    if current_user.role not in (UserRole.ADMIN, UserRole.PM, UserRole.ENGINEER):
        raise HTTPException(403, "엔지니어 이상 권한 필요")
    await _assert_project(project_id, db)

    level = 1
    if body.parent_id:
        parent = await _get_item_or_404(project_id, body.parent_id, db)
        level = parent.level + 1
        if level > 4:
            raise HTTPException(400, "WBS 최대 깊이(4레벨)를 초과합니다")
        # 부모 노드를 리프가 아닌 것으로 변경
        parent.is_leaf = False

    item = WbsItem(
        project_id    = project_id,
        parent_id     = body.parent_id,
        wbs_code      = body.wbs_code,
        title         = body.title,
        phase         = body.phase,
        activity      = body.activity,
        assignee      = body.assignee,
        planned_start = body.planned_start,
        planned_end   = body.planned_end,
        weight        = body.weight,
        deliverable   = body.deliverable,
        note          = body.note,
        level         = level,
        is_leaf       = True,
    )
    db.add(item)
    await db.commit()
    await db.refresh(item)
    return item


# ── 수정 ──────────────────────────────────────────────────────────────────────

@router.patch("/{project_id}/wbs/{item_id}", response_model=WbsItemOut)
async def update_wbs_item(
    project_id: int, item_id: int,
    body: WbsItemUpdate,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    if current_user.role not in (UserRole.ADMIN, UserRole.PM, UserRole.ENGINEER):
        raise HTTPException(403, "엔지니어 이상 권한 필요")
    item = await _get_item_or_404(project_id, item_id, db)

    data = body.model_dump(exclude_none=True)
    # 리프가 아닌 노드는 completion_pct 직접 수정 불가
    if "completion_pct" in data and not item.is_leaf:
        raise HTTPException(400, "리프 노드만 진척률을 직접 수정할 수 있습니다")

    for field, val in data.items():
        setattr(item, field, val)

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


# ── 진척률 업데이트 (부모 자동 재계산) ──────────────────────────────────────

@router.patch("/{project_id}/wbs/{item_id}/progress")
async def update_progress(
    project_id: int, item_id: int,
    pct: int = Query(..., ge=0, le=100, description="완료율 0-100"),
    actual_start: Optional[date] = Query(None),
    actual_end:   Optional[date] = Query(None),
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    """리프 노드 진척 입력 → 상위 노드 가중 평균 자동 계산."""
    item = await _get_item_or_404(project_id, item_id, db)
    if not item.is_leaf:
        raise HTTPException(400, "리프 노드만 진척률을 직접 입력할 수 있습니다")

    item.completion_pct = pct
    if actual_start:
        item.actual_start = actual_start
    if actual_end:
        item.actual_end = actual_end

    # 자동 상태 설정
    if pct == 0:
        item.status = WbsStatus.NOT_STARTED
    elif pct == 100:
        item.status = WbsStatus.COMPLETED
    else:
        from datetime import date as dt_date
        if item.planned_end and item.planned_end < dt_date.today() and pct < 100:
            item.status = WbsStatus.DELAYED
        else:
            item.status = WbsStatus.IN_PROGRESS

    await db.flush()

    # 부모 체인 재계산
    await _recalc_ancestors(item, db)
    await db.commit()
    await db.refresh(item)

    return {"id": item.id, "completion_pct": item.completion_pct, "status": item.status}


# ── 삭제 ──────────────────────────────────────────────────────────────────────

@router.delete("/{project_id}/wbs/{item_id}", status_code=204)
async def delete_wbs_item(
    project_id: int, item_id: int,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    if current_user.role not in (UserRole.ADMIN, UserRole.PM):
        raise HTTPException(403, "PM 이상 권한 필요")
    item = await _get_item_or_404(project_id, item_id, db)
    await db.delete(item)
    await db.commit()


# ── RFP 요구사항 → WBS 일괄 자동 생성 ──────────────────────────────────────

@router.post("/{project_id}/wbs/bulk", status_code=201)
async def bulk_generate_wbs(
    project_id: int,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    """표준 방법론 4단계 기반 WBS 기본 골격 자동 생성."""
    if current_user.role not in (UserRole.ADMIN, UserRole.PM):
        raise HTTPException(403, "PM 이상 권한 필요")
    await _assert_project(project_id, db)

    # 이미 WBS가 있으면 중복 생성 방지
    existing = (await db.execute(
        select(WbsItem).where(WbsItem.project_id == project_id).limit(1)
    )).scalars().first()
    if existing:
        raise HTTPException(400, "이미 WBS 항목이 존재합니다. 삭제 후 재생성하세요.")

    created = []
    phase_order = [
        (ProjectPhase.ANALYSIS,       "1", "분석"),
        (ProjectPhase.DESIGN,         "2", "설계"),
        (ProjectPhase.IMPLEMENTATION, "3", "구현"),
        (ProjectPhase.DEPLOYMENT,     "4", "인도"),
    ]

    for phase_val, phase_num, phase_label in phase_order:
        # 레벨 1: 단계 노드
        phase_node = WbsItem(
            project_id = project_id,
            wbs_code   = phase_num,
            title      = phase_label,
            phase      = phase_val,
            level      = 1,
            is_leaf    = False,
        )
        db.add(phase_node)
        await db.flush()

        activities = PHASE_DEFAULT_ACTIVITIES.get(phase_val, [])
        for idx, (activity_val, activity_name) in enumerate(activities, start=1):
            # 레벨 2: 활동 노드
            act_node = WbsItem(
                project_id = project_id,
                parent_id  = phase_node.id,
                wbs_code   = f"{phase_num}.{idx}",
                title      = activity_name,
                phase      = phase_val,
                activity   = activity_val,
                level      = 2,
                is_leaf    = True,  # 기본은 리프 — 하위 태스크 추가 시 False로 변경
            )
            db.add(act_node)
            created.append(f"{phase_num}.{idx} {activity_name}")

    await db.commit()
    logger.info("WBS 자동 생성: project_id=%d count=%d", project_id, len(created))
    return {"message": "WBS 기본 골격 생성 완료", "created": len(created), "items": created}


# ── 내부 헬퍼 ─────────────────────────────────────────────────────────────────

async def _assert_project(project_id: int, db: AsyncSession) -> None:
    proj = (await db.execute(
        select(SiProject).where(SiProject.id == project_id)
    )).scalars().first()
    if not proj:
        raise HTTPException(404, f"SI 프로젝트 {project_id}를 찾을 수 없습니다")


async def _get_item_or_404(project_id: int, item_id: int, db: AsyncSession) -> WbsItem:
    item = (await db.execute(
        select(WbsItem).where(WbsItem.id == item_id, WbsItem.project_id == project_id)
    )).scalars().first()
    if not item:
        raise HTTPException(404, "WBS 항목을 찾을 수 없습니다")
    return item


async def _recalc_ancestors(item: WbsItem, db: AsyncSession) -> None:
    """리프부터 루트까지 가중 평균 진척률 재계산."""
    current_id = item.parent_id
    while current_id:
        parent = (await db.execute(
            select(WbsItem).where(WbsItem.id == current_id)
        )).scalars().first()
        if not parent:
            break

        siblings = (await db.execute(
            select(WbsItem).where(WbsItem.parent_id == parent.id)
        )).scalars().all()

        if siblings:
            total_weight = sum(s.weight for s in siblings)
            weighted_pct = sum(s.completion_pct * s.weight for s in siblings)
            parent.completion_pct = weighted_pct // total_weight if total_weight else 0

            # 부모 상태 자동 갱신
            from datetime import date as dt_date
            all_done = all(s.completion_pct == 100 for s in siblings)
            any_started = any(s.completion_pct > 0 for s in siblings)
            if all_done:
                parent.status = WbsStatus.COMPLETED
            elif any_started:
                if parent.planned_end and parent.planned_end < dt_date.today():
                    parent.status = WbsStatus.DELAYED
                else:
                    parent.status = WbsStatus.IN_PROGRESS
            else:
                parent.status = WbsStatus.NOT_STARTED

        current_id = parent.parent_id


def _build_tree(items: List[WbsItem]) -> List[Dict[str, Any]]:
    """리스트 → 계층 트리 변환."""
    id_map: Dict[int, Dict] = {}
    for item in items:
        node = WbsItemOut.model_validate(item).model_dump()
        node["children"] = []
        id_map[item.id] = node

    roots = []
    for item in items:
        node = id_map[item.id]
        if item.parent_id and item.parent_id in id_map:
            id_map[item.parent_id]["children"].append(node)
        else:
            roots.append(node)

    return roots
