import { renderHook, act } from "@testing-library/react";
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
import type { CreateFlowState } from "../../../app/(app)/create/types";
import { useCreateFlowFinalize } from "../../../app/(app)/create/hooks/useCreateFlowFinalize";
import { publishRule, updatePublishedRule } from "../../../lib/create/api";
import { writeLastPublishedRule } from "../../../lib/create/lastPublishedRule";
import { CREATE_FLOW_PENDING_KEEP_RULE_LOGIN_KEY } from "../../../lib/create/pendingKeepRuleLogin";
import {
  CREATE_FLOW_COMPLETED_CELEBRATE_QUERY,
  CREATE_FLOW_COMPLETED_CELEBRATE_VALUE,
} from "../../../app/(app)/create/utils/flowSteps";

vi.mock("../../../lib/create/buildPublishPayload", () => ({
  buildPublishPayload: vi.fn(() => ({
    ok: true as const,
    title: "Published title",
    summary: "Published summary",
    document: {},
  })),
}));

vi.mock("../../../lib/create/api", () => ({
  publishRule: vi.fn(),
  updatePublishedRule: vi.fn(),
}));

vi.mock("../../../lib/create/lastPublishedRule", () => ({
  writeLastPublishedRule: vi.fn(),
}));

const emptyState = {} as CreateFlowState;
const signedIn = { id: "user-1", email: "owner@example.com" };

