import { EventEmitter } from "./emitter";
export class Response {
  _promise;
  _eventEmitter;
  _context;
  _elasped = null;
  _simplify;
  constructor(context, promise, simplify) {
    this._context = context;
    this._eventEmitter = new EventEmitter();
    this._simplify = simplify;
    this._promise = promise.then(
      (value) => {
        this._elasped ||= this._context.elapsedTime;
        this._eventEmitter.emit("complete", value);
        this._eventEmitter.clear();
        this._context.clear();
        return value;
      },
      (reason) => {
        this._elasped ||= this._context.elapsedTime;
        this._eventEmitter.emit("error", reason);
        this._eventEmitter.clear();
        this._context.clear();
        throw reason;
      }
    );
    this._context.on("update", (usage) => {
      this._eventEmitter.emit("progress", usage);
    });
  }
  /**
   * Subscribes to events emitted during operation execution.
   *
   * @param type - Event type: 'progress', 'complete', or 'error'
   * @param listener - Callback function to handle the event
   * @returns This Response instance for chaining
   *
   * @example Track progress
   * ```typescript
   * response.on('progress', (usage) => {
   *   console.log(`${usage.requests.percentage * 100}% complete`)
   *   console.log(`Cost: $${usage.cost.total}`)
   * })
   * ```
   *
   * @example Handle completion
   * ```typescript
   * response.on('complete', (result) => {
   *   console.log('Operation completed:', result)
   * })
   * ```
   *
   * @example Handle errors
   * ```typescript
   * response.on('error', (error) => {
   *   console.error('Operation failed:', error)
   * })
   * ```
   */
  on(type, listener) {
    this._eventEmitter.on(type, listener);
    return this;
  }
  /**
   * Unsubscribes from events.
   *
   * @param type - Event type to unsubscribe from
   * @param listener - The exact listener function to remove
   * @returns This Response instance for chaining
   *
   * @example
   * ```typescript
   * const progressHandler = (usage) => console.log(usage.tokens.total)
   * response.on('progress', progressHandler)
   * // Later...
   * response.off('progress', progressHandler)
   * ```
   */
  off(type, listener) {
    this._eventEmitter.off(type, listener);
    return this;
  }
  /**
   * Subscribes to an event for a single emission.
   *
   * The listener is automatically removed after being called once.
   *
   * @param type - Event type: 'progress', 'complete', or 'error'
   * @param listener - Callback function to handle the event once
   * @returns This Response instance for chaining
   *
   * @example
   * ```typescript
   * response.once('complete', (result) => {
   *   console.log('Finished:', result)
   * })
   * ```
   */
  once(type, listener) {
    this._eventEmitter.once(type, listener);
    return this;
  }
  /**
   * Binds an external AbortSignal to this operation.
   *
   * When the signal is aborted, the operation will be cancelled automatically.
   * Useful for integrating with UI cancel buttons or request timeouts.
   *
   * @param signal - AbortSignal to bind
   * @returns This Response instance for chaining
   *
   * @example With AbortController
   * ```typescript
   * const controller = new AbortController()
   * const response = zai.extract(data, schema).bindSignal(controller.signal)
   *
   * // Cancel from elsewhere
   * cancelButton.onclick = () => controller.abort()
   * ```
   *
   * @example With timeout
   * ```typescript
   * const controller = new AbortController()
   * setTimeout(() => controller.abort('Timeout'), 10000)
   *
   * const response = zai.answer(docs, question).bindSignal(controller.signal)
   * ```
   */
  bindSignal(signal) {
    if (signal.aborted) {
      this.abort(signal.reason);
    }
    const signalAbort = () => {
      this.abort(signal.reason);
    };
    signal.addEventListener("abort", () => signalAbort());
    void this.once("complete", () => signal.removeEventListener("abort", signalAbort));
    void this.once("error", () => signal.removeEventListener("abort", signalAbort));
    return this;
  }
  /**
   * Aborts the operation in progress.
   *
   * The operation will be cancelled and throw an abort error.
   * Any partial results will not be returned.
   *
   * @param reason - Optional reason for aborting (string or Error)
   *
   * @example
   * ```typescript
   * const response = zai.extract(largeDocument, schema)
   *
   * // Abort after 5 seconds
   * setTimeout(() => response.abort('Operation timeout'), 5000)
   *
   * try {
   *   await response
   * } catch (error) {
   *   console.log('Aborted:', error)
   * }
   * ```
   */
  abort(reason) {
    this._context.controller.abort(reason);
  }
  /**
   * Promise interface - allows awaiting the Response.
   *
   * When awaited, returns the simplified value (S).
   * Use `.result()` for full output with usage statistics.
   *
   * @param onfulfilled - Success handler
   * @param onrejected - Error handler
   * @returns Promise resolving to simplified value
   *
   * @example
   * ```typescript
   * // Simplified value
   * const isPositive = await zai.check(review, 'Is positive?')
   * console.log(isPositive) // true
   * ```
   */
  // oxlint-disable-next-line no-thenable
  then(onfulfilled, onrejected) {
    return this._promise.then(
      (value) => {
        const simplified = this._simplify(value);
        return onfulfilled ? onfulfilled(simplified) : simplified;
      },
      (reason) => {
        if (onrejected) {
          return onrejected(reason);
        }
        throw reason;
      }
    );
  }
  /**
   * Promise interface - handles errors.
   *
   * @param onrejected - Error handler
   * @returns Promise resolving to simplified value or error result
   */
  catch(onrejected) {
    return this._promise.catch(onrejected);
  }
  /**
   * Gets the full result with detailed usage statistics and timing.
   *
   * Unlike awaiting the Response directly (which returns simplified value),
   * this method provides:
   * - `output`: Full operation result (not simplified)
   * - `usage`: Detailed token usage, cost, and request statistics
   * - `elapsed`: Operation duration in milliseconds
   *
   * @returns Promise resolving to full result object
   *
   * @example
   * ```typescript
   * const { output, usage, elapsed } = await zai.check(text, condition).result()
   *
   * console.log(output.value) // true/false
   * console.log(output.explanation) // "The text expresses..."
   * console.log(usage.tokens.total) // 245
   * console.log(usage.cost.total) // 0.0012
   * console.log(elapsed) // 1523 (ms)
   * ```
   *
   * @example Usage statistics breakdown
   * ```typescript
   * const { usage } = await response.result()
   *
   * console.log('Requests:', usage.requests.requests)
   * console.log('Cached:', usage.requests.cached)
   * console.log('Input tokens:', usage.tokens.input)
   * console.log('Output tokens:', usage.tokens.output)
   * console.log('Input cost:', usage.cost.input)
   * console.log('Output cost:', usage.cost.output)
   * console.log('Total cost:', usage.cost.total)
   * ```
   */
  async result() {
    const output = await this._promise;
    const usage = this._context.usage;
    return { output, usage, elapsed: this._elasped };
  }
}
