"""기관(사이트) + 담당자 등록·관리 엔드포인트."""
from datetime import datetime
from typing import List, Optional

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

from core.auth import get_current_user
from database import get_db
from middleware.license_guard import check_institution_limit
from models import (
    Institution, InstitutionCreate, InstitutionOut, InstitutionUpdate,
    InstContact, InstContactCreate, InstContactOut, InstContactUpdate,
    Server, User, UserRole,
)

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


def _require_admin_or_pm(user: User):
    if user.role not in (UserRole.ADMIN, UserRole.PM):
        raise HTTPException(403, "ADMIN 또는 PM 권한이 필요합니다.")


# ── 기관 CRUD ──────────────────────────────────────────────────────────────────

@router.get("", response_model=List[InstitutionOut])
async def list_institutions(
    keyword: Optional[str] = Query(None),
    region: Optional[str] = Query(None),
    is_active: Optional[bool] = Query(None),
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    q = select(Institution).order_by(Institution.inst_name)
    if keyword:
        q = q.where(Institution.inst_name.contains(keyword))
    if region:
        q = q.where(Institution.region == region)
    if is_active is not None:
        q = q.where(Institution.is_active == is_active)
    result = await db.execute(q)
    return result.scalars().all()


@router.get("/{inst_code}", response_model=InstitutionOut)
async def get_institution(
    inst_code: str,
    db: AsyncSession = Depends(get_db),
    _u: User = Depends(get_current_user),
):
    r = await db.execute(select(Institution).where(Institution.inst_code == inst_code))
    inst = r.scalars().first()
    if not inst:
        raise HTTPException(404, "기관을 찾을 수 없습니다.")
    return inst


@router.post("", response_model=InstitutionOut, status_code=201)
async def create_institution(
    payload: InstitutionCreate,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
    _: None = Depends(check_institution_limit),
):
    _require_admin_or_pm(current_user)
    # 중복 코드 확인
    dup = await db.execute(
        select(Institution).where(Institution.inst_code == payload.inst_code)
    )
    if dup.scalars().first():
        raise HTTPException(409, f"기관 코드 '{payload.inst_code}'가 이미 존재합니다.")
    inst = Institution(**payload.model_dump())
    db.add(inst)
    await db.commit()
    await db.refresh(inst)
    return inst


@router.patch("/{inst_code}", response_model=InstitutionOut)
async def update_institution(
    inst_code: str,
    payload: InstitutionUpdate,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    _require_admin_or_pm(current_user)
    r = await db.execute(select(Institution).where(Institution.inst_code == inst_code))
    inst = r.scalars().first()
    if not inst:
        raise HTTPException(404, "기관을 찾을 수 없습니다.")
    for k, v in payload.model_dump(exclude_unset=True).items():
        setattr(inst, k, v)
    inst.updated_at = datetime.now()
    await db.commit()
    await db.refresh(inst)
    return inst


@router.delete("/{inst_code}", status_code=204)
async def deactivate_institution(
    inst_code: str,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    if current_user.role != UserRole.ADMIN:
        raise HTTPException(403, "ADMIN 권한이 필요합니다.")
    r = await db.execute(select(Institution).where(Institution.inst_code == inst_code))
    inst = r.scalars().first()
    if not inst:
        raise HTTPException(404, "기관을 찾을 수 없습니다.")
    inst.is_active = False
    inst.updated_at = datetime.now()
    await db.commit()


# ── 담당자 CRUD ────────────────────────────────────────────────────────────────

async def _get_inst(db: AsyncSession, inst_code: str) -> Institution:
    r = await db.execute(select(Institution).where(Institution.inst_code == inst_code))
    inst = r.scalars().first()
    if not inst:
        raise HTTPException(404, "기관을 찾을 수 없습니다.")
    return inst


@router.get("/{inst_code}/contacts", response_model=List[InstContactOut])
async def list_contacts(
    inst_code: str,
    db: AsyncSession = Depends(get_db),
    _u: User = Depends(get_current_user),
):
    inst = await _get_inst(db, inst_code)
    result = await db.execute(
        select(InstContact)
        .where(InstContact.inst_id == inst.id)
        .order_by(InstContact.is_primary.desc(), InstContact.contact_name)
    )
    return result.scalars().all()


@router.post("/{inst_code}/contacts", response_model=InstContactOut, status_code=201)
async def add_contact(
    inst_code: str,
    payload: InstContactCreate,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    _require_admin_or_pm(current_user)
    inst = await _get_inst(db, inst_code)
    # 주 담당자로 설정 시 기존 주 담당자 해제
    if payload.is_primary:
        await db.execute(
            select(InstContact).where(
                InstContact.inst_id == inst.id,
                InstContact.is_primary == True
            )
        )
        prev_primary = (await db.execute(
            select(InstContact).where(
                InstContact.inst_id == inst.id,
                InstContact.is_primary == True
            )
        )).scalars().first()
        if prev_primary:
            prev_primary.is_primary = False
    contact = InstContact(inst_id=inst.id, **payload.model_dump())
    db.add(contact)
    await db.commit()
    await db.refresh(contact)
    return contact


@router.patch("/{inst_code}/contacts/{contact_id}", response_model=InstContactOut)
async def update_contact(
    inst_code: str,
    contact_id: int,
    payload: InstContactUpdate,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    _require_admin_or_pm(current_user)
    inst = await _get_inst(db, inst_code)
    r = await db.execute(
        select(InstContact).where(
            InstContact.id == contact_id,
            InstContact.inst_id == inst.id
        )
    )
    contact = r.scalars().first()
    if not contact:
        raise HTTPException(404, "담당자를 찾을 수 없습니다.")
    # 주 담당자로 변경 시 기존 주 담당자 해제
    if payload.is_primary:
        prev = (await db.execute(
            select(InstContact).where(
                InstContact.inst_id == inst.id,
                InstContact.is_primary == True,
                InstContact.id != contact_id
            )
        )).scalars().first()
        if prev:
            prev.is_primary = False
    for k, v in payload.model_dump(exclude_unset=True).items():
        setattr(contact, k, v)
    await db.commit()
    await db.refresh(contact)
    return contact


@router.delete("/{inst_code}/contacts/{contact_id}", status_code=204)
async def delete_contact(
    inst_code: str,
    contact_id: int,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    if current_user.role != UserRole.ADMIN:
        raise HTTPException(403, "ADMIN 권한이 필요합니다.")
    inst = await _get_inst(db, inst_code)
    r = await db.execute(
        select(InstContact).where(
            InstContact.id == contact_id,
            InstContact.inst_id == inst.id
        )
    )
    contact = r.scalars().first()
    if not contact:
        raise HTTPException(404, "담당자를 찾을 수 없습니다.")
    contact.is_active = False
    await db.commit()


# ── 기관 서버 목록 (기존 cmdb 대체) ───────────────────────────────────────────

@router.get("/{inst_code}/servers")
async def list_inst_servers(
    inst_code: str,
    db: AsyncSession = Depends(get_db),
    _u: User = Depends(get_current_user),
):
    from models import ServerOut
    inst = await _get_inst(db, inst_code)
    result = await db.execute(
        select(Server)
        .where(Server.inst_id == inst.id)
        .order_by(Server.server_role, Server.server_name)
    )
    servers = result.scalars().all()
    return [ServerOut.model_validate(s) for s in servers]
