///|
pub fn backend_info() -> @render.RendererBackendInfo {
  @render.RendererBackendInfo::new(
    backend=@render.RendererBackendKind::WebGpuWasm,
    status=@render.RendererCapabilityStatus::Supported,
    can_render=true,
    can_resize=true,
    owns_surface=false,
    description="Browser WebGPU renderer for MoonBit wasm-gc. It requires the `webgpu` host import module and has no JS-target fallback.",
  )
}

///|
pub fn host_import_info() -> WebGpuHostImportInfo {
  WebGpuHostImportInfo::new(
    import_module="webgpu",
    target="wasm-gc",
    surface_resource="browser canvas WebGPU surface handle",
    renderer_resource="browser WebGPU renderer handle",
    description="MoonBit calls synchronous wasm-gc host imports; the browser loader must initialize navigator.gpu before instantiating the app.",
  )
}

///|
pub fn required_capabilities() -> WebGpuHostCapabilities {
  WebGpuHostCapabilities::new(
    webgpu_available=true,
    adapter_ready=true,
    can_render=true,
  )
}

///|
pub fn canvas_surface_contract() -> @render.WgpuSurfaceContract {
  @render.WgpuSurfaceContract::new(
    source=@render.WgpuSurfaceSourceKind::WebCanvasSurface,
    backend=@render.RendererBackendKind::WebGpuWasm,
    ready=true,
    description="Canvas surface is supplied by window/web and configured by the wasm-gc `webgpu` host import module.",
  )
}

///|
pub fn renderer_descriptor() -> @render.RendererDescriptor {
  @render.RendererDescriptor::new(
    backend=@render.RendererBackendKind::WebGpuWasm,
    family=@render.RendererFamily::WebGpu,
    presentation=@render.RendererPresentationModel::WebCanvas,
    supported_targets=[
      @render.RendererTarget::WasmGc,
      @render.RendererTarget::Web,
    ],
    capabilities=webgpu_provider_capabilities(
      provider_id="webgpu-wasm",
      backend=@render.RendererBackendKind::WebGpuWasm,
    ),
    description="Browser WebGPU renderer reached through wasm-gc host imports.",
  )
}

///|
/// Provider factory for the wasm-gc WebGPU adapter. Browser composition owns
/// the canvas-specific renderer factory while this package owns negotiation
/// and its capability declaration.
pub fn create_webgpu_provider(
  bind~ : (@render.HostSurface) -> @render.RendererBindResult,
) -> @render.RendererProvider {
  let caps = webgpu_provider_capabilities(
    provider_id="webgpu-wasm",
    backend=@render.RendererBackendKind::WebGpuWasm,
  )
  @render.RendererProvider::new(
    id="webgpu-wasm",
    descriptor=renderer_descriptor(),
    capabilities=() => caps,
    bind~,
  )
}

///|
/// Build the browser Canvas2D fallback provider. The browser runtime exposes
/// its Canvas2D implementation through the WebGPU-compatible host-import
/// port, so this provider intentionally creates `WebGpuWasmRenderer` values
/// instead of importing the WeChat-only `canvas2d` wasm host module.
pub fn create_browser_canvas2d_fallback_provider(
  bind~ : (@render.HostSurface) -> @render.RendererBindResult,
) -> @render.RendererProvider {
  let caps = browser_canvas2d_fallback_capabilities(
    provider_id="canvas2d",
    backend=@render.RendererBackendKind::Canvas2DWasm,
  )
  @render.RendererProvider::new(
    id="canvas2d",
    descriptor=@render.RendererDescriptor::new(
      backend=@render.RendererBackendKind::Canvas2DWasm,
      family=@render.RendererFamily::Canvas2D,
      presentation=@render.RendererPresentationModel::WebCanvas,
      supported_targets=[
        @render.RendererTarget::WasmGc,
        @render.RendererTarget::Web,
      ],
      capabilities=caps,
      description="Browser Canvas2D fallback reached through the WebGPU-compatible wasm-gc host-import port.",
    ),
    capabilities=() => caps,
    bind~,
  )
}

///|
pub fn webgpu_provider_capabilities(
  provider_id~ : String,
  backend~ : @render.RendererBackendKind,
) -> Array[@render.RendererBackendFeatureCapability] {
  @render_common.renderer_provider_capabilities(provider_id~, backend~, capability_for=feature => {
    let status = match feature {
      @render.RendererFeature::Gradient | @render.RendererFeature::Transform =>
        @render.RendererFeatureStatus::Partial
      _ => @render.RendererFeatureStatus::Supported
    }
    (status, "Browser WebGPU provider declares \{feature.label()} support.")
  })
}

