import { EventEmitter } from "./emitter";
export class ZaiContext {
  _startedAt = Date.now();
  _inputCost = 0;
  _outputCost = 0;
  _inputTokens = 0;
  _outputTokens = 0;
  _totalCachedResponses = 0;
  _totalRequests = 0;
  _totalErrors = 0;
  _totalResponses = 0;
  taskId;
  taskType;
  modelId;
  adapter;
  source;
  _eventEmitter;
  controller = new AbortController();
  _client;
  constructor(props) {
    this._client = props.client.clone();
    this.taskId = props.taskId;
    this.modelId = props.modelId;
    this.adapter = props.adapter;
    this.source = props.source;
    this.taskType = props.taskType;
    this._eventEmitter = new EventEmitter();
    this._client.on("request", () => {
      this._totalRequests++;
      this._eventEmitter.emit("update", this.usage);
    });
    this._client.on("response", (_req, res) => {
      this._totalResponses++;
      if (res.meta.cached) {
        this._totalCachedResponses++;
      } else {
        this._inputTokens += res.meta.tokens.input || 0;
        this._outputTokens += res.meta.tokens.output || 0;
        this._inputCost += res.meta.cost.input || 0;
        this._outputCost += res.meta.cost.output || 0;
      }
      this._eventEmitter.emit("update", this.usage);
    });
    this._client.on("error", () => {
      this._totalErrors++;
      this._eventEmitter.emit("update", this.usage);
    });
  }
  async getModel() {
    return this._client.getModelDetails(this.modelId);
  }
  on(type, listener) {
    this._eventEmitter.on(type, listener);
    return this;
  }
  clear() {
    this._eventEmitter.clear();
  }
  async generateContent(props) {
    const maxRetries = Math.max(props.maxRetries ?? 3, 0);
    const transform = props.transform;
    let lastError = null;
    const messages = [...props.messages || []];
    for (let attempt = 0; attempt <= maxRetries && !this.controller.signal.aborted; attempt++) {
      try {
        const response = await this._client.generateContent({
          ...props,
          messages,
          signal: this.controller.signal,
          model: this.modelId,
          meta: {
            integrationName: props.meta?.integrationName || "zai",
            promptCategory: props.meta?.promptCategory || `zai:${this.taskType}`,
            promptSource: props.meta?.promptSource || `zai:${this.taskType}:${this.taskId ?? "default"}`
          }
        });
        if (this.controller.signal.aborted) {
          throw this.controller.signal.reason;
        }
        const content = response.output.choices[0]?.content;
        const str = typeof content === "string" ? content : content?.[0]?.text || "";
        let output;
        messages.push({
          role: "assistant",
          content: str || "<Invalid output, no content provided>"
        });
        if (!transform) {
          output = str;
        } else {
          output = transform(str, response.output);
        }
        return { meta: response.meta, output: response.output, text: str, extracted: output };
      } catch (error) {
        if (this.controller.signal.aborted) {
          throw this.controller.signal.reason;
        }
        lastError = error;
        if (attempt === maxRetries) {
          throw lastError;
        }
        messages.push({
          role: "user",
          content: `ERROR PARSING OUTPUT

${lastError.message}.

Please return a valid response addressing the error above.`
        });
      }
    }
    throw lastError;
  }
  get elapsedTime() {
    return Date.now() - this._startedAt;
  }
  get usage() {
    return {
      requests: {
        errors: this._totalErrors,
        requests: this._totalRequests,
        responses: this._totalResponses,
        cached: this._totalCachedResponses,
        percentage: this._totalRequests > 0 ? (this._totalResponses + this._totalErrors) / this._totalRequests : 0
      },
      tokens: {
        input: this._inputTokens,
        output: this._outputTokens,
        total: this._inputTokens + this._outputTokens
      },
      cost: {
        input: this._inputCost,
        output: this._outputCost,
        total: this._inputCost + this._outputCost
      }
    };
  }
}
