import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import {
  cleanup,
  fireEvent,
  render,
  screen,
  waitFor,
} from "@testing-library/react";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { useFormSchemaQuery } from "@/hooks/marriage/use-form-schema";
import { useMarriageProfileQuery } from "@/hooks/marriage/use-profile-main";
import {
  useMarriageSectionDataQuery,
  useUpdateMarriageSectionDataMutation,
} from "@/hooks/marriage/use-section-data";
import { convertSchemaToFrontendItems } from "@/lib/schema-adapter";
import {
  QuestionAnswersProvider,
  useQuestionAnswers,
} from "./question-answer-storage";

vi.mock("@/hooks/marriage/use-form-schema", () => ({
  useFormSchemaQuery: vi.fn(),
}));
vi.mock("@/hooks/marriage/use-profile-main", () => ({
  useMarriageProfileQuery: vi.fn(),
}));
vi.mock("@/hooks/marriage/use-section-data", () => ({
  applyProfilePatchResultToCache: vi.fn(),
  useMarriageSectionDataQuery: vi.fn(),
  useUpdateMarriageSectionDataMutation: vi.fn(),
}));

// Dummy component to interact with the context
function TestComponent({ slug }: { slug: string }) {
  const { setAnswerValue, flushAnswers } = useQuestionAnswers();

  return (
    <div>
      <button
        data-testid="set-radio"
        onClick={() =>
          setAnswerValue(
            {
              id: "q1",
              type: "radio",
              title: "Q1",
              order: 1,
              required: true,
              baseRequired: true,
              isVisible: true,
              description: "",
              tooltip: "",
              extras: { placeHolder: "", options: [], range: [0, 0] },
              options: [
                { id: "opt1", value: "A", label: "Option A", order: 1 },
              ],
            },
            "opt1",
          )
        }
      >
        Set Radio
      </button>

      <button
        data-testid="set-checkbox"
        onClick={() =>
          setAnswerValue(
            {
              id: "q2",
              type: "checkbox",
              title: "Q2",
              order: 2,
              required: true,
              baseRequired: true,
              isVisible: true,
              description: "",
              tooltip: "",
              extras: { placeHolder: "", options: [], range: [0, 0] },
              options: [
                { id: "opt2", value: "B", label: "Option B", order: 1 },
                { id: "opt3", value: "C", label: "Option C", order: 2 },
              ],
            },
            ["opt2", "opt3"],
          )
        }
      >
        Set Checkbox
      </button>

      <button data-testid="save" onClick={() => flushAnswers()}>
        Save
      </button>
    </div>
  );
}

