import { Model } from '../core/Model.js'
import { exponentialBackoffDelay, type RetryOptions, shouldRetryApiError, retry } from './retry_logic.js'
import { fromError } from '../exceptions/builder.js'
import { LiveData } from '../reactive/LiveData.js'
import type SdkService from '../services/SdkService.js'
import type { TrashEntry } from '@api-client/core/models/TrashEntry.js'
import type { ContextListOptions, ContextListResult } from '@api-client/core/events/BaseEvents.js'

enum ModelState {
  /**
   * Not doing anything.
   */
  idle,
  /**
   * Currently querying for breadcrumb data.
   */
  reading,
  /**
   * Data was received and the result yielded no results or less than the limit.
   */
  ended,
}

export default class TrashModel extends Model<TrashEntry[]> {
  /**
   * The current state of the model.
   */
  state: ModelState = ModelState.idle

  /**
   * The trash items
   */
  items: LiveData<TrashEntry[]> = new LiveData<TrashEntry[]>([])

  /**
   * The pagination cursor for files to use with the next query.
   */
  cursor?: string

  constructor(
    protected oid: string,
    protected sdk: SdkService
  ) {
    super()
  }

  reset(): void {
    this.state = ModelState.idle
    this.cursor = undefined
    this.items.set([])
  }

  scheduleQuery(): void {
    this.debounce(this.#query)
  }

  async #query(): Promise<void> {
    if (this.state !== ModelState.idle) {
      return
    }
    this.state = ModelState.reading
    const operation = async (): Promise<ContextListResult<TrashEntry>> => {
      return this.sdk.trash.list(this.oid, this.getPageQueryOptions())
    }
    const retryOptions: RetryOptions = {
      retries: this.retryCount,
      delayMs: exponentialBackoffDelay,
      shouldRetry: shouldRetryApiError as (error: Error) => boolean,
    }
    try {
      const result = await retry(operation, retryOptions)
      this.#processTrashResults(result)
    } catch (e) {
      const err = fromError(e as Error)
      this.notifyError(err)
      this.state = ModelState.idle
    }
  }

  #processTrashResults(result: ContextListResult<TrashEntry>): void {
    const { items, cursor } = result
    if (cursor) {
      this.cursor = cursor
    }
    if (!items.length) {
      this.state = ModelState.ended
      return
    }
    this.items.set(this.items.value.concat(items))
    this.state = ModelState.idle
  }

  getPageQueryOptions(): ContextListOptions {
    const { cursor, limit } = this
    const opts: ContextListOptions = {}
    if (cursor) {
      opts.cursor = cursor
    }
    if (typeof limit === 'number' && limit) {
      opts.limit = limit
    }
    return opts
  }

  /**
   * Finds a file by the key.
   * @param key The key of the file to find
   * @returns The file, if any
   */
  getItem(key: string): TrashEntry | undefined {
    return this.items.value.find((i) => i.key === key)
  }

  delete(keys: string[]): Promise<void> {
    return this.sdk.trash.delete(this.oid, keys)
  }

  restore(keys: string[]): Promise<void> {
    return this.sdk.trash.restore(this.oid, keys)
  }

  removeItems(keys: string[]): void {
    this.items.set(this.items.value.filter((item) => !keys.includes(item.key)))
  }
}
