"""Zero Trust Network Access — 정책 엔진 + 디바이스 상태 검증"""
from __future__ import annotations
import json, logging
from datetime import datetime
from typing import Optional
from fastapi import APIRouter, Depends, HTTPException
from pydantic import BaseModel
from sqlalchemy import select, desc
from sqlalchemy.ext.asyncio import AsyncSession
from core.auth import get_current_user, require_admin_role
from database import get_db
from models import User, ZTNAPolicy, ZTNADevice, ZTNAViolation

logger = logging.getLogger(__name__)
router = APIRouter(prefix="/api/ztna", tags=["Zero Trust"])


class PolicyCreate(BaseModel):
    name: str; resource: str; allowed_roles: list = []
    require_mfa: bool = True; min_trust_score: int = 70
    allowed_ips: list = []; require_device_compliant: bool = True
    is_active: bool = True


class DeviceRegister(BaseModel):
    device_name: str; device_type: str  # PC|MOBILE|SERVER
    os_name: str; os_version: str
    antivirus_active: bool = False; disk_encrypted: bool = False
    last_patch_days: int = 999  # 마지막 패치 경과일


class VerifyRequest(BaseModel):
    user_id: int; device_id: Optional[int] = None
    resource: str; source_ip: Optional[str] = None
    mfa_verified: bool = False


def _calc_trust_score(device: Optional[ZTNADevice], mfa: bool, source_ip: str) -> int:
    score = 50
    if mfa:
        score += 20
    if device:
        if device.antivirus_active:
            score += 10
        if device.disk_encrypted:
            score += 10
        if device.last_patch_days <= 30:
            score += 10
        elif device.last_patch_days > 90:
            score -= 15
    return max(0, min(100, score))


@router.get("/policies")
async def list_policies(db: AsyncSession = Depends(get_db), user: User = Depends(get_current_user)):
    rows = await db.execute(select(ZTNAPolicy).order_by(ZTNAPolicy.name))
    return [{"id":p.id,"name":p.name,"resource":p.resource,"min_trust_score":p.min_trust_score,
             "require_mfa":p.require_mfa,"is_active":p.is_active}
            for p in rows.scalars().all()]


@router.post("/policies", status_code=201)
async def create_policy(body: PolicyCreate, db: AsyncSession = Depends(get_db),
                        user: User = Depends(require_admin_role)):
    data = body.model_dump()
    data['allowed_roles'] = json.dumps(data.get('allowed_roles', []))
    data['allowed_ips'] = json.dumps(data.get('allowed_ips', []))
    p = ZTNAPolicy(**data, created_by=user.id, created_at=datetime.utcnow())
    db.add(p); await db.commit(); await db.refresh(p)
    return {"id": p.id}


@router.put("/policies/{pid}")
async def update_policy(pid: int, body: PolicyCreate, db: AsyncSession = Depends(get_db),
                        user: User = Depends(require_admin_role)):
    row = await db.execute(select(ZTNAPolicy).where(ZTNAPolicy.id == pid))
    p = row.scalar_one_or_none()
    if not p: raise HTTPException(404)
    for k, v in body.model_dump().items(): setattr(p, k, v)
    await db.commit(); return {"ok": True}


@router.post("/verify")
async def verify_access(body: VerifyRequest, db: AsyncSession = Depends(get_db),
                        user: User = Depends(get_current_user)):
    """접속 요청 검증 — Zero Trust 결정 엔진."""
    # 해당 리소스의 정책 조회
    row = await db.execute(
        select(ZTNAPolicy).where(ZTNAPolicy.resource == body.resource, ZTNAPolicy.is_active == True)
    )
    policy = row.scalar_one_or_none()
    if not policy:
        return {"allowed": True, "reason": "정책 없음 — 기본 허용", "trust_score": 50}

    device = None
    if body.device_id:
        dr = await db.execute(select(ZTNADevice).where(ZTNADevice.id == body.device_id))
        device = dr.scalar_one_or_none()

    trust_score = _calc_trust_score(device, body.mfa_verified, body.source_ip or "")
    reasons = []

    if policy.require_mfa and not body.mfa_verified:
        reasons.append("MFA 미인증")
    if trust_score < policy.min_trust_score:
        reasons.append(f"신뢰 점수 부족 ({trust_score}/{policy.min_trust_score})")
    if policy.require_device_compliant and not device:
        reasons.append("등록된 디바이스 없음")

    allowed = len(reasons) == 0

    if not allowed:
        try:
            db.add(ZTNAViolation(
                user_id=body.user_id, resource=body.resource,
                reason=", ".join(reasons), source_ip=body.source_ip or "",
                trust_score=trust_score, created_at=datetime.utcnow()
            ))
            await db.commit()
        except Exception:
            await db.rollback()

    return {"allowed": allowed, "trust_score": trust_score,
            "reasons": reasons, "policy": policy.name}


@router.get("/devices")
async def list_devices(db: AsyncSession = Depends(get_db), user: User = Depends(get_current_user)):
    rows = await db.execute(select(ZTNADevice).order_by(desc(ZTNADevice.registered_at)))
    return [{"id":d.id,"device_name":d.device_name,"device_type":d.device_type,
             "os_name":d.os_name,"compliant":d.is_compliant} for d in rows.scalars().all()]


@router.post("/devices/register", status_code=201)
async def register_device(body: DeviceRegister, db: AsyncSession = Depends(get_db),
                          user: User = Depends(get_current_user)):
    is_compliant = (body.antivirus_active and body.disk_encrypted and body.last_patch_days <= 90)
    d = ZTNADevice(
        **body.model_dump(), is_compliant=is_compliant,
        registered_by=user.id, registered_at=datetime.utcnow()
    )
    db.add(d); await db.commit(); await db.refresh(d)
    return {"id": d.id, "is_compliant": is_compliant}


@router.get("/violations")
async def list_violations(limit: int = 50, db: AsyncSession = Depends(get_db),
                          user: User = Depends(require_admin_role)):
    rows = await db.execute(select(ZTNAViolation).order_by(desc(ZTNAViolation.created_at)).limit(limit))
    return [{"id":v.id,"user_id":v.user_id,"resource":v.resource,"reason":v.reason,
             "trust_score":v.trust_score,"created_at":v.created_at}
            for v in rows.scalars().all()]


@router.get("/segments")
async def list_segments(db: AsyncSession = Depends(get_db), user: User = Depends(get_current_user)):
    """마이크로세그멘테이션 구성 (정책 기반)."""
    rows = await db.execute(select(ZTNAPolicy).where(ZTNAPolicy.is_active == True))
    policies = rows.scalars().all()
    return [{"segment": p.resource, "min_trust": p.min_trust_score,
             "require_mfa": p.require_mfa, "require_device": p.require_device_compliant}
            for p in policies]
