from __future__ import annotations

import time
from datetime import datetime
from typing import Optional, Any, List

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, HarnessAgent, HarnessRunHistory, HarnessSkill

router = APIRouter(prefix="/api/harness", tags=["Harness Builder"])


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


async def _ollama(prompt: str, system: str = "") -> str:
    try:
        payload: dict[str, Any] = {"model": "llama3", "prompt": prompt, "stream": False}
        if system:
            payload["system"] = system
        async with httpx.AsyncClient(timeout=30) as c:
            r = await c.post("http://localhost:11434/api/generate", json=payload)
            return r.json().get("response", "AI 응답 없음")
    except Exception:
        return "AI 연결 불가 (Ollama 오프라인)"


_BUILTIN_ORCHESTRATORS = [
    {"name": "guardia-orchestrator", "type": "orchestrator", "description": "ITSM E2E 워크플로우 오케스트레이터"},
    {"name": "guardia-brain-orchestrator", "type": "orchestrator", "description": "AI 지능화 엔진 오케스트레이터"},
    {"name": "guardia-extend5-orchestrator", "type": "orchestrator", "description": "5세대 확장 오케스트레이터"},
    {"name": "guardia-parent-orchestrator", "type": "orchestrator", "description": "부모 역할 4가지 오케스트레이터"},
    {"name": "guardia-fullstack-orchestrator", "type": "orchestrator", "description": "풀스택 통합 오케스트레이터"},
]

_SKILL_TEMPLATES = {
    "orchestrator": "# {name} 오케스트레이터 스킬\n\n## 목적\n{role}\n\n## 트리거\n{keywords}\n\n## 에이전트 목록\n- 전문 에이전트 목록을 여기에 정의\n\n## 실행 순서\n1. 요청 분석\n2. 에이전트 선택\n3. 병렬 실행\n4. 결과 통합\n",
    "specialist": "# {name} 전문 에이전트 스킬\n\n## 역할\n{role}\n\n## 입력\n- 요청 텍스트\n- 컨텍스트 데이터\n\n## 출력\n- 분석 결과\n- 권고 사항\n\n## 보안 원칙\n- JWT 인증 필수\n- 외부 API 호출 금지\n",
    "reviewer": "# {name} 리뷰어 스킬\n\n## 역할\n{role}\n\n## 체크리스트\n- [ ] 보안 검토\n- [ ] 성능 검토\n- [ ] 코드 품질 검토\n\n## 출력\n- 리뷰 결과 (통과/실패)\n- 개선 권고\n",
}


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

class AgentIn(BaseModel):
    name: str
    role: Optional[str] = None
    skills: list[str] = []
    trigger_keywords: list[str] = []
    system_prompt: Optional[str] = None
    auto_describe: bool = False

class AgentOut(BaseModel):
    model_config = {"from_attributes": True}
    id: int
    name: str
    role: Optional[str]
    skills: Optional[Any]
    trigger_keywords: Optional[Any]
    is_active: bool
    run_count: int
    last_run_at: Optional[datetime]
    created_at: datetime

class AgentUpdate(BaseModel):
    role: Optional[str] = None
    skills: Optional[list[str]] = None
    trigger_keywords: Optional[list[str]] = None
    system_prompt: Optional[str] = None
    is_active: Optional[bool] = None

class RunIn(BaseModel):
    prompt: str

class SkillIn(BaseModel):
    name: str
    skill_type: str = "specialist"
    content_md: Optional[str] = None
    auto_generate: bool = False
    role: Optional[str] = None


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

