from __future__ import annotations

from datetime import datetime, timedelta
from typing import Optional, Any

import httpx
from fastapi import APIRouter, Depends, HTTPException, Query
from pydantic import BaseModel
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 User, SecurityEvent, SOARPlaybook, ThreatIntel

router = APIRouter(prefix="/api/soc", tags=["AI-SOC"])


def _tenant(user: User) -> str:
    return user.inst_code or str(user.id)


async def _ollama(prompt: str) -> str:
    try:
        async with httpx.AsyncClient(timeout=30) as c:
            r = await c.post(
                "http://localhost:11434/api/generate",
                json={"model": "llama3", "prompt": prompt, "stream": False},
            )
            return r.json().get("response", "분석 결과 없음")
    except Exception:
        return "AI 분석 불가 (Ollama 연결 실패)"


# ── Pydantic 스키마 ────────────────────────────────────────────────────────────

class EventIn(BaseModel):
    event_type: str
    source_ip: Optional[str] = None
    severity: str = "MEDIUM"
    description: Optional[str] = None
    raw_log: Optional[str] = None

class EventOut(BaseModel):
    model_config = {"from_attributes": True}
    id: int
    event_type: str
    source_ip: Optional[str]
    severity: str
    description: Optional[str]
    status: str
    created_at: datetime

class ThreatIn(BaseModel):
    ioc_type: str
    ioc_value: str
    threat_type: Optional[str] = None
    confidence: float = 0.5
    source: Optional[str] = None
    expires_days: Optional[int] = None

class PlaybookIn(BaseModel):
    name: str
    trigger_condition: dict[str, Any]
    actions: list[dict[str, Any]]

class PlaybookOut(BaseModel):
    model_config = {"from_attributes": True}
    id: int
    name: str
    trigger_condition: Any
    actions: Any
    is_active: bool
    run_count: int
    created_at: datetime

class CveImpactIn(BaseModel):
    cve_id: str
    cvss_score: float
    affected_software: Optional[str] = None


# ── 엔드포인트 ─────────────────────────────────────────────────────────────────