describe("useCreateFlowFinalize", () => {
  const router = { push: vi.fn() };
  const updateState = vi.fn();
  const openLogin = vi.fn();
  const onGuestPublished = vi.fn();

  beforeEach(() => {
    vi.mocked(publishRule).mockReset();
    vi.mocked(updatePublishedRule).mockReset();
    vi.mocked(writeLastPublishedRule).mockReset();
    router.push.mockReset();
    updateState.mockReset();
    openLogin.mockReset();
    onGuestPublished.mockReset();
    sessionStorage.clear();
  });

  afterEach(() => {
    vi.restoreAllMocks();
  });

  it("routes with celebrate query after initial POST publish", async () => {
    vi.mocked(publishRule).mockResolvedValue({
      ok: true,
      id: "new-rule-id",
      title: "Published title",
    });

    const { result } = renderHook(() =>
      useCreateFlowFinalize({
        state: emptyState,
        router,
        openLogin,
        updateState,
        loginReturnPath: "/create/final-review",
        sessionUser: signedIn,
      }),
    );

    await act(async () => {
      await result.current.finalize();
    });

    expect(router.push).toHaveBeenCalledWith(
      `/create/completed?${CREATE_FLOW_COMPLETED_CELEBRATE_QUERY}=${CREATE_FLOW_COMPLETED_CELEBRATE_VALUE}`,
    );
    expect(updatePublishedRule).not.toHaveBeenCalled();
    expect(writeLastPublishedRule).toHaveBeenCalledWith({
      id: "new-rule-id",
      title: "Published title",
      summary: "Published summary",
      document: {},
    });
    expect(
      sessionStorage.getItem(CREATE_FLOW_PENDING_KEEP_RULE_LOGIN_KEY),
    ).toBeNull();
  });

  it("does not publish while the session is unresolved", async () => {
    const { result } = renderHook(() =>
      useCreateFlowFinalize({
        state: emptyState,
        router,
        openLogin,
        updateState,
        loginReturnPath: "/create/final-review",
        sessionUser: undefined,
      }),
    );

    await act(async () => {
      await result.current.finalize();
    });

    expect(publishRule).not.toHaveBeenCalled();
    expect(router.push).not.toHaveBeenCalled();
  });

  it("omits stakeholder emails when a guest publishes with invites in state", async () => {
    vi.mocked(publishRule).mockResolvedValue({
      ok: true,
      id: "guest-rule-id",
      title: "Published title",
    });

    const { result } = renderHook(() =>
      useCreateFlowFinalize({
        state: {
          ...emptyState,
          stakeholderEmails: ["invitee@example.com"],
        },
        router,
        openLogin,
        updateState,
        loginReturnPath: "/create/final-review",
        sessionUser: null,
        onGuestPublished,
      }),
    );

    await act(async () => {
      await result.current.finalize();
    });

    expect(publishRule).toHaveBeenCalledWith({
      title: "Published title",
      summary: "Published summary",
      document: {},
    });
    expect(onGuestPublished).toHaveBeenCalledWith({ skippedInvites: true });
    expect(openLogin).not.toHaveBeenCalled();
    expect(sessionStorage.getItem(CREATE_FLOW_PENDING_KEEP_RULE_LOGIN_KEY)).toBe("1");
    expect(router.push).toHaveBeenCalledWith(
      `/create/completed?${CREATE_FLOW_COMPLETED_CELEBRATE_QUERY}=${CREATE_FLOW_COMPLETED_CELEBRATE_VALUE}`,
    );
  });

  it("hints to claim later when a guest publishes without stakeholder emails", async () => {
    vi.mocked(publishRule).mockResolvedValue({
      ok: true,
      id: "guest-rule-id",
      title: "Published title",
    });

    const { result } = renderHook(() =>
      useCreateFlowFinalize({
        state: emptyState,
        router,
        openLogin,
        updateState,
        loginReturnPath: "/create/final-review",
        sessionUser: null,
        onGuestPublished,
      }),
    );

    await act(async () => {
      await result.current.finalize();
    });

    expect(publishRule).toHaveBeenCalledWith({
      title: "Published title",
      summary: "Published summary",
      document: {},
    });
    expect(onGuestPublished).toHaveBeenCalledWith({ skippedInvites: false });
    expect(openLogin).not.toHaveBeenCalled();
    expect(sessionStorage.getItem(CREATE_FLOW_PENDING_KEEP_RULE_LOGIN_KEY)).toBe("1");
    expect(router.push).toHaveBeenCalledWith(
      `/create/completed?${CREATE_FLOW_COMPLETED_CELEBRATE_QUERY}=${CREATE_FLOW_COMPLETED_CELEBRATE_VALUE}`,
    );
  });

  it("passes stakeholderEmails to publishRule on initial publish", async () => {
    vi.mocked(publishRule).mockResolvedValue({
      ok: true,
      id: "new-rule-id",
      title: "Published title",
    });

    const { result } = renderHook(() =>
      useCreateFlowFinalize({
        state: {
          ...emptyState,
          stakeholderEmails: ["invitee@example.com"],
        },
        router,
        openLogin,
        updateState,
        loginReturnPath: "/create/final-review",
        sessionUser: signedIn,
      }),
    );

    await act(async () => {
      await result.current.finalize();
    });

    expect(publishRule).toHaveBeenCalledWith(
      expect.objectContaining({
        stakeholderEmails: ["invitee@example.com"],
      }),
    );
  });

  it("routes to /create/completed without celebrate after PATCH update", async () => {
    vi.mocked(updatePublishedRule).mockResolvedValue({ ok: true });

    const { result } = renderHook(() =>
      useCreateFlowFinalize({
        state: {
          ...emptyState,
          editingPublishedRuleId: "  existing-id  ",
        },
        router,
        openLogin,
        updateState,
        loginReturnPath: "/create/edit-rule",
        sessionUser: signedIn,
      }),
    );

    await act(async () => {
      await result.current.finalize();
    });

    expect(router.push).toHaveBeenCalledWith("/create/completed");
    expect(publishRule).not.toHaveBeenCalled();
    expect(updatePublishedRule).toHaveBeenCalledWith(
      "existing-id",
      expect.objectContaining({
        title: "Published title",
        summary: "Published summary",
        document: {},
      }),
    );
    expect(writeLastPublishedRule).toHaveBeenCalledWith({
      id: "existing-id",
      title: "Published title",
      summary: "Published summary",
      document: {},
    });
    expect(updateState).toHaveBeenCalledWith({
      editingPublishedRuleId: undefined,
    });
  });

  it("does not PATCH when a guest publishes an already-published rule", async () => {
    const { result } = renderHook(() =>
      useCreateFlowFinalize({
        state: {
          ...emptyState,
          editingPublishedRuleId: "guest-rule-1",
        },
        router,
        openLogin,
        updateState,
        loginReturnPath: "/create/final-review?syncDraft=1",
        sessionUser: null,
        onGuestPublished,
      }),
    );

    await act(async () => {
      await result.current.finalize();
    });

    expect(updatePublishedRule).not.toHaveBeenCalled();
    expect(publishRule).not.toHaveBeenCalled();
    expect(openLogin).not.toHaveBeenCalled();
    expect(onGuestPublished).toHaveBeenCalledWith({ skippedInvites: false });
    expect(writeLastPublishedRule).toHaveBeenCalledWith({
      id: "guest-rule-1",
      title: "Published title",
      summary: "Published summary",
      document: {},
    });
    expect(sessionStorage.getItem(CREATE_FLOW_PENDING_KEEP_RULE_LOGIN_KEY)).toBe("1");
    expect(router.push).toHaveBeenCalledWith("/create/completed");
  });

  it("opens keepRule login when a guest publish is unauthorized", async () => {
    vi.mocked(publishRule).mockResolvedValue({
      ok: false,
      error: "Unauthorized",
      status: 401,
    });

    const { result } = renderHook(() =>
      useCreateFlowFinalize({
        state: emptyState,
        router,
        openLogin,
        updateState,
        loginReturnPath: "/create/final-review?syncDraft=1",
        sessionUser: null,
        onGuestPublished,
      }),
    );

    await act(async () => {
      await result.current.finalize();
    });

    expect(openLogin).toHaveBeenCalledWith({
      variant: "keepRule",
      nextPath: "/create/completed",
      backdropVariant: "blurredYellow",
    });
    expect(router.push).not.toHaveBeenCalled();
  });
});