describe("Question Answer & Schema Integration", () => {
  let capturedPayload: any = null;

  beforeEach(() => {
    capturedPayload = null;
    const updateMutateAsync = vi.fn(async (payload) => {
      capturedPayload = payload;
      return payload;
    });
    (useUpdateMarriageSectionDataMutation as any).mockReturnValue({
      mutateAsync: updateMutateAsync,
      isPending: false,
    });
    (useMarriageProfileQuery as any).mockReturnValue({
      data: { can_edit_profile: true },
    });
    (useMarriageSectionDataQuery as any).mockReturnValue({
      data: [],
      isLoading: false,
    });
    (useFormSchemaQuery as any).mockReturnValue({
      data: { is_completed: false },
      isLoading: false,
      isFetching: false,
    });
  });

  afterEach(() => {
    cleanup();
  });

  it("should send option_id instead of label/value for radio", async () => {
    const queryClient = new QueryClient({
      defaultOptions: { queries: { retry: false } },
    });

    render(
      <QueryClientProvider client={queryClient}>
        <QuestionAnswersProvider
          slug="test_slug"
          questions={[
            {
              id: "q1",
              type: "radio",
              title: "Q1",
              order: 1,
              required: true,
              baseRequired: true,
              isVisible: true,
              description: "",
              tooltip: "",
              extras: { placeHolder: "", options: [], range: [0, 0] },
              options: [
                { id: "opt1", value: "A", label: "Option A", order: 1 },
              ],
            },
          ]}
        >
          <TestComponent slug="test_slug" />
        </QuestionAnswersProvider>
      </QueryClientProvider>,
    );

    fireEvent.click(screen.getByTestId("set-radio"));
    fireEvent.click(screen.getByTestId("save"));

    await waitFor(() => {
      expect(capturedPayload).not.toBeNull();
      const field = capturedPayload.fields.find((f: any) => f.key === "q1");
      expect(field.option_id).toBe("opt1");
      expect(field.value).toBe("opt1"); // ui value is option_id
    });
  });

  it("should send array of option_ids for checkbox", async () => {
    const queryClient = new QueryClient({
      defaultOptions: { queries: { retry: false } },
    });

    render(
      <QueryClientProvider client={queryClient}>
        <QuestionAnswersProvider
          slug="test_slug"
          questions={[
            {
              id: "q2",
              type: "checkbox",
              title: "Q2",
              order: 2,
              required: true,
              baseRequired: true,
              isVisible: true,
              description: "",
              tooltip: "",
              extras: { placeHolder: "", options: [], range: [0, 0] },
              options: [
                { id: "opt2", value: "B", label: "Option B", order: 1 },
                { id: "opt3", value: "C", label: "Option C", order: 2 },
              ],
            },
          ]}
        >
          <TestComponent slug="test_slug" />
        </QuestionAnswersProvider>
      </QueryClientProvider>,
    );

    fireEvent.click(screen.getByTestId("set-checkbox"));
    fireEvent.click(screen.getByTestId("save"));

    await waitFor(() => {
      expect(capturedPayload).not.toBeNull();
      const field = capturedPayload.fields.find((f: any) => f.key === "q2");
      expect(field.option_id).toEqual(["opt2", "opt3"]);
    });
  });

  it("batches all dirty answers into one mutation", async () => {
    const queryClient = new QueryClient({
      defaultOptions: { queries: { retry: false } },
    });

    render(
      <QueryClientProvider client={queryClient}>
        <QuestionAnswersProvider
          slug="test_slug"
          questions={[
            {
              id: "q1",
              type: "radio",
              title: "Q1",
              order: 1,
              required: true,
              baseRequired: true,
              isVisible: true,
              description: "",
              tooltip: "",
              extras: { placeHolder: "", options: [], range: [0, 0] },
              options: [
                { id: "opt1", value: "A", label: "Option A", order: 1 },
              ],
            },
            {
              id: "q2",
              type: "checkbox",
              title: "Q2",
              order: 2,
              required: true,
              baseRequired: true,
              isVisible: true,
              description: "",
              tooltip: "",
              extras: { placeHolder: "", options: [], range: [0, 0] },
              options: [
                { id: "opt2", value: "B", label: "Option B", order: 1 },
                { id: "opt3", value: "C", label: "Option C", order: 2 },
              ],
            },
          ]}
        >
          <TestComponent slug="test_slug" />
        </QuestionAnswersProvider>
      </QueryClientProvider>,
    );

    fireEvent.click(screen.getByTestId("set-radio"));
    fireEvent.click(screen.getByTestId("set-checkbox"));
    fireEvent.click(screen.getByTestId("save"));

    await waitFor(() => expect(capturedPayload?.fields).toHaveLength(2));
  });

  it("should sort schema questions and options by order correctly", () => {
    const mockSchema = {
      sections: [
        {
          id: "sec1",
          title: "Section",
          order: 1,
          icon: "user-circle",
          is_required: true,
          estimated_minutes: 5,
          cards: [
            {
              id: "card1",
              title: "Card",
              order: 2,
              questions: [
                {
                  id: "q1",
                  title: "Q1",
                  type: "text",
                  order: 10,
                  required: true,
                  is_visible: true,
                  ui_config: {},
                  options: [],
                },
                {
                  id: "q2",
                  title: "Q2",
                  type: "text",
                  order: 5,
                  required: true,
                  is_visible: true,
                  ui_config: {},
                  options: [
                    { id: "opt1", value: "A", label: "Option A", order: 2 },
                    { id: "opt2", value: "B", label: "Option B", order: 1 },
                  ],
                },
              ],
            },
            {
              id: "card2",
              title: "Card 2",
              order: 1,
              questions: [
                {
                  id: "q3",
                  title: "Q3",
                  type: "text",
                  order: 1,
                  required: true,
                  is_visible: true,
                  ui_config: {},
                  options: [],
                },
              ],
            },
          ],
        },
      ],
      progress: { sections_progress: {} },
    } as any;

    const frontendItems = convertSchemaToFrontendItems(mockSchema, "en");
    const questions = frontendItems[0].questions;

    expect(questions[0].id).toBe("q3");
    expect(questions[1].id).toBe("q2");
    expect(questions[2].id).toBe("q1");

    expect(questions[1].options[0].id).toBe("opt2");
    expect(questions[1].options[1].id).toBe("opt1");
  });

  it("should hydrate radio/dropdown/checkbox with option_id instead of canonical value", async () => {
    // Mock the backend sending canonical value but keeping option_id
    (useMarriageSectionDataQuery as any).mockReturnValue({
      data: {
        slug: "test_slug",
        data: [
          {
            key: "q_radio",
            type: "radio",
            value: "Server Canonical Value",
            option_id: "opt_radio",
          },
          {
            key: "q_check",
            type: "checkbox",
            value: ["Server Val 1", "Server Val 2"],
            option_id: ["opt_check1", "opt_check2"],
          },
        ],
      },
      isLoading: false,
    });

    const capturedValues: any = {};

    function HydrationTestComponent() {
      const { getAnswerValue } = useQuestionAnswers();
      capturedValues.radio = getAnswerValue({ id: "q_radio" } as any);
      capturedValues.check = getAnswerValue({ id: "q_check" } as any);
      return <div />;
    }

    const queryClient = new QueryClient({
      defaultOptions: { queries: { retry: false } },
    });

    render(
      <QueryClientProvider client={queryClient}>
        <QuestionAnswersProvider slug="test_slug" questions={[]}>
          <HydrationTestComponent />
        </QuestionAnswersProvider>
      </QueryClientProvider>,
    );

    // Give it a moment to reconcile useEffect in QuestionAnswersProvider
    await waitFor(() => {
      expect(capturedValues.radio).toBe("opt_radio");
      expect(capturedValues.check).toEqual(["opt_check1", "opt_check2"]);
    });
  });

  it("should respect bq.is_required over bq.required for required state", () => {
    const mockSchema = {
      sections: [
        {
          id: "sec1",
          title: "Section",
          order: 1,
          icon: "user-circle",
          is_required: true,
          estimated_minutes: 5,
          cards: [
            {
              id: "card1",
              title: "Card",
              order: 1,
              questions: [
                {
                  id: "q1",
                  title: "Q1",
                  type: "text",
                  order: 1,
                  required: false,
                  is_required: true,
                  is_visible: true,
                  ui_config: {},
                  options: [],
                },
              ],
            },
          ],
        },
      ],
      progress: { sections_progress: {} },
    } as any;

    const frontendItems = convertSchemaToFrontendItems(mockSchema, "en");
    const question = frontendItems[0].questions[0];

    expect(question.baseRequired).toBe(false);
    expect(question.required).toBe(true);
  });
});
