"""GLM MCP Server - FastMCP 기반 GLM API 래퍼."""

import os
import time

from openai import OpenAI

from mcp.server.fastmcp import FastMCP

# ---------------------------------------------------------------------------
# 설정
# ---------------------------------------------------------------------------

API_URL = "https://api.z.ai/api/coding/paas/v4"
API_KEY = os.environ.get(
    "GLM_API_KEY",
    "9bfe3868d21d4697a84787c614c4c607.TIN4HD6DKjr8QSkB",
)
TIMEOUT = 120
MAX_RETRIES = 2
RETRY_INTERVAL = 5

# ---------------------------------------------------------------------------
# 역할별 System Prompts
# ---------------------------------------------------------------------------

SYSTEM_PROMPTS: dict[str, str] = {
    "backend": (
        "당신은 시니어 백엔드 개발자입니다.\n"
        "전문 분야: Python, FastAPI, 데이터 모델링, REST API 설계, 데이터베이스, 보안.\n"
        "코딩 표준: black 포맷, isort 정렬, type hints 필수, docstring 작성.\n"
        "항상 에러 처리를 포함하고, 테스트 가능한 코드를 작성하세요."
    ),
    "frontend": (
        "당신은 시니어 프론트엔드 개발자입니다.\n"
        "전문 분야: React, TypeScript, Next.js, Tailwind CSS, 컴포넌트 설계, 상태 관리.\n"
        "코딩 표준: ESLint 준수, 접근성(a11y) 고려, 반응형 디자인.\n"
        "재사용 가능한 컴포넌트를 작성하고, 성능을 고려하세요."
    ),
    "uxui": (
        "당신은 시니어 UX/UI 설계자입니다.\n"
        "전문 분야: 와이어프레임, 정보 아키텍처, 사용성 테스트, 디자인 시스템, 접근성.\n"
        "사용자 중심 설계 원칙을 따르고, 명확한 설계 문서를 작성하세요.\n"
        "시각적 일관성과 사용 편의성을 최우선으로 고려하세요."
    ),
    "tester": (
        "당신은 시니어 QA 엔지니어입니다.\n"
        "전문 분야: pytest, 단위 테스트, 통합 테스트, 엣지 케이스 분석, 테스트 커버리지.\n"
        "코딩 표준: 테스트 함수명은 test_로 시작, arrange-act-assert 패턴, fixture 활용.\n"
        "버그 리포트는 재현 단계, 예상 결과, 실제 결과를 명시하세요."
    ),
}

# ---------------------------------------------------------------------------
# OpenAI 클라이언트 초기화
# ---------------------------------------------------------------------------

client = OpenAI(
    api_key=API_KEY,
    base_url=API_URL,
    timeout=TIMEOUT,
)

# ---------------------------------------------------------------------------
# MCP 서버 초기화
# ---------------------------------------------------------------------------

mcp = FastMCP("glm-mcp")

# ---------------------------------------------------------------------------
# 내부 헬퍼
# ---------------------------------------------------------------------------


def _call_api(
    messages: list[dict[str, str]],
    model: str,
    max_tokens: int,
) -> str:
    """GLM API를 호출하고 응답 텍스트를 반환합니다.

    재시도 로직을 포함하며, 최대 MAX_RETRIES 회 재시도합니다.

    Args:
        messages: OpenAI 형식의 메시지 리스트.
        model: 사용할 모델 이름.
        max_tokens: 최대 생성 토큰 수.

    Returns:
        모델이 생성한 텍스트.

    Raises:
        RuntimeError: 모든 재시도 실패 시.
    """
    last_error: Exception | None = None

    for attempt in range(MAX_RETRIES + 1):
        try:
            response = client.chat.completions.create(
                model=model,
                messages=messages,  # type: ignore[arg-type]
                max_tokens=max_tokens,
            )
            content = response.choices[0].message.content
            return content if content is not None else ""
        except Exception as exc:  # noqa: BLE001
            last_error = exc
            if attempt < MAX_RETRIES:
                time.sleep(RETRY_INTERVAL)

    raise RuntimeError(f"API 호출 실패 ({MAX_RETRIES + 1}회 시도): {last_error}")


# ---------------------------------------------------------------------------
# Tool 1: glm_generate
# ---------------------------------------------------------------------------


@mcp.tool()
def glm_generate(
    prompt: str,
    model: str = "glm-4.7-flash",
    system_prompt: str = "",
    max_tokens: int = 4096,
) -> str:
    """GLM 모델로 텍스트를 생성합니다.

    Args:
        prompt: 생성에 사용할 사용자 프롬프트.
        model: 사용할 GLM 모델 이름.
        system_prompt: 시스템 프롬프트 (선택).
        max_tokens: 최대 생성 토큰 수.

    Returns:
        모델이 생성한 텍스트.
    """
    messages: list[dict[str, str]] = []

    if system_prompt:
        messages.append({"role": "system", "content": system_prompt})

    messages.append({"role": "user", "content": prompt})

    try:
        return _call_api(messages=messages, model=model, max_tokens=max_tokens)
    except RuntimeError as exc:
        return f"[오류] {exc}"


