"""Claude Code 실행기를 사용하는 Z.ai GLM 작업자."""
import os
import re
import shlex
from collections.abc import Mapping
from dataclasses import replace
from pathlib import Path
from typing import Any

from okstra_ctl.adapters.providers.claude.adapter import ClaudeExecution
from okstra_ctl.domain.provider import ModelSpec, ProviderSpec, ServedModelAttestation
from okstra_ctl.domain.role import ROLE_DEFINITIONS
from okstra_ctl.domain.worker_exec import ExecCommand, PolicySupport, WorkerExecRequest
from okstra_ctl.domain.worker_presentation import JsonEvents
from okstra_ctl.domain.worker_stream import content_block_events

ZAI_MODELS = {
    # 입력·캐시 입력·출력 USD/100만 토큰, 2026-09-14 공식 단가 확인.
    # https://docs.z.ai/guides/overview/pricing
    name: ModelSpec(name, label, name, pricing=pricing)
    for name, label, pricing in (
        ("glm-5.3", "GLM-5.3", (1.40, 0.26, 4.40)),
        ("glm-5.3-flash", "GLM-5.3 Flash", (0.15, 0.03, 0.50)),
    )
}


def normalise_served_model(raw_model: str | None) -> ServedModelAttestation:
    if not raw_model or not raw_model.strip():
        return ServedModelAttestation.unknown()
    return ServedModelAttestation(
        raw_model, f"zai/{raw_model.strip().lower()}", "exact", "provider-output"
    )


def observe_served_model(event: Mapping[str, Any]) -> str | None:
    # init.model 은 실행기가 요청한 값이다. 응답 message.model 만 관측으로 인정한다.
    message = event.get("message")
    model = message.get("model") if isinstance(message, Mapping) else None
    return model if isinstance(model, str) and model.strip() else None


def observe_usage(event: Mapping[str, Any]) -> Mapping[str, Any] | None:
    # assistant.usage 는 턴별 값이므로 최종 누적 result.usage 와 섞어 더하지 않는다.
    usage = event.get("usage") if event.get("type") == "result" else None
    return usage if isinstance(usage, Mapping) and usage else None


def _read_zai_api_key() -> str:
    """별도 창에서도 동일한 홈 파일을 읽으며 다른 항목은 환경에 넣지 않는다."""
    api_key = os.environ.get("ZAI_API_KEY", "").strip()
    if api_key:
        return api_key
    try:
        lines = (Path.home() / ".env").read_text(encoding="utf-8-sig").splitlines()
    except FileNotFoundError:
        # expected-miss: 홈 파일 없이 환경 변수만 사용하는 설치도 지원한다.
        lines = []
    except (OSError, UnicodeError):
        raise OSError("Cannot read ZAI_API_KEY from ~/.env") from None
    for line in lines:
        match = re.fullmatch(r"\s*(?:export\s+)?ZAI_API_KEY\s*=\s*(.*)", line)
        if match is None:
            continue
        try:
            values = shlex.split(match.group(1), comments=True, posix=True)
        except ValueError:
            raise OSError(
                "Invalid ZAI_API_KEY in ~/.env: use a single-line value"
            ) from None
        if len(values) > 1:
            raise OSError("Invalid ZAI_API_KEY in ~/.env: use a single-line value")
        api_key = values[0].strip() if values else ""
    if not api_key:
        raise OSError("ZAI_API_KEY is required in the process environment or ~/.env")
    return api_key


class ZaiExecution:
    def build_command(self, request: WorkerExecRequest) -> ExecCommand:
        api_key = _read_zai_api_key()
        command = ClaudeExecution().build_command(request)
        return replace(
            command,
            # 설정 파일의 env 가 자식 환경을 다시 덮어쓰지 않게 한다.
            argv=(*command.argv, "--setting-sources", ""),
            environment={
                "ANTHROPIC_BASE_URL": "https://api.z.ai/api/anthropic",
                "ANTHROPIC_AUTH_TOKEN": api_key,
                "ANTHROPIC_API_KEY": None,
                "CLAUDE_CODE_OAUTH_TOKEN": None,
                "CLAUDE_CODE_USE_BEDROCK": None,
                "CLAUDE_CODE_USE_VERTEX": None,
                "CLAUDE_CODE_USE_FOUNDRY": None,
                "CLAUDECODE": None,
                "ANTHROPIC_MODEL": request.model,
                "ANTHROPIC_DEFAULT_OPUS_MODEL": request.model,
                "ANTHROPIC_DEFAULT_SONNET_MODEL": request.model,
                "ANTHROPIC_DEFAULT_HAIKU_MODEL": request.model,
                "CLAUDE_CODE_SUBAGENT_MODEL": request.model,
                "CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": "1",
            },
            presentation=JsonEvents(
                normalise=content_block_events,
                observe=observe_served_model,
                observe_usage=observe_usage,
            ),
        )

    def policy_support(self) -> PolicySupport:
        return ClaudeExecution().policy_support()


def create_provider() -> ProviderSpec:
    roles = frozenset(role.id for role in ROLE_DEFINITIONS if role.id != "leader")
    return ProviderSpec(
        provider="zai",
        display_label="Z.ai GLM",
        models=ZAI_MODELS,
        default_models={role: "glm-5.3" for role in roles},
        wrapper="okstra-zai-exec.sh",
        supported_roles=roles,
        execution_capabilities=frozenset({
            "worker-artifact-io", "source-readonly",
            "extended-artifact-authoring", "project-mutation",
        }),
        exec_strategy=ZaiExecution(),
        served_model_normalizer=normalise_served_model,
    )
