// HTTP via the host. `fetch` is async (host streams the body), `fetchLocal` reads a preloaded asset
// synchronously. Ported from worker/src/fetch.ts over the same _creatorUtils bridge.

export type FetchOptions = {
  body?: any
  method?: string
  headers?: Record<string, string>
  useOnce?: boolean
  onProgress?: (progress: { loaded: number, total?: number }) => void
}

export interface FetchResponse {
  readonly status: number
  json<T = any>(): T
  text(): string
  dispose(): void
}

class FetchResponseImpl implements FetchResponse {
  readonly status: number
  private readonly _id: number
  constructor(id: number, status: number) {
    this._id = id
    this.status = status
  }
  json<T = any>(): T {
    return _creatorUtils.fetchToJson(this._id)
  }
  text(): string {
    return _creatorUtils.fetchToText(this._id)
  }
  dispose(): void {
    _creatorUtils.disposeFetch(this._id)
  }
}

export const fetch = (url: string, options: FetchOptions = {}): Promise<FetchResponse> =>
  new Promise<FetchResponse>((resolve, reject) => {
    _creatorUtils.fetch(url, options, (systemId, statusCode) => {
      resolve(new FetchResponseImpl(systemId, statusCode))
    }, reject)
  })

export const fetchLocal = (path: string): FetchResponse =>
  new FetchResponseImpl(_creatorUtils.fetchLocal(path), 200)

/** A handle to a picked / fetched file (see openFilePicker). */
export class File {
  /** @internal host buffer handle. `_`-prefixed because hosts read it off the raw object —
   *  same key `FetchResponse` carries, so both are accepted anywhere a buffer is expected
   *  (FormData entries, `UIImage` src, `Texture.load`, `share`). */
  readonly _id: number
  readonly name: string
  readonly size: number
  constructor(systemId: number, name: string, size: number) {
    this._id = systemId
    this.name = name
    this.size = size
  }
}

type FormDataValue = string | number | boolean | File | FetchResponse

export class FormData {
  // Host-facing: hosts walk `_entries` and treat any object value carrying `_id` as binary
  // (the buffer id), everything else as a text field. See viewer-lite utils/fetch.ts,
  // creator-pkg creatorUtilsHandler.cpp, iOS Fetch.swift buildMultipartBody.
  private _entries: [string, FormDataValue, filename?: string][] = []

  append(name: string, value: FormDataValue, filename?: string): void {
    this._entries.push([ name, value, filename ?? (typeof value === "object" ? (value as any).name : undefined) ])
  }
  delete(name: string): void {
    const index = this._entries.findIndex((i) => i[0] === name)
    if (index >= 0) this._entries.splice(index, 1)
  }
}