///|
pub fn browser_canvas2d_fallback_capabilities(
  provider_id~ : String,
  backend~ : @render.RendererBackendKind,
) -> Array[@render.RendererBackendFeatureCapability] {
  @render_common.renderer_provider_capabilities(provider_id~, backend~, capability_for=feature => {
    let status = match feature {
      @render.RendererFeature::Image | @render.RendererFeature::AsyncImage =>
        @render.RendererFeatureStatus::Partial
      _ => @render.RendererFeatureStatus::Supported
    }
    (
      status,
      "Browser Canvas2D fallback provider declares \{feature.label()} support.",
    )
  })
}

///|
pub fn adapter_info() -> WebGpuHostAdapterInfo {
  WebGpuHostAdapterInfo::new(
    renderer=backend_info(),
    surface_contract=canvas_surface_contract(),
    imports=host_import_info(),
    capabilities=required_capabilities(),
    description="Web/Wasm uses a single wasm-gc path: window/web events plus browser WebGPU host imports. Missing host capabilities fail startup instead of falling back.",
  )
}

///|
pub fn create_webgpu_renderer(
  canvas_id~ : String,
  logical_size~ : @core.Size,
  scale_factor? : Double = 1.0,
) -> WebGpuWasmRenderer raise WebGpuHostError {
  create_renderer(
    canvas_id~,
    logical_size~,
    scale_factor~,
    port=wasm_gc_host_port(),
  )
}

///|
pub fn wasm_gc_host_port() -> WebGpuHostRendererPort {
  let surface_ref : Ref[HostWebGpuSurface?] = Ref(None)
  let renderer_ref : Ref[HostWebGpuRenderer?] = Ref(None)
  WebGpuHostRendererPort::new(
    capabilities=() => {
      WebGpuHostCapabilities::new(
        webgpu_available=host_webgpu_available(),
        adapter_ready=host_adapter_ready(),
        can_render=host_can_render(),
      )
    },
    create_surface=(canvas_id, metrics) => {
      let surface = host_create_surface(
        host_string(canvas_id),
        metrics.logical_size.width,
        metrics.logical_size.height,
        metrics.scale_factor,
      )
      if !host_surface_is_valid(surface) {
        raise WebGpuHostError::SurfaceUnavailable
      }
      surface_ref.val = Some(surface)
      WebGpuHostSurface::new(canvas_id~, metrics~)
    },
    create_renderer=_surface => {
      match surface_ref.val {
        Some(surface) => {
          let renderer = host_create_renderer(surface)
          if !host_renderer_is_valid(renderer) {
            raise WebGpuHostError::DeviceUnavailable
          }
          renderer_ref.val = Some(renderer)
        }
        None => raise WebGpuHostError::SurfaceUnavailable
      }
    },
    resize=metrics => {
      match (surface_ref.val, renderer_ref.val) {
        (Some(surface), Some(renderer)) => {
          raise_for_host_status(
            host_surface_resize(
              surface,
              metrics.logical_size.width,
              metrics.logical_size.height,
              metrics.scale_factor,
            ),
          )
          raise_for_host_status(
            host_renderer_resize(
              renderer,
              metrics.logical_size.width,
              metrics.logical_size.height,
              metrics.scale_factor,
            ),
          )
        }
        _ => raise WebGpuHostError::InvalidResource
      }
    },
    measure_text=input => host_measure_text_layout(input),
    register_font_data=(family, data) => {
      ignore(
        host_register_font_data(
          host_string(family.css_token()),
          host_string(@base64.encode(data[:])),
        ),
      )
    },
    image_resource=source => {
      match renderer_ref.val {
        Some(renderer) => host_image_resource_record(renderer, source)
        None => None
      }
    },
    submit=calls => {
      match renderer_ref.val {
        Some(renderer) => submit_host_calls(renderer, calls)
        None => raise WebGpuHostError::InvalidResource
      }
    },
    dispose=() => {
      match renderer_ref.val {
        Some(renderer) => host_renderer_dispose(renderer)
        None => ()
      }
      match surface_ref.val {
        Some(surface) => host_surface_dispose(surface)
        None => ()
      }
      renderer_ref.val = None
      surface_ref.val = None
    },
  )
}