@router.get("/agents", summary="에이전트 목록 조회")
async def list_agents(
    active_only: bool = True,
    keyword: Optional[str] = None,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    tid = _tenant(current_user)
    q = select(HarnessAgent).where(HarnessAgent.tenant_id == tid)
    if active_only:
        q = q.where(HarnessAgent.is_active == True)
    rows = (await db.execute(q.order_by(HarnessAgent.created_at.desc()))).scalars().all()
    if keyword:
        keyword_lower = keyword.lower()
        rows = [r for r in rows if keyword_lower in (r.name or "").lower() or keyword_lower in (r.role or "").lower()]
    return [AgentOut.model_validate(r) for r in rows]


@router.post("/agents", status_code=201, summary="에이전트 노코드 생성")
async def create_agent(
    body: AgentIn,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    tid = _tenant(current_user)
    system_prompt = body.system_prompt
    if body.auto_describe or not system_prompt:
        prompt = (
            f"AI 에이전트 역할 설명 작성:\n"
            f"이름: {body.name}\n역할: {body.role or '범용 에이전트'}\n"
            f"스킬: {', '.join(body.skills) or '없음'}\n"
            f"위 정보를 바탕으로 이 에이전트의 시스템 프롬프트를 한국어로 3문장 이내로 작성하라."
        )
        system_prompt = await _ollama(prompt)

    agent = HarnessAgent(
        tenant_id=tid,
        name=body.name,
        role=body.role,
        skills=body.skills,
        trigger_keywords=body.trigger_keywords,
        system_prompt=system_prompt,
    )
    db.add(agent)
    await db.commit()
    await db.refresh(agent)
    return AgentOut.model_validate(agent)


@router.put("/agents/{agent_id}", summary="에이전트 수정")
async def update_agent(
    agent_id: int,
    body: AgentUpdate,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    tid = _tenant(current_user)
    agent = (await db.execute(
        select(HarnessAgent).where(HarnessAgent.tenant_id == tid, HarnessAgent.id == agent_id)
    )).scalar_one_or_none()
    if not agent:
        raise HTTPException(404, "에이전트를 찾을 수 없습니다")
    if body.role is not None:
        agent.role = body.role
    if body.skills is not None:
        agent.skills = body.skills
    if body.trigger_keywords is not None:
        agent.trigger_keywords = body.trigger_keywords
    if body.system_prompt is not None:
        agent.system_prompt = body.system_prompt
    if body.is_active is not None:
        agent.is_active = body.is_active
    await db.commit()
    await db.refresh(agent)
    return AgentOut.model_validate(agent)


@router.delete("/agents/{agent_id}", summary="에이전트 삭제")
async def delete_agent(
    agent_id: int,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    tid = _tenant(current_user)
    agent = (await db.execute(
        select(HarnessAgent).where(HarnessAgent.tenant_id == tid, HarnessAgent.id == agent_id)
    )).scalar_one_or_none()
    if not agent:
        raise HTTPException(404, "에이전트를 찾을 수 없습니다")
    agent.is_active = False
    await db.commit()
    return {"id": agent_id, "message": "에이전트가 비활성화되었습니다"}


@router.post("/agents/{agent_id}/run", summary="에이전트 즉시 실행")
async def run_agent(
    agent_id: int,
    body: RunIn,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    tid = _tenant(current_user)
    agent = (await db.execute(
        select(HarnessAgent).where(HarnessAgent.tenant_id == tid, HarnessAgent.id == agent_id)
    )).scalar_one_or_none()
    if not agent:
        raise HTTPException(404, "에이전트를 찾을 수 없습니다")
    if not agent.is_active:
        raise HTTPException(400, "비활성화된 에이전트입니다")

    # 스킬 컨텍스트 조회
    skill_context = ""
    if agent.skills:
        skills = (await db.execute(
            select(HarnessSkill).where(
                HarnessSkill.tenant_id == tid,
                HarnessSkill.name.in_(agent.skills),
            )
        )).scalars().all()
        if skills:
            skill_context = "\n".join(f"[스킬: {s.name}]\n{(s.content_md or '')[:300]}" for s in skills)

    system_parts = [agent.system_prompt or f"당신은 {agent.name} AI 에이전트입니다. 역할: {agent.role}"]
    if skill_context:
        system_parts.append(f"\n[활성 스킬]\n{skill_context[:500]}")
    full_system = "\n".join(system_parts)

    start = time.monotonic()
    result = await _ollama(body.prompt, system=full_system)
    duration_ms = int((time.monotonic() - start) * 1000)

    run = HarnessRunHistory(
        tenant_id=tid,
        agent_id=agent_id,
        prompt=body.prompt,
        result=result,
        status="SUCCESS",
        duration_ms=duration_ms,
        run_by=current_user.username,
    )
    db.add(run)
    agent.run_count = (agent.run_count or 0) + 1
    agent.last_run_at = datetime.utcnow()
    await db.commit()

    return {
        "agent_id": agent_id,
        "agent_name": agent.name,
        "prompt": body.prompt,
        "result": result,
        "duration_ms": duration_ms,
        "run_id": run.id,
    }


@router.get("/agents/{agent_id}/history", summary="에이전트 실행 이력")
async def agent_history(
    agent_id: int,
    limit: int = Query(20, ge=1, le=100),
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    tid = _tenant(current_user)
    agent = (await db.execute(
        select(HarnessAgent).where(HarnessAgent.tenant_id == tid, HarnessAgent.id == agent_id)
    )).scalar_one_or_none()
    if not agent:
        raise HTTPException(404, "에이전트를 찾을 수 없습니다")
    rows = (await db.execute(
        select(HarnessRunHistory).where(
            HarnessRunHistory.tenant_id == tid,
            HarnessRunHistory.agent_id == agent_id,
        ).order_by(HarnessRunHistory.created_at.desc()).limit(limit)
    )).scalars().all()
    return {
        "agent_id": agent_id,
        "agent_name": agent.name,
        "total_runs": agent.run_count,
        "history": [
            {"id": r.id, "prompt": (r.prompt or "")[:100], "status": r.status,
             "duration_ms": r.duration_ms, "run_by": r.run_by,
             "created_at": r.created_at.isoformat() if r.created_at else None}
            for r in rows
        ],
    }


@router.get("/skills", summary="스킬 템플릿 목록")
async def list_skills(
    skill_type: Optional[str] = None,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    tid = _tenant(current_user)
    q = select(HarnessSkill).where(HarnessSkill.tenant_id == tid, HarnessSkill.is_active == True)
    if skill_type:
        q = q.where(HarnessSkill.skill_type == skill_type)
    rows = (await db.execute(q)).scalars().all()
    return {"total": len(rows), "skills": [
        {"id": r.id, "name": r.name, "skill_type": r.skill_type,
         "created_at": r.created_at.isoformat() if r.created_at else None}
        for r in rows
    ]}


@router.post("/skills", status_code=201, summary="스킬 생성")
async def create_skill(
    body: SkillIn,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    if body.skill_type not in ("orchestrator", "specialist", "reviewer"):
        raise HTTPException(400, "skill_type은 orchestrator/specialist/reviewer 중 하나여야 합니다")
    tid = _tenant(current_user)

    content = body.content_md
    if body.auto_generate or not content:
        template = _SKILL_TEMPLATES.get(body.skill_type, "")
        prompt = (
            f"SKILL.md 파일 작성:\n이름: {body.name}\n유형: {body.skill_type}\n역할: {body.role or body.name}\n"
            f"다음 템플릿을 채워서 완성된 SKILL.md를 한국어로 작성하라:\n{template}"
        )
        content = await _ollama(prompt)

    skill = HarnessSkill(
        tenant_id=tid,
        name=body.name,
        skill_type=body.skill_type,
        content_md=content,
    )
    db.add(skill)
    await db.commit()
    await db.refresh(skill)
    return {"id": skill.id, "name": skill.name, "skill_type": skill.skill_type,
            "content_preview": (content or "")[:200]}


@router.get("/orchestrators", summary="오케스트레이터 목록 + 연결된 에이전트")
async def list_orchestrators(
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    tid = _tenant(current_user)
    db_agents = (await db.execute(
        select(HarnessAgent).where(
            HarnessAgent.tenant_id == tid,
            HarnessAgent.is_active == True,
        )
    )).scalars().all()

    result = list(_BUILTIN_ORCHESTRATORS)
    for agent in db_agents:
        if agent.skill_type if hasattr(agent, "skill_type") else "specialist" == "orchestrator":
            result.append({"name": agent.name, "type": "orchestrator",
                           "description": agent.role or "", "id": agent.id})

    return {
        "total": len(result),
        "orchestrators": result,
        "registered_agents": [
            {"id": a.id, "name": a.name, "role": a.role, "run_count": a.run_count}
            for a in db_agents
        ],
    }