@router.post("/events", summary="보안 이벤트 수집")
async def ingest_event(
    body: EventIn,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    tid = _tenant(current_user)
    valid_severities = {"CRITICAL", "HIGH", "MEDIUM", "LOW"}
    if body.severity not in valid_severities:
        raise HTTPException(400, f"severity는 {valid_severities} 중 하나여야 합니다")

    # 위협 인텔리전스 IP 매칭
    matched_threat = None
    if body.source_ip:
        matched = (await db.execute(
            select(ThreatIntel).where(
                ThreatIntel.tenant_id == tid,
                ThreatIntel.ioc_type == "IP",
                ThreatIntel.ioc_value == body.source_ip,
            )
        )).scalar_one_or_none()
        if matched:
            matched_threat = matched.threat_type
            body.severity = "CRITICAL"

    event = SecurityEvent(
        tenant_id=tid,
        event_type=body.event_type,
        source_ip=body.source_ip,
        severity=body.severity,
        description=body.description,
        raw_log=body.raw_log,
    )
    db.add(event)
    await db.commit()
    await db.refresh(event)
    return {
        "id": event.id,
        "severity": event.severity,
        "threat_matched": matched_threat,
        "message": "이벤트 수집 완료",
    }


@router.get("/events", summary="보안 이벤트 목록")
async def list_events(
    status: Optional[str] = None,
    severity: Optional[str] = None,
    hours: int = Query(24, ge=1, le=720),
    limit: int = Query(50, ge=1, le=500),
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    tid = _tenant(current_user)
    since = datetime.utcnow() - timedelta(hours=hours)
    q = select(SecurityEvent).where(
        SecurityEvent.tenant_id == tid,
        SecurityEvent.created_at >= since,
    )
    if status:
        q = q.where(SecurityEvent.status == status)
    if severity:
        q = q.where(SecurityEvent.severity == severity)
    q = q.order_by(SecurityEvent.created_at.desc()).limit(limit)
    rows = (await db.execute(q)).scalars().all()
    return [EventOut.model_validate(r) for r in rows]


@router.post("/correlate", summary="AI 이벤트 상관분석")
async def correlate_events(
    hours: int = Query(6, ge=1, le=72),
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    tid = _tenant(current_user)
    since = datetime.utcnow() - timedelta(hours=hours)
    events = (await db.execute(
        select(SecurityEvent).where(
            SecurityEvent.tenant_id == tid,
            SecurityEvent.created_at >= since,
        ).limit(100)
    )).scalars().all()

    if not events:
        return {"correlation_groups": [], "message": "분석할 이벤트 없음"}

    # IP 기반 규칙 상관관계
    ip_groups: dict[str, list] = {}
    for e in events:
        key = e.source_ip or "unknown"
        ip_groups.setdefault(key, []).append({"id": e.id, "type": e.event_type, "severity": e.severity})

    summary = "\n".join(
        f"- IP {ip}: {len(evs)}건 ({', '.join(set(e['type'] for e in evs))})"
        for ip, evs in ip_groups.items() if len(evs) > 1
    )
    prompt = (
        f"보안 이벤트 상관분석 ({len(events)}건, 최근 {hours}시간):\n{summary or '단일 이벤트'}\n"
        f"공격 패턴, 캠페인 연관성, 권고 조치를 분석하시오."
    )
    analysis = await _ollama(prompt)

    for e in events:
        if e.source_ip and ip_groups.get(e.source_ip, []):
            e.correlation_id = f"corr-{e.source_ip.replace('.', '-')}"
    await db.commit()

    return {
        "event_count": len(events),
        "correlation_groups": [
            {"source_ip": ip, "event_count": len(evs), "events": evs}
            for ip, evs in ip_groups.items() if len(evs) > 1
        ],
        "ai_analysis": analysis,
    }


@router.get("/threats", summary="위협 인텔리전스 피드 목록")
async def list_threats(
    active_only: bool = True,
    ioc_type: Optional[str] = None,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    tid = _tenant(current_user)
    q = select(ThreatIntel).where(ThreatIntel.tenant_id == tid)
    if active_only:
        q = q.where(
            (ThreatIntel.expires_at == None) | (ThreatIntel.expires_at > datetime.utcnow())
        )
    if ioc_type:
        q = q.where(ThreatIntel.ioc_type == ioc_type)
    rows = (await db.execute(q)).scalars().all()
    return [
        {
            "id": r.id,
            "ioc_type": r.ioc_type,
            "ioc_value": r.ioc_value,
            "threat_type": r.threat_type,
            "confidence": r.confidence,
            "source": r.source,
            "expires_at": r.expires_at,
        }
        for r in rows
    ]


@router.post("/threats", summary="위협 인텔리전스 등록")
async def create_threat(
    body: ThreatIn,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    tid = _tenant(current_user)
    expires_at = None
    if body.expires_days:
        expires_at = datetime.utcnow() + timedelta(days=body.expires_days)
    intel = ThreatIntel(
        tenant_id=tid,
        ioc_type=body.ioc_type,
        ioc_value=body.ioc_value,
        threat_type=body.threat_type,
        confidence=body.confidence,
        source=body.source,
        expires_at=expires_at,
    )
    db.add(intel)
    await db.commit()
    await db.refresh(intel)
    return {"id": intel.id, "message": "위협 인텔리전스 등록 완료"}


@router.post("/playbooks", summary="SOAR 플레이북 정의 등록")
async def create_playbook(
    body: PlaybookIn,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    tid = _tenant(current_user)
    pb = SOARPlaybook(
        tenant_id=tid,
        name=body.name,
        trigger_condition=body.trigger_condition,
        actions=body.actions,
    )
    db.add(pb)
    await db.commit()
    await db.refresh(pb)
    return PlaybookOut.model_validate(pb)


@router.get("/playbooks", summary="플레이북 목록")
async def list_playbooks(
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    tid = _tenant(current_user)
    rows = (await db.execute(
        select(SOARPlaybook).where(SOARPlaybook.tenant_id == tid, SOARPlaybook.is_active == True)
    )).scalars().all()
    return [PlaybookOut.model_validate(r) for r in rows]


@router.post("/playbooks/{playbook_id}/run", summary="플레이북 자동 실행")
async def run_playbook(
    playbook_id: int,
    event_id: Optional[int] = None,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    tid = _tenant(current_user)
    pb = (await db.execute(
        select(SOARPlaybook).where(SOARPlaybook.tenant_id == tid, SOARPlaybook.id == playbook_id)
    )).scalar_one_or_none()
    if not pb:
        raise HTTPException(404, "플레이북을 찾을 수 없습니다")

    actions_result = []
    for action in (pb.actions or []):
        action_type = action.get("type", "unknown")
        actions_result.append({
            "action": action_type,
            "status": "EXECUTED",
            "timestamp": datetime.utcnow().isoformat(),
        })

    pb.run_count = (pb.run_count or 0) + 1
    await db.commit()

    return {
        "playbook_id": playbook_id,
        "playbook_name": pb.name,
        "event_id": event_id,
        "actions_executed": len(actions_result),
        "results": actions_result,
        "run_count": pb.run_count,
    }


@router.get("/incidents/{incident_id}/timeline", summary="보안 인시던트 타임라인 자동 재구성")
async def incident_timeline(
    incident_id: str,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    tid = _tenant(current_user)
    events = (await db.execute(
        select(SecurityEvent).where(
            SecurityEvent.tenant_id == tid,
            SecurityEvent.correlation_id == incident_id,
        ).order_by(SecurityEvent.created_at.asc())
    )).scalars().all()

    if not events:
        raise HTTPException(404, "해당 인시던트 이벤트를 찾을 수 없습니다")

    timeline = [
        {
            "timestamp": e.created_at.isoformat(),
            "event_type": e.event_type,
            "source_ip": e.source_ip,
            "severity": e.severity,
            "description": e.description,
        }
        for e in events
    ]

    summary_text = "\n".join(
        f"{t['timestamp']}: {t['event_type']} ({t['severity']})" for t in timeline
    )
    prompt = f"보안 인시던트 타임라인 재구성:\n{summary_text}\n공격 흐름을 단계별로 설명하고 대응 조치를 제시하시오."
    narrative = await _ollama(prompt)

    return {
        "incident_id": incident_id,
        "event_count": len(events),
        "timeline": timeline,
        "ai_narrative": narrative,
    }


@router.post("/cve-impact", summary="CVE→자산 영향도 분석 + 패치 우선순위")
async def cve_impact(
    body: CveImpactIn,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    # CVSS 기반 패치 우선순위 결정
    cvss = body.cvss_score
    if cvss >= 9.0:
        priority = "P1"
        sla_days = 3
    elif cvss >= 7.0:
        priority = "P2"
        sla_days = 7
    elif cvss >= 4.0:
        priority = "P3"
        sla_days = 30
    else:
        priority = "P4"
        sla_days = 90

    prompt = (
        f"CVE 영향도 분석:\n"
        f"- CVE ID: {body.cve_id}\n"
        f"- CVSS 점수: {body.cvss_score}\n"
        f"- 영향 소프트웨어: {body.affected_software or '미지정'}\n"
        f"영향받는 시스템 유형, 익스플로잇 가능성, 패치 적용 방법을 간략히 설명하시오."
    )
    analysis = await _ollama(prompt)

    return {
        "cve_id": body.cve_id,
        "cvss_score": body.cvss_score,
        "patch_priority": priority,
        "sla_days": sla_days,
        "due_date": (datetime.utcnow() + timedelta(days=sla_days)).strftime("%Y-%m-%d"),
        "affected_software": body.affected_software,
        "ai_analysis": analysis,
    }


@router.get("/dashboard", summary="SOC 대시보드")
async def soc_dashboard(
    hours: int = Query(24, ge=1, le=720),
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    tid = _tenant(current_user)
    since = datetime.utcnow() - timedelta(hours=hours)
    events = (await db.execute(
        select(SecurityEvent).where(
            SecurityEvent.tenant_id == tid,
            SecurityEvent.created_at >= since,
        )
    )).scalars().all()

    by_severity: dict[str, int] = {}
    by_status: dict[str, int] = {}
    for e in events:
        by_severity[e.severity] = by_severity.get(e.severity, 0) + 1
        by_status[e.status] = by_status.get(e.status, 0) + 1

    threat_count = (await db.execute(
        select(ThreatIntel).where(ThreatIntel.tenant_id == tid)
    )).scalars().all()

    return {
        "period_hours": hours,
        "total_events": len(events),
        "by_severity": by_severity,
        "by_status": by_status,
        "active_threats": len(threat_count),
    }
