import { useState } from "react";
import { RadioOption } from "../../src/components/ui/Radio";

// The `type="radio"` sibling of CheckboxOption.cy.tsx — same "selectable
// card" markup, but exercised for what actually differs about a radio
// input: the mandatory `name`/`value` pair and the mutually-exclusive
// group behavior that's the whole reason to reach for radio over checkbox.

describe("RadioOption", () => {
  it("renders its label content", () => {
    cy.mount(
      <RadioOption name="theme" value="dark" checked={false} onChange={() => {}}>
        Sombre
      </RadioOption>,
    );

    cy.contains("label", "Sombre").should("be.visible");
  });

  it("sets the native input's name and value", () => {
    cy.mount(
      <RadioOption name="theme" value="dark" checked={false} onChange={() => {}}>
        Sombre
      </RadioOption>,
    );

    cy.get("input[type=radio]")
      .should("have.attr", "name", "theme")
      .and("have.attr", "value", "dark");
  });

  it("reflects the checked prop on the native input, and the is-selected class", () => {
    cy.mount(
      <RadioOption name="theme" value="dark" checked={false} onChange={() => {}}>
        Sombre
      </RadioOption>,
    );
    cy.get("input[type=radio]").should("not.be.checked");
    cy.get("label").should("not.have.class", "is-selected");

    cy.mount(
      <RadioOption name="theme" value="dark" checked={true} onChange={() => {}}>
        Sombre
      </RadioOption>,
    );
    cy.get("input[type=radio]").should("be.checked");
    cy.get("label").should("have.class", "is-selected");
  });

  it("calls onChange with its own value when clicked while unchecked", () => {
    const onChange = cy.stub().as("onChange");
    cy.mount(
      <RadioOption name="theme" value="dark" checked={false} onChange={onChange}>
        Sombre
      </RadioOption>,
    );

    cy.get("input[type=radio]").click();

    cy.get("@onChange").should("have.been.calledOnceWith", "dark");
  });

  it("does not fire onChange again when clicking a radio that's already checked", () => {
    // Native radio inputs only emit a `change` event when their checked
    // state actually flips — clicking an already-selected option in a
    // group is a no-op, unlike a checkbox which always toggles.
    const onChange = cy.stub().as("onChange");
    cy.mount(
      <RadioOption name="theme" value="dark" checked={true} onChange={onChange}>
        Sombre
      </RadioOption>,
    );

    cy.get("input[type=radio]").click();

    cy.get("@onChange").should("not.have.been.called");
  });

  it("toggles when the click lands on the label text, not just the input itself", () => {
    const onChange = cy.stub().as("onChange");
    cy.mount(
      <RadioOption name="theme" value="dark" checked={false} onChange={onChange}>
        Sombre
      </RadioOption>,
    );

    cy.contains("label", "Sombre").click();

    cy.get("@onChange").should("have.been.calledOnceWith", "dark");
  });

  it("merges the caller's className with the container layout, alongside is-selected", () => {
    cy.mount(
      <RadioOption
        name="theme"
        value="dark"
        checked={true}
        onChange={() => {}}
        className="theme-select__option"
      >
        Sombre
      </RadioOption>,
    );
    cy.get("label").should("have.class", "theme-select__option").and("have.class", "is-selected");
  });

  it("marks the check-mark decoration as aria-hidden, so screen readers only announce the radio itself", () => {
    cy.mount(
      <RadioOption name="theme" value="dark" checked={false} onChange={() => {}}>
        Sombre
      </RadioOption>,
    );
    cy.get("span.check-mark").should("have.attr", "aria-hidden", "true");
  });

  it("behaves as a mutually-exclusive group when several options share the same name", () => {
    // A stateful wrapper mirroring UserPreferencesPage's theme picker — the
    // one real caller — mounting 3 RadioOptions that share `name="theme"`
    // and one `value` of state between them.
    function ThemeGroup() {
      const [theme, setTheme] = useState<"system" | "light" | "dark">("system");
      return (
        <>
          <RadioOption name="theme" value="system" checked={theme === "system"} onChange={setTheme}>
            Système
          </RadioOption>
          <RadioOption name="theme" value="light" checked={theme === "light"} onChange={setTheme}>
            Clair
          </RadioOption>
          <RadioOption name="theme" value="dark" checked={theme === "dark"} onChange={setTheme}>
            Sombre
          </RadioOption>
        </>
      );
    }
    cy.mount(<ThemeGroup />);

    cy.contains("label", "Système").should("have.class", "is-selected");
    cy.contains("label", "Clair").should("not.have.class", "is-selected");
    cy.contains("label", "Sombre").should("not.have.class", "is-selected");

    cy.contains("label", "Sombre").click();

    cy.contains("label", "Sombre").should("have.class", "is-selected");
    cy.contains("label", "Système").should("not.have.class", "is-selected");
    cy.contains("label", "Clair").should("not.have.class", "is-selected");
    // The native `name` grouping also keeps the browser's own radio
    // semantics honest — only one input in the group can be `:checked`.
    cy.get("input[type=radio]:checked").should("have.length", 1);
  });
});