///|
pub fn create_surface(
  canvas_id~ : String,
  logical_size~ : @core.Size,
  scale_factor? : Double = 1.0,
  port~ : WebGpuHostRendererPort,
) -> WebGpuHostSurface raise WebGpuHostError {
  let metrics = @core.SurfaceMetrics::from_logical(
    size=logical_size,
    scale_factor~,
  )
  port.create_surface(canvas_id, metrics)
}

///|
pub fn create_renderer(
  canvas_id~ : String,
  logical_size~ : @core.Size,
  scale_factor? : Double = 1.0,
  port~ : WebGpuHostRendererPort,
) -> WebGpuWasmRenderer raise WebGpuHostError {
  let capabilities = port.capabilities()
  ensure_required_capabilities(capabilities)
  let surface = create_surface(canvas_id~, logical_size~, scale_factor~, port~)
  port.create_renderer(surface)
  {
    surface,
    port,
    size: logical_size,
    present_count: 0,
    last_calls: [],
    last_fallback_plan: @render_common.plan_renderer_command_fallbacks([]),
    image_lifecycle: @render_common.ImageResourceLifecycle::new(),
    layer_cache: WebGpuLayerCommandCache::new(),
  }
}

///|
pub fn WebGpuWasmRenderer::backend_kind(
  self : WebGpuWasmRenderer,
) -> @render.RendererBackendKind {
  let _ = self
  @render.RendererBackendKind::WebGpuWasm
}

///|
pub fn WebGpuWasmRenderer::backend_info(
  self : WebGpuWasmRenderer,
) -> @render.RendererBackendInfo {
  let _ = self
  backend_info()
}

///|
pub fn WebGpuWasmRenderer::resize(
  self : WebGpuWasmRenderer,
  metrics : @core.SurfaceMetrics,
) -> Unit raise WebGpuHostError {
  self.port.resize(metrics)
  self.surface.metrics = metrics
  self.size = metrics.logical_size
}

///|
pub fn WebGpuWasmRenderer::render(
  self : WebGpuWasmRenderer,
  commands : Array[@core.DrawCommand],
) -> Unit raise WebGpuHostError {
  self.last_fallback_plan = @render_common.plan_renderer_command_fallbacks(
    commands,
  )
  self.record_web_image_commands(commands)
  let calls = draw_commands_to_host_calls(commands, self.size)
  self.port.submit(calls)
  self.refresh_web_image_commands(commands)
  self.last_calls = calls
  self.present_count = self.present_count + 1
}

///|
pub fn WebGpuWasmRenderer::render_frame(
  self : WebGpuWasmRenderer,
  frame : @core.DrawFrame,
) -> @render.RenderFrameResult {
  let expanded = self.layer_cache.expand_frame_commands(frame.commands)
  let commands : Array[@core.DrawCommand] = [
    @core.DrawCommand::Clear(frame.clear_color),
  ]
  commands.append(expanded.commands)
  self.render(commands) catch {
    _ => ()
  }
  @render.RenderFrameResult::new(
    cache_hit_count=expanded.cache_hit_count,
    cache_miss_count=expanded.cache_miss_count,
    cache_update_count=expanded.cache_update_count,
  )
}

///|
pub fn WebGpuWasmRenderer::text_system(
  self : WebGpuWasmRenderer,
) -> @core.TextSystem {
  @core.TextSystem::new(
    id="webgpu-wasm-host",
    measure=input => self.port.measure_text(input),
    register_font_data=(family, data) => {
      self.port.register_font_data(family, data)
    },
  )
}

///|
pub fn WebGpuWasmRenderer::dispose(self : WebGpuWasmRenderer) -> Unit {
  self.port.dispose()
}

///|
pub fn WebGpuWasmRenderer::present_count(self : WebGpuWasmRenderer) -> Int {
  self.present_count
}

///|
pub fn WebGpuWasmRenderer::last_calls(
  self : WebGpuWasmRenderer,
) -> Array[WebGpuHostCall] {
  self.last_calls
}

///|
pub fn WebGpuWasmRenderer::last_fallback_plan(
  self : WebGpuWasmRenderer,
) -> @render.RendererCommandPlan {
  self.last_fallback_plan
}

///|
pub fn WebGpuWasmRenderer::image_resources(
  self : WebGpuWasmRenderer,
) -> Array[@render.ImageResourceRecord] {
  self.image_lifecycle.snapshot()
}

///|