"""작업 타임테이블 CRUD + Excel 다운로드 엔드포인트."""
import io
from datetime import datetime, date
from typing import List, Optional

from fastapi import APIRouter, Depends, HTTPException, Query
from fastapi.responses import StreamingResponse
from sqlalchemy import select, and_, or_
from sqlalchemy.ext.asyncio import AsyncSession

from core.auth import get_current_user
from database import get_db
from models import (
    Institution, Server, ShellScript,
    WorkTimetable, WorkTimetableCreate, WorkTimetableOut, WorkTimetableUpdate,
    User, UserRole,
)

router = APIRouter(prefix="/api/timetable", tags=["timetable"])

WORK_TYPE_KO = {
    "REGULAR_CHECK": "정기점검",
    "PM":            "예방정비",
    "SR":            "SR작업",
    "ADHOC":         "수시점검",
    "DEPLOY":        "배포작업",
    "EMERGENCY":     "긴급대응",
}

RESULT_STATUS_KO = {
    "PENDING":   "예정",
    "SUCCESS":   "완료",
    "FAILED":    "실패",
    "PARTIAL":   "부분완료",
    "CANCELLED": "취소",
}


async def _build_query(
    db: AsyncSession,
    current_user: User,
    inst_id: Optional[int],
    work_type: Optional[str],
    result_status: Optional[str],
    assignee: Optional[str],
    date_from: Optional[date],
    date_to: Optional[date],
    keyword: Optional[str],
):
    q = select(WorkTimetable)

    # CUSTOMER 역할: 자기 기관만
    if current_user.role == UserRole.CUSTOMER and current_user.inst_code:
        r = await db.execute(
            select(Institution).where(Institution.inst_code == current_user.inst_code)
        )
        own_inst = r.scalars().first()
        if own_inst:
            q = q.where(WorkTimetable.inst_id == own_inst.id)
        else:
            q = q.where(WorkTimetable.id == -1)
    elif inst_id:
        q = q.where(WorkTimetable.inst_id == inst_id)

    if work_type:
        q = q.where(WorkTimetable.work_type == work_type)
    if result_status:
        q = q.where(WorkTimetable.result_status == result_status)
    if assignee:
        q = q.where(WorkTimetable.assignee.contains(assignee))
    if date_from:
        q = q.where(WorkTimetable.scheduled_at >= datetime.combine(date_from, datetime.min.time()))
    if date_to:
        q = q.where(WorkTimetable.scheduled_at <= datetime.combine(date_to, datetime.max.time()))
    if keyword:
        q = q.where(
            or_(
                WorkTimetable.title.contains(keyword),
                WorkTimetable.content.contains(keyword),
            )
        )
    return q


