import { describe, expect, it, vi } from "vitest"
import { flushPromises, mount } from "@vue/test-utils"
import AssetPickerFile from "./AssetPickerFile.vue"
import type { Asset } from "./AssetPickerFile.vue"

const SAMPLE_ASSETS: Asset[] = [
  { id: "a1", url: "/u/a1.png", name: "Hero.png" },
  { id: "a2", url: "/u/a2.png", name: "Logo.png" },
]

describe("AssetPickerFile (props-based composition)", () => {
  describe("rendering", () => {
    it("mounts without error", () => {
      const wrapper = mount(AssetPickerFile, { props: { label: "Asset" } })
      expect(wrapper.exists()).toBe(true)
    })

    it("renders the label via FieldFrame", () => {
      const wrapper = mount(AssetPickerFile, {
        props: { label: "Custom label" },
      })
      expect(wrapper.text()).toContain("Custom label")
    })

    it("renders the Browse button when nothing is selected", () => {
      const wrapper = mount(AssetPickerFile, { props: { label: "X" } })
      const browse = wrapper.find("[data-testid='asset-picker-browse']")
      expect(browse.exists()).toBe(true)
      expect(browse.text()).toContain("Browse")
    })

    it("renders the selected asset chip with thumbnail and name", () => {
      const wrapper = mount(AssetPickerFile, {
        props: {
          label: "X",
          modelValue: {
            id: "a1",
            url: "/u/a1.png",
            name: "Hero.png",
            thumbnailUrl: "/u/a1-thumb.png",
          },
        },
      })
      const chip = wrapper.find("[data-testid='asset-picker-chip']")
      expect(chip.exists()).toBe(true)
      expect(chip.text()).toContain("Hero.png")
      const thumb = wrapper.find("[data-testid='asset-picker-chip-thumb']")
      expect(thumb.exists()).toBe(true)
      expect(thumb.attributes("src")).toBe("/u/a1-thumb.png")
    })

    it("falls back to url when thumbnailUrl is missing", () => {
      const wrapper = mount(AssetPickerFile, {
        props: {
          label: "X",
          modelValue: { id: "a1", url: "/u/a1.png", name: "logo.png" },
        },
      })
      const thumb = wrapper.find("[data-testid='asset-picker-chip-thumb']")
      expect(thumb.attributes("src")).toBe("/u/a1.png")
    })

    it("accepts a plain string modelValue", () => {
      const wrapper = mount(AssetPickerFile, {
        props: { label: "X", modelValue: "hero.png" },
      })
      expect(wrapper.text()).toContain("hero.png")
    })

    it("works without any props at all", () => {
      const wrapper = mount(AssetPickerFile)
      expect(wrapper.exists()).toBe(true)
    })
  })

  describe("clear selection", () => {
    it("emits update:modelValue null when the remove button is clicked", async () => {
      const wrapper = mount(AssetPickerFile, {
        props: {
          label: "X",
          modelValue: { id: "a1", url: "/u/a1.png", name: "hero.png" },
        },
      })
      await wrapper.find("[data-testid='asset-picker-remove']").trigger("click")
      const emitted = wrapper.emitted("update:modelValue")
      expect(emitted).toBeTruthy()
      expect(emitted![emitted!.length - 1]).toEqual([null])
    })
  })

  describe("onBrowse / selection", () => {
    it("calls onBrowse when the Browse button is clicked", async () => {
      const onBrowse = vi.fn().mockResolvedValue(SAMPLE_ASSETS)
      const wrapper = mount(AssetPickerFile, {
        props: { label: "X", onBrowse },
        attachTo: document.body,
      })
      await wrapper.find("[data-testid='asset-picker-browse']").trigger("click")
      await flushPromises()
      expect(onBrowse).toHaveBeenCalledTimes(1)
      wrapper.unmount()
    })

    it("disables Browse when disabled is true", () => {
      const wrapper = mount(AssetPickerFile, {
        props: { label: "X", disabled: true },
      })
      const browse = wrapper.find("[data-testid='asset-picker-browse']")
      expect(browse.attributes("disabled")).toBeDefined()
    })
  })

  describe("upload flow", () => {
    it("does not render upload button when onUpload is not provided", () => {
      const wrapper = mount(AssetPickerFile, {
        props: { label: "X", onBrowse: () => Promise.resolve([]) },
      })
      expect(wrapper.find("[data-testid='asset-picker-upload']").exists()).toBe(
        false,
      )
    })
  })

  describe("state matrix", () => {
    it("renders help text", () => {
      const wrapper = mount(AssetPickerFile, {
        props: { label: "X", helpText: "Pick an image" },
      })
      expect(wrapper.find("[data-testid='field-frame-help']").text()).toBe(
        "Pick an image",
      )
    })

    it("renders required asterisk", () => {
      const wrapper = mount(AssetPickerFile, {
        props: { label: "X", required: true },
      })
      expect(wrapper.html()).toContain("after:content-['*']")
    })

    it("renders errors", () => {
      const wrapper = mount(AssetPickerFile, {
        props: { label: "X", errors: ["Required"] },
      })
      expect(wrapper.find("[data-testid='field-frame-errors']").text()).toContain(
        "Required",
      )
    })

    it("hides the label when hideLabel is true", () => {
      const wrapper = mount(AssetPickerFile, {
        props: { label: "Hidden", hideLabel: true },
      })
      expect(wrapper.find("[data-testid='field-frame-label-sr']").exists()).toBe(
        true,
      )
    })
  })

  // Dialog content is portalled — interactive selection of an asset card
  // inside the open Dialog body is asserted in Storybook (interactions
  // run reliably under playwright). jsdom drops the portal target on
  // teardown which causes test flake.
})
