export class GlmClient {
  constructor({ baseUrl, apiKey, model = 'glm-4.6', timeoutMs = 300000 } = {}) {
    if (!baseUrl) throw new Error('GlmClient: baseUrl обязателен');
    if (!apiKey) throw new Error('GlmClient: apiKey обязателен');
    this.baseUrl = baseUrl.replace(/\/+$/, '');
    this.apiKey = apiKey;
    this.model = model;
    this.timeoutMs = timeoutMs;
  }

  async chat({ messages, tools, onDelta, onReasoning, signal } = {}) {
    const body = {
      model: this.model,
      messages,
      stream: true,
      stream_options: { include_usage: true },
    };
    if (tools?.length) {
      body.tools = tools;
      body.tool_choice = 'auto';
    }

    const res = await fetch(`${this.baseUrl}/chat/completions`, {
      method: 'POST',
      headers: {
        'content-type': 'application/json',
        authorization: `Bearer ${this.apiKey}`,
      },
      body: JSON.stringify(body),
      signal: signal ?? AbortSignal.timeout(this.timeoutMs),
    });

    if (!res.ok) {
      const text = await res.text().catch(() => '');
      let hint = text.slice(0, 500);
      try {
        const parsed = JSON.parse(text);
        hint = parsed.error?.message ?? parsed.message ?? hint;
      } catch {}
      const err = new Error(`API ${res.status}: ${hint}`);
      err.status = res.status;
      throw err;
    }

    let content = '';
    let reasoning = '';
    const toolCalls = new Map();
    let usage = null;
    let finishReason = null;

    const reader = res.body.getReader();
    const decoder = new TextDecoder();
    let buffer = '';

    while (true) {
      const { done, value } = await reader.read();
      if (done) break;
      buffer += decoder.decode(value, { stream: true });
      let newlineIdx;
      while ((newlineIdx = buffer.indexOf('\n')) >= 0) {
        const line = buffer.slice(0, newlineIdx).trim();
        buffer = buffer.slice(newlineIdx + 1);
        if (!line.startsWith('data:')) continue;
        const data = line.slice(5).trim();
        if (!data || data === '[DONE]') continue;

        let evt;
        try {
          evt = JSON.parse(data);
        } catch {
          continue;
        }
        if (evt.usage) usage = evt.usage;

        const choice = evt.choices?.[0];
        if (!choice) continue;
        if (choice.finish_reason) finishReason = choice.finish_reason;

        const delta = choice.delta ?? {};
        if (delta.reasoning_content) {
          reasoning += delta.reasoning_content;
          if (onReasoning) onReasoning(delta.reasoning_content, reasoning);
        }
        if (delta.content) {
          content += delta.content;
          if (onDelta) onDelta(delta.content, content);
        }
        for (const tc of delta.tool_calls ?? []) {
          const i = tc.index ?? 0;
          const acc = toolCalls.get(i) ?? {
            id: '',
            type: 'function',
            function: { name: '', arguments: '' },
          };
          if (tc.id) acc.id = tc.id;
          if (tc.function?.name) acc.function.name += tc.function.name;
          if (tc.function?.arguments) acc.function.arguments += tc.function.arguments;
          toolCalls.set(i, acc);
        }
      }
    }

    return {
      content,
      reasoning,
      toolCalls: [...toolCalls.values()].filter((c) => c.function.name),
      usage,
      finishReason,
    };
  }
}
