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

function makeFile(name: string, size = 1024): File {
  const blob = new Blob([new Uint8Array(size)], { type: "image/png" })
  return new File([blob], name, { type: "image/png" })
}

describe("UploadFile (props-based composition)", () => {
  describe("rendering", () => {
    it("renders the label and trigger button", () => {
      const wrapper = mount(UploadFile, { props: { label: "Avatar" } })
      expect(wrapper.text()).toContain("Avatar")
      expect(wrapper.find("[data-testid='upload-trigger']").exists()).toBe(true)
    })

    it("passes accept to the input", () => {
      const wrapper = mount(UploadFile, {
        props: { label: "X", accept: "image/png" },
      })
      expect(
        wrapper.find("[data-testid='upload-input']").attributes("accept"),
      ).toBe("image/png")
    })

    it("joins acceptedFileTypes into the accept attribute", () => {
      const wrapper = mount(UploadFile, {
        props: {
          label: "X",
          acceptedFileTypes: ["image/png", "image/jpeg"],
        },
      })
      expect(
        wrapper.find("[data-testid='upload-input']").attributes("accept"),
      ).toBe("image/png,image/jpeg")
    })

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

  describe("value binding", () => {
    it("emits update:modelValue with a single selected file", async () => {
      const wrapper = mount(UploadFile, { props: { label: "Avatar" } })
      const input = wrapper.find("[data-testid='upload-input']")
        .element as HTMLInputElement
      const file = makeFile("hero.png", 5000)
      Object.defineProperty(input, "files", { configurable: true, value: [file] })
      await wrapper.find("[data-testid='upload-input']").trigger("change")
      const emitted = wrapper.emitted("update:modelValue")
      expect(emitted).toBeTruthy()
      expect(emitted![emitted!.length - 1]).toEqual([file])
      expect(wrapper.text()).toContain("hero.png")
    })

    it("accumulates files when multiple is set", async () => {
      const wrapper = mount(UploadFile, {
        props: { label: "Docs", multiple: true },
      })
      const inputEl = wrapper.find("[data-testid='upload-input']")
        .element as HTMLInputElement
      const f1 = makeFile("a.png")
      const f2 = makeFile("b.png")
      Object.defineProperty(inputEl, "files", { configurable: true, value: [f1] })
      await wrapper.find("[data-testid='upload-input']").trigger("change")
      Object.defineProperty(inputEl, "files", { configurable: true, value: [f2] })
      await wrapper.find("[data-testid='upload-input']").trigger("change")
      expect(wrapper.text()).toContain("a.png")
      expect(wrapper.text()).toContain("b.png")
    })

    it("removes a selected file", async () => {
      const wrapper = mount(UploadFile, { props: { label: "Avatar" } })
      const inputEl = wrapper.find("[data-testid='upload-input']")
        .element as HTMLInputElement
      const f = makeFile("doc.png")
      Object.defineProperty(inputEl, "files", { configurable: true, value: [f] })
      await wrapper.find("[data-testid='upload-input']").trigger("change")
      await wrapper.find("[data-testid='upload-remove']").trigger("click")
      expect(wrapper.text()).not.toContain("doc.png")
    })

    it("rejects files larger than maxFileSize", async () => {
      const wrapper = mount(UploadFile, {
        props: { label: "Avatar", maxFileSize: 1024 },
      })
      const inputEl = wrapper.find("[data-testid='upload-input']")
        .element as HTMLInputElement
      const big = makeFile("big.png", 9999)
      Object.defineProperty(inputEl, "files", { configurable: true, value: [big] })
      await wrapper.find("[data-testid='upload-input']").trigger("change")
      expect(wrapper.text()).not.toContain("big.png")
    })

    it("hydrates files from a File modelValue", () => {
      const f = makeFile("preset.png")
      const wrapper = mount(UploadFile, {
        props: { label: "Avatar", modelValue: f },
      })
      expect(wrapper.text()).toContain("preset.png")
    })
  })

  describe("state matrix", () => {
    it("disables the trigger and input when disabled", () => {
      const wrapper = mount(UploadFile, {
        props: { label: "X", disabled: true },
      })
      expect(
        wrapper.find("[data-testid='upload-trigger']").attributes("disabled"),
      ).toBeDefined()
      expect(
        wrapper.find("[data-testid='upload-input']").attributes("disabled"),
      ).toBeDefined()
    })

    it("renders help text", () => {
      const wrapper = mount(UploadFile, {
        props: { label: "X", helpText: "Max 2 MB" },
      })
      expect(wrapper.find("[data-testid='field-frame-help']").text()).toBe(
        "Max 2 MB",
      )
    })

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

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

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