// CameraView — the device camera (live preview + still capture) as a Presentable. A typed wrapper
// over the `NativeView("camera")` capability (docs/navigation-presentable-plan.md): open it
// fullscreen (`await camera.open()`), push it (`Router.push(camera)`), or embed it among a
// screen's children as a live preview node. `takePhoto()` returns a `File` (a host buffer
// handle) — use it as a texture (`Texture2D.load(file)`), upload it (`FormData`), or `share` it.
//
// Host-OPTIONAL: it exists where the host registers a "camera" view (Android; web can via
// `_creatorUI._registerView`) — gate on `CameraView.isSupported`. Opening prepares first (camera
// permission) while the current destination stays visible — the same contract as ARScene/QRScanner.
//
// The pre-Presentable `_creatorUtils.camera*` bridges are gone from the ABI (2026-07-15): hosts
// keep their implementations only to serve previously compiled bundles.

import { File } from "../runtime/fetch"
import { _navEpoch, type PresentOptions } from "../ui/presentable"
import { NativeView, NativeViewElement } from "../ui/NativeView"
import { _requestCameraPermission } from "./permission"

export type CameraFacing = "front" | "back"

export interface CameraView extends NativeView {
  /** Request camera permission, then present the preview. The current destination stays visible
   *  until the camera is ready; rejects if permission is denied, the host has no camera, or
   *  another navigation superseded this one. */
  open(options?: PresentOptions): Promise<void>
  /** Capture a still frame as an image `File` (host-encoded). */
  takePhoto(): Promise<File>
  /** Switch between the front and back camera while the preview is live. */
  setFacingMode(facingMode: CameraFacing): Promise<void>
}

class CameraViewElement extends NativeViewElement {
  constructor(options: { facingMode?: CameraFacing } = {}) {
    super("camera", { facingMode: options.facingMode ?? "back" })
  }

  /** @internal prepare-then-present: permission before the swap (see Presentable._prepare). */
  async _prepare(): Promise<void> {
    const epoch = _navEpoch()
    await _requestCameraPermission()
    if (_navEpoch() !== epoch) {
      throw new Error("CameraView.open() superseded: another destination was opened while preparing")
    }
  }

  override async open(options?: PresentOptions): Promise<void> {
    // Fail before prompting for the camera — see QRScanner.
    if (!NativeView.isSupported("camera")) {
      throw new Error("CameraView isn't available on this host — it needs a registered \"camera\" view.")
    }
    await this._prepare()
    super.open(options)
  }

  async takePhoto(): Promise<File> {
    const photo = await this.call("takePhoto")
    return new File(photo.systemId, photo.name, photo.size)
  }

  async setFacingMode(facingMode: CameraFacing): Promise<void> {
    await this.call("setFacingMode", facingMode)
  }
}

/**
 * Create a camera view (default: the back camera). `CameraView.isSupported` reports whether this
 * host has a camera capability — check it before offering the feature.
 */
// PURE IIFE so an app that never uses the camera tree-shakes the whole plugin away (see NativeView).
export const CameraView: {
  (options?: { facingMode?: CameraFacing }): CameraView
  /** Whether this host registered a "camera" view. */
  readonly isSupported: boolean
} = /*#__PURE__*/ (() => {
  const factory = (options?: { facingMode?: CameraFacing }): CameraView =>
    new CameraViewElement(options) as unknown as CameraView
  Object.defineProperty(factory, "isSupported", {
    get: (): boolean => NativeView.isSupported("camera"),
  })
  return factory as any
})()
