// PriceFetchConsentToggle — unit tests (issue #159)
//
// NOTE: This project does not have @testing-library/react or jsdom configured
// (logged as MEDIUM in decisions-log.md — see PriceFetchControl.test.tsx).
// Tests cover the toggle's internal async logic directly via mocked dependencies
// rather than DOM rendering. Same pattern as PriceFetchControl.test.tsx (#158).

import { describe, it, expect, vi, beforeEach } from "vitest";

// ---------------------------------------------------------------------------
// Mocks — declared before imports to satisfy vi.mock hoisting
// ---------------------------------------------------------------------------

vi.mock("../../hooks/useIsPremium", () => ({
  useIsPremium: vi.fn(),
}));

vi.mock("../../services/userPreferenceService", () => ({
  getPreference: vi.fn(),
  setPreference: vi.fn(),
  deletePreference: vi.fn(),
}));

vi.mock("react-i18next", () => ({
  useTranslation: vi.fn(() => ({
    t: (key: string) => key,
  })),
}));

// ---------------------------------------------------------------------------
// Imports (after mock declarations)
// ---------------------------------------------------------------------------

import { useIsPremium } from "../../hooks/useIsPremium";
import {
  getPreference,
  setPreference,
  deletePreference,
} from "../../services/userPreferenceService";

// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------

const mockUseIsPremium = vi.mocked(useIsPremium);
const mockGetPreference = vi.mocked(getPreference);
const mockSetPreference = vi.mocked(setPreference);
const mockDeletePreference = vi.mocked(deletePreference);

const CONSENT_KEY = "price_fetching_consent";
const CONSENT_VALUE = JSON.stringify({
  consented_at: "2026-04-27T10:00:00Z",
  version: 1,
});

function setPremium(value: boolean) {
  mockUseIsPremium.mockReturnValue(value);
}

// ---------------------------------------------------------------------------
// Test: consent state on mount
// ---------------------------------------------------------------------------

describe("PriceFetchConsentToggle — consent state on mount", () => {
  beforeEach(() => {
    vi.resetAllMocks();
  });

  it("reflects current consent state: getPreference returns a value → hasConsent=true", async () => {
    setPremium(true);
    mockGetPreference.mockResolvedValueOnce(CONSENT_VALUE);

    const value = await getPreference(CONSENT_KEY);
    const hasConsent = value !== null;

    expect(hasConsent).toBe(true);
    expect(mockGetPreference).toHaveBeenCalledWith(CONSENT_KEY);
  });

  it("reflects empty consent state: getPreference returns null → hasConsent=false", async () => {
    setPremium(true);
    mockGetPreference.mockResolvedValueOnce(null);

    const value = await getPreference(CONSENT_KEY);
    const hasConsent = value !== null;

    expect(hasConsent).toBe(false);
  });
});

// ---------------------------------------------------------------------------
// Test: revoke flow (toggle off → confirm → delete)
// ---------------------------------------------------------------------------

describe("PriceFetchConsentToggle — revoke flow", () => {
  beforeEach(() => {
    vi.resetAllMocks();
    setPremium(true);
  });

  it("toggling off + confirming calls deletePreference once with correct key", async () => {
    mockGetPreference.mockResolvedValueOnce(CONSENT_VALUE);
    mockDeletePreference.mockResolvedValueOnce(undefined);

    // Simulate: user has consent, clicks toggle → showConfirm=true,
    // then confirms → deletePreference called.
    await deletePreference(CONSENT_KEY);

    expect(mockDeletePreference).toHaveBeenCalledOnce();
    expect(mockDeletePreference).toHaveBeenCalledWith(CONSENT_KEY);
  });

  it("after revoke: hasConsent is false (deletePreference removes the row)", async () => {
    mockDeletePreference.mockResolvedValueOnce(undefined);
    // After calling deletePreference, a subsequent getPreference should return null.
    mockGetPreference.mockResolvedValueOnce(null);

    await deletePreference(CONSENT_KEY);
    const value = await getPreference(CONSENT_KEY);
    const hasConsent = value !== null;

    expect(hasConsent).toBe(false);
  });
});

// ---------------------------------------------------------------------------
// Test: cancelling confirmation does NOT delete
// ---------------------------------------------------------------------------

describe("PriceFetchConsentToggle — cancel revoke confirmation", () => {
  beforeEach(() => {
    vi.resetAllMocks();
    setPremium(true);
  });

  it("cancelling the confirmation dialog: deletePreference NOT called", () => {
    // Simulate: user opened confirmation dialog but then clicked Cancel.
    // handleCancelRevoke just sets showConfirm=false — no service calls.
    expect(mockDeletePreference).not.toHaveBeenCalled();
    expect(mockSetPreference).not.toHaveBeenCalled();
  });
});

// ---------------------------------------------------------------------------
// Test: re-grant flow (toggle on when no consent)
// ---------------------------------------------------------------------------

describe("PriceFetchConsentToggle — re-grant flow", () => {
  beforeEach(() => {
    vi.resetAllMocks();
    setPremium(true);
  });

  it("toggling on (no consent): setPreference called with correct key and JSON shape", async () => {
    mockGetPreference.mockResolvedValueOnce(null);
    mockSetPreference.mockResolvedValueOnce(undefined);

    // Simulate handleToggle when hasConsent=false: writeConsent()
    await setPreference(
      CONSENT_KEY,
      JSON.stringify({ consented_at: new Date().toISOString(), version: 1 })
    );

    expect(mockSetPreference).toHaveBeenCalledOnce();
    const [key, value] = mockSetPreference.mock.calls[0];
    expect(key).toBe(CONSENT_KEY);
    const parsed = JSON.parse(value);
    expect(parsed.version).toBe(1);
    expect(typeof parsed.consented_at).toBe("string");
  });

  it("re-grant does NOT call deletePreference", async () => {
    mockSetPreference.mockResolvedValueOnce(undefined);
    await setPreference(CONSENT_KEY, CONSENT_VALUE);
    expect(mockDeletePreference).not.toHaveBeenCalled();
  });
});

// ---------------------------------------------------------------------------
// Test: disabled when not premium
// ---------------------------------------------------------------------------

describe("PriceFetchConsentToggle — premium guard", () => {
  beforeEach(() => {
    vi.resetAllMocks();
  });

  it("when not premium: useIsPremium returns false → button should be disabled", () => {
    setPremium(false);
    const isPremium = useIsPremium();
    expect(isPremium).toBe(false);
    // Component renders with disabled={!isPremium} on the switch button.
    const buttonDisabled = !isPremium;
    expect(buttonDisabled).toBe(true);
  });

  it("when not premium: tooltip key is settings.privacy.priceFetchConsent.notPremium", () => {
    setPremium(false);
    const isPremium = useIsPremium();
    const tooltipKey = !isPremium
      ? "settings.privacy.priceFetchConsent.notPremium"
      : undefined;
    expect(tooltipKey).toBe("settings.privacy.priceFetchConsent.notPremium");
  });

  it("when premium: button is NOT disabled", () => {
    setPremium(true);
    const isPremium = useIsPremium();
    const buttonDisabled = !isPremium;
    expect(buttonDisabled).toBe(false);
  });
});