# ---------------------------------------------------------------------------
# Tool 2: glm_code
# ---------------------------------------------------------------------------


@mcp.tool()
def glm_code(
    task: str,
    role: str = "backend",
    model: str = "glm-5",
    review_cycles: int = 1,
) -> str:
    """코드를 작성하고 자체 리뷰 사이클을 수행합니다.

    Args:
        task: 구현할 코드 작업 설명.
        role: 역할 (backend / frontend / uxui / tester).
        model: 사용할 GLM 모델 이름.
        review_cycles: 리뷰 반복 횟수 (0~3).

    Returns:
        최종 코드 또는 에러 메시지.
    """
    review_cycles = max(0, min(3, review_cycles))

    system_prompt = SYSTEM_PROMPTS.get(role, SYSTEM_PROMPTS["backend"])

    # 1단계: 초기 코드 생성
    messages: list[dict[str, str]] = [
        {"role": "system", "content": system_prompt},
        {"role": "user", "content": task},
    ]

    try:
        code = _call_api(messages=messages, model=model, max_tokens=4096)
    except RuntimeError as exc:
        return f"[오류] 코드 생성 실패: {exc}"

    # 2단계: 리뷰 사이클
    for cycle in range(review_cycles):
        review_messages: list[dict[str, str]] = [
            {
                "role": "system",
                "content": (
                    "당신은 엄격한 시니어 코드 리뷰어입니다. "
                    "아래 코드를 검토하고, 버그·보안 취약점·코딩 표준 위반·개선 사항을 "
                    "구체적으로 지적하세요. 피드백만 작성하고 수정된 코드는 작성하지 마세요."
                ),
            },
            {
                "role": "user",
                "content": (f"다음 코드를 리뷰해 주세요 (리뷰 사이클 {cycle + 1}/{review_cycles}):\n\n" f"{code}"),
            },
        ]

        try:
            review_feedback = _call_api(messages=review_messages, model=model, max_tokens=2048)
        except RuntimeError as exc:
            return f"[오류] 리뷰 사이클 {cycle + 1} 실패: {exc}\n\n현재까지의 코드:\n{code}"

        # 리뷰 피드백을 반영하여 코드 수정
        fix_messages: list[dict[str, str]] = [
            {"role": "system", "content": system_prompt},
            {"role": "user", "content": task},
            {"role": "assistant", "content": code},
            {
                "role": "user",
                "content": (f"아래 리뷰 피드백을 반영하여 코드를 수정해 주세요:\n\n{review_feedback}"),
            },
        ]

        try:
            code = _call_api(messages=fix_messages, model=model, max_tokens=4096)
        except RuntimeError as exc:
            return f"[오류] 코드 수정 (사이클 {cycle + 1}) 실패: {exc}\n\n" f"리뷰 피드백:\n{review_feedback}"

    return code


# ---------------------------------------------------------------------------
# Tool 3: glm_backend
# ---------------------------------------------------------------------------


@mcp.tool()
def glm_backend(task: str) -> str:
    """백엔드 개발자 관점에서 코드/답변을 생성합니다.

    Args:
        task: 백엔드 작업 설명.

    Returns:
        생성된 텍스트 또는 에러 메시지.
    """
    return glm_generate(
        prompt=task,
        model="glm-5",
        system_prompt=SYSTEM_PROMPTS["backend"],
    )


# ---------------------------------------------------------------------------
# Tool 4: glm_frontend
# ---------------------------------------------------------------------------


@mcp.tool()
def glm_frontend(task: str) -> str:
    """프론트엔드 개발자 관점에서 코드/답변을 생성합니다.

    Args:
        task: 프론트엔드 작업 설명.

    Returns:
        생성된 텍스트 또는 에러 메시지.
    """
    return glm_generate(
        prompt=task,
        model="glm-5",
        system_prompt=SYSTEM_PROMPTS["frontend"],
    )


# ---------------------------------------------------------------------------
# Tool 5: glm_uxui
# ---------------------------------------------------------------------------


@mcp.tool()
def glm_uxui(task: str) -> str:
    """UX/UI 설계자 관점에서 설계 문서/답변을 생성합니다.

    Args:
        task: UX/UI 작업 설명.

    Returns:
        생성된 텍스트 또는 에러 메시지.
    """
    return glm_generate(
        prompt=task,
        model="glm-5",
        system_prompt=SYSTEM_PROMPTS["uxui"],
    )


# ---------------------------------------------------------------------------
# Tool 6: glm_tester
# ---------------------------------------------------------------------------


@mcp.tool()
def glm_tester(task: str) -> str:
    """QA 엔지니어 관점에서 테스트 코드/버그 리포트를 생성합니다.

    Args:
        task: 테스트 작업 설명.

    Returns:
        생성된 텍스트 또는 에러 메시지.
    """
    return glm_generate(
        prompt=task,
        model="glm-5",
        system_prompt=SYSTEM_PROMPTS["tester"],
    )


# ---------------------------------------------------------------------------
# 서버 실행
# ---------------------------------------------------------------------------

if __name__ == "__main__":
    mcp.run(transport="stdio")
