import { describe, expect, it } from "vitest"
import { mount, flushPromises } from "@vue/test-utils"
import BarcodeScannerInput from "./BarcodeScannerInput.vue"

describe("BarcodeScannerInput (props-based composition)", () => {
  it("mounts and shows the read-only input + scan button", () => {
    const wrapper = mount(BarcodeScannerInput, { props: { label: "Scan" } })
    expect(wrapper.find("input").exists()).toBe(true)
    expect(wrapper.find("input").attributes("readonly")).not.toBeUndefined()
    expect(wrapper.find("[data-testid='barcode-scan-toggle']").exists()).toBe(true)
    expect(wrapper.text()).toContain("Scan")
  })

  it("renders the label via FieldFrame", () => {
    const wrapper = mount(BarcodeScannerInput, { props: { label: "Ticket code" } })
    expect(wrapper.find("[data-testid='field-frame-label']").text()).toBe(
      "Ticket code",
    )
  })

  it("renders help text and errors through the FieldFrame", () => {
    const wrapper = mount(BarcodeScannerInput, {
      props: {
        label: "Scan",
        helpText: "Point at code",
        errors: ["Camera blocked"],
      },
    })
    expect(wrapper.find("[data-testid='field-frame-help']").text()).toBe(
      "Point at code",
    )
    expect(wrapper.find("[data-testid='field-frame-errors']").text()).toContain(
      "Camera blocked",
    )
  })

  it("sets data-element-name on the inner wrapper", () => {
    const wrapper = mount(BarcodeScannerInput, {
      props: { name: "ticket_code" },
    })
    expect(
      wrapper.find("[data-element-name='ticket_code']").exists(),
    ).toBe(true)
  })

  it("forwards modelValue to the input", () => {
    const wrapper = mount(BarcodeScannerInput, {
      props: { modelValue: "ABC-123" },
    })
    expect((wrapper.find("input").element as HTMLInputElement).value).toBe(
      "ABC-123",
    )
  })

  it("toggles the scan button label when scanning fails to start (no camera)", async () => {
    const wrapper = mount(BarcodeScannerInput, { props: { label: "Scan" } })
    const button = wrapper.find("[data-testid='barcode-scan-toggle']")
    expect(button.text()).toContain("Scan")

    await button.trigger("click")
    await flushPromises()

    // jsdom has no getUserMedia, so start() should fail and surface an error
    // and the button falls back to "Scan" rather than getting stuck.
    expect(wrapper.text()).toContain("Scan")
    expect(wrapper.find("[data-testid='field-frame-errors']").exists()).toBe(true)
  })

  it("updates v-model when a barcode is detected (simulated)", async () => {
    const wrapper = mount(BarcodeScannerInput, {
      props: { modelValue: "" },
    })
    // Bypass the real camera/Quagga interaction (jsdom can't run it) and
    // exercise the v-model emission path that the onDetected handler uses.
    const vm = wrapper.vm as unknown as {
      __handleDetectedForTest: (code: string) => void
    }
    vm.__handleDetectedForTest("DECODED-CODE-42")
    await flushPromises()
    const emissions = wrapper.emitted("update:modelValue")
    expect(emissions).toBeTruthy()
    expect(emissions?.[emissions.length - 1]).toEqual(["DECODED-CODE-42"])
  })

  it("disables the scan button when the input is disabled", () => {
    const wrapper = mount(BarcodeScannerInput, {
      props: { disabled: true },
    })
    const button = wrapper.find("[data-testid='barcode-scan-toggle']")
    expect(button.attributes("disabled")).toBeDefined()
  })

  it.skip("live-scans a barcode through quagga2 (requires a real camera)", () => {
    // jsdom doesn't implement getUserMedia or the canvas streams Quagga
    // needs. The full pipeline is covered by Storybook visual stories.
  })
})