@router.get("", response_model=List[WorkTimetableOut])
async def list_timetable(
    inst_id: Optional[int] = Query(None),
    work_type: Optional[str] = Query(None),
    result_status: Optional[str] = Query(None),
    assignee: Optional[str] = Query(None),
    date_from: Optional[date] = Query(None),
    date_to: Optional[date] = Query(None),
    keyword: Optional[str] = Query(None),
    skip: int = 0,
    limit: int = 100,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    q = await _build_query(
        db, current_user, inst_id, work_type, result_status,
        assignee, date_from, date_to, keyword,
    )
    q = q.order_by(WorkTimetable.scheduled_at.desc()).offset(skip).limit(limit)
    result = await db.execute(q)
    return result.scalars().all()


@router.get("/stats")
async def timetable_stats(
    inst_id: Optional[int] = Query(None),
    date_from: Optional[date] = Query(None),
    date_to: Optional[date] = Query(None),
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    """작업 유형별·결과별 집계."""
    q = await _build_query(
        db, current_user, inst_id, None, None, None, date_from, date_to, None
    )
    result = await db.execute(q)
    rows = result.scalars().all()
    by_type = {}
    by_status = {}
    for r in rows:
        by_type[r.work_type]       = by_type.get(r.work_type, 0) + 1
        by_status[r.result_status] = by_status.get(r.result_status, 0) + 1
    return {"total": len(rows), "by_type": by_type, "by_status": by_status}


@router.get("/export/excel")
async def export_excel(
    inst_id: Optional[int] = Query(None),
    work_type: Optional[str] = Query(None),
    result_status: Optional[str] = Query(None),
    assignee: Optional[str] = Query(None),
    date_from: Optional[date] = Query(None),
    date_to: Optional[date] = Query(None),
    keyword: Optional[str] = Query(None),
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    """Excel 파일 생성 후 다운로드 (openpyxl 사용, 외부 API 없음)."""
    if current_user.role not in (UserRole.ADMIN, UserRole.PM, UserRole.ENGINEER):
        raise HTTPException(403, "권한이 없습니다.")

    try:
        from openpyxl import Workbook
        from openpyxl.styles import (
            Font, PatternFill, Alignment, Border, Side,
        )
        from openpyxl.utils import get_column_letter
    except ImportError:
        raise HTTPException(500, "openpyxl 라이브러리가 설치되지 않았습니다. pip install openpyxl")

    q = await _build_query(
        db, current_user, inst_id, work_type, result_status,
        assignee, date_from, date_to, keyword,
    )
    q = q.order_by(WorkTimetable.scheduled_at)
    res = await db.execute(q)
    rows = res.scalars().all()

    # 기관·서버 이름 매핑 (ID → 이름)
    inst_map: dict[int, str] = {}
    srv_map:  dict[int, str] = {}
    inst_ids = {r.inst_id for r in rows if r.inst_id}
    srv_ids  = {r.server_id for r in rows if r.server_id}
    if inst_ids:
        ir = await db.execute(select(Institution).where(Institution.id.in_(inst_ids)))
        for inst in ir.scalars():
            inst_map[inst.id] = inst.inst_name
    if srv_ids:
        sr = await db.execute(select(Server).where(Server.id.in_(srv_ids)))
        for srv in sr.scalars():
            srv_map[srv.id] = srv.server_name

    # ── Workbook 구성 ────────────────────────────────────────────────────────────
    wb = Workbook()

    # ── 스타일 정의 ──────────────────────────────────────────────────────────────
    hdr_fill  = PatternFill("solid", fgColor="1E3A5F")
    hdr_font  = Font(name="맑은 고딕", bold=True, color="FFFFFF", size=10)
    hdr_align = Alignment(horizontal="center", vertical="center", wrap_text=True)
    thin_side = Side(style="thin", color="CCCCCC")
    thin_bdr  = Border(left=thin_side, right=thin_side, top=thin_side, bottom=thin_side)
    even_fill = PatternFill("solid", fgColor="F2F6FA")
    data_align_center = Alignment(horizontal="center", vertical="center")
    data_align_left   = Alignment(horizontal="left",   vertical="center", wrap_text=True)

    status_fills = {
        "SUCCESS":   PatternFill("solid", fgColor="C6EFCE"),
        "FAILED":    PatternFill("solid", fgColor="FFC7CE"),
        "PARTIAL":   PatternFill("solid", fgColor="FFEB9C"),
        "PENDING":   PatternFill("solid", fgColor="F4F4F4"),
        "CANCELLED": PatternFill("solid", fgColor="E2E8F0"),
    }

    # ── 작업이력 시트 ────────────────────────────────────────────────────────────
    ws = wb.active
    ws.title = "작업이력"
    ws.freeze_panes = "A2"

    headers = [
        ("번호",        7),
        ("작업유형",    12),
        ("제목",        35),
        ("기관명",      14),
        ("대상서버",    16),
        ("처리예정",    17),
        ("시작",        17),
        ("완료",        17),
        ("소요(분)",     9),
        ("처리내용",    40),
        ("명령어/쉘",   30),
        ("처리결과",    35),
        ("결과상태",    10),
        ("담당자",      12),
        ("검토자",      12),
        ("SR번호",      20),
        ("비고",        20),
    ]
    for col_idx, (hdr, width) in enumerate(headers, 1):
        cell = ws.cell(row=1, column=col_idx, value=hdr)
        cell.font      = hdr_font
        cell.fill      = hdr_fill
        cell.alignment = hdr_align
        cell.border    = thin_bdr
        ws.column_dimensions[get_column_letter(col_idx)].width = width
    ws.row_dimensions[1].height = 30

    for i, row in enumerate(rows, start=2):
        is_even = (i % 2 == 0)
        bg_fill = even_fill if is_even else None

        def _duration_min(r):
            if r.started_at and r.completed_at:
                return int((r.completed_at - r.started_at).total_seconds() / 60)
            return ""

        def _fmt(dt):
            return dt.strftime("%Y-%m-%d %H:%M") if dt else ""

        vals = [
            i - 1,
            WORK_TYPE_KO.get(row.work_type, row.work_type),
            row.title,
            inst_map.get(row.inst_id, "") if row.inst_id else "",
            srv_map.get(row.server_id, "") if row.server_id else "",
            _fmt(row.scheduled_at),
            _fmt(row.started_at),
            _fmt(row.completed_at),
            _duration_min(row),
            row.content,
            row.command_or_shell or "",
            row.result or "",
            RESULT_STATUS_KO.get(row.result_status, row.result_status),
            row.assignee or "",
            row.reviewer or "",
            row.sr_id or "",
            row.note or "",
        ]
        center_cols = {1, 2, 4, 5, 6, 7, 8, 9, 13, 14, 15, 16}
        for col_idx, val in enumerate(vals, 1):
            cell = ws.cell(row=i, column=col_idx, value=val)
            cell.border    = thin_bdr
            cell.alignment = data_align_center if col_idx in center_cols else data_align_left
            if col_idx == 13:  # 결과상태 강조
                cell.fill = status_fills.get(row.result_status, bg_fill or PatternFill())
            elif bg_fill:
                cell.fill = bg_fill

    # ── 통계 시트 ────────────────────────────────────────────────────────────────
    ws2 = wb.create_sheet("통계요약")
    ws2.column_dimensions["A"].width = 20
    ws2.column_dimensions["B"].width = 12

    ws2.cell(1, 1, "◆ 작업 유형별 집계").font = Font(name="맑은 고딕", bold=True, size=11)
    ws2.cell(2, 1, "작업유형").fill  = hdr_fill
    ws2.cell(2, 1).font = hdr_font
    ws2.cell(2, 2, "건수").fill      = hdr_fill
    ws2.cell(2, 2).font = hdr_font
    by_type: dict[str, int] = {}
    for r in rows:
        by_type[r.work_type] = by_type.get(r.work_type, 0) + 1
    for ri, (wt, cnt) in enumerate(sorted(by_type.items()), start=3):
        ws2.cell(ri, 1, WORK_TYPE_KO.get(wt, wt))
        ws2.cell(ri, 2, cnt)

    offset = len(by_type) + 5
    ws2.cell(offset, 1, "◆ 결과 상태별 집계").font = Font(name="맑은 고딕", bold=True, size=11)
    ws2.cell(offset+1, 1, "결과상태").fill  = hdr_fill
    ws2.cell(offset+1, 1).font = hdr_font
    ws2.cell(offset+1, 2, "건수").fill      = hdr_fill
    ws2.cell(offset+1, 2).font = hdr_font
    by_status: dict[str, int] = {}
    for r in rows:
        by_status[r.result_status] = by_status.get(r.result_status, 0) + 1
    for ri, (st, cnt) in enumerate(sorted(by_status.items()), start=offset+2):
        ws2.cell(ri, 1, RESULT_STATUS_KO.get(st, st))
        ws2.cell(ri, 2, cnt)
        ws2.cell(ri, 1).fill = status_fills.get(st, PatternFill())
        ws2.cell(ri, 2).fill = status_fills.get(st, PatternFill())

    # ── 파일 이름 생성 및 반환 ────────────────────────────────────────────────────
    inst_suffix = inst_map.get(inst_id, "ALL") if inst_id else "ALL"
    today_str   = date.today().strftime("%Y%m%d")
    filename    = f"GUARDiA_작업이력_{inst_suffix}_{today_str}.xlsx"

    buf = io.BytesIO()
    wb.save(buf)
    buf.seek(0)

    from urllib.parse import quote
    encoded_name = quote(filename)
    return StreamingResponse(
        buf,
        media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
        headers={
            "Content-Disposition": f"attachment; filename*=UTF-8''{encoded_name}",
        },
    )


@router.get("/{timetable_id}", response_model=WorkTimetableOut)
async def get_timetable(
    timetable_id: int,
    db: AsyncSession = Depends(get_db),
    _u: User = Depends(get_current_user),
):
    r = await db.execute(select(WorkTimetable).where(WorkTimetable.id == timetable_id))
    row = r.scalars().first()
    if not row:
        raise HTTPException(404, "타임테이블 항목을 찾을 수 없습니다.")
    return row


@router.post("", response_model=WorkTimetableOut, status_code=201)
async def create_timetable(
    payload: WorkTimetableCreate,
    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, "권한이 없습니다.")
    row = WorkTimetable(**payload.model_dump(), created_by=current_user.username)
    db.add(row)
    # 스크립트 사용 횟수 증가
    if payload.script_id:
        sc = (await db.execute(
            select(ShellScript).where(ShellScript.id == payload.script_id)
        )).scalars().first()
        if sc:
            sc.use_count = (sc.use_count or 0) + 1
    await db.commit()
    await db.refresh(row)
    return row


@router.patch("/{timetable_id}", response_model=WorkTimetableOut)
async def update_timetable(
    timetable_id: int,
    payload: WorkTimetableUpdate,
    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, "권한이 없습니다.")
    r = await db.execute(select(WorkTimetable).where(WorkTimetable.id == timetable_id))
    row = r.scalars().first()
    if not row:
        raise HTTPException(404, "타임테이블 항목을 찾을 수 없습니다.")
    for k, v in payload.model_dump(exclude_unset=True).items():
        setattr(row, k, v)
    row.updated_at = datetime.now()
    await db.commit()
    await db.refresh(row)
    return row


@router.delete("/{timetable_id}", status_code=204)
async def delete_timetable(
    timetable_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, "ADMIN 또는 PM 권한이 필요합니다.")
    r = await db.execute(select(WorkTimetable).where(WorkTimetable.id == timetable_id))
    row = r.scalars().first()
    if not row:
        raise HTTPException(404, "타임테이블 항목을 찾을 수 없습니다.")
    await db.delete(row)
    await db.commit()
