import { createRef } from 'react';
import { act, fireEvent, render } from '@testing-library/react';

import type { FormApi } from '~/src/components/Form';
import type { RefObject } from 'react';

import { resolveServiceDataFromUrl } from '~/src/lib/getServiceDisplayData';
import { EditLinkForm } from './EditLinkForm';

jest.mock('~/src/lib/i18n', () => ({
  __esModule: true,
  useI18n: () => ({ t: (key: string) => key }),
}));

jest.mock('~/src/lib/tracker/useTracker', () => ({
  __esModule: true,
  useTracker: () => ({ trackEvent: jest.fn() }),
}));

jest.mock('~/src/hooks/useHash', () => ({
  __esModule: true,
  default: () => ({
    hasHashParam: () => false,
    setHashParam: jest.fn(),
    removeHashParam: jest.fn(),
  }),
}));

jest.mock('~/src/components/NextApp/lib/CoreUi', () => ({
  __esModule: true,
  useAppAlert: () => jest.fn(),
}));

jest.mock('../../../ItemPageContext', () => ({
  __esModule: true,
  useItemContext: () => ({
    data: { item: { primaryOwnerAccount: undefined } },
  }),
}));

// We need a controlled implementation to drive the link state
// because the real component debounces input events 600ms.
jest.mock('../LinkInputWithPagePeekingSwitch', () => ({
  __esModule: true,
  default: ({ onInputEnd, defaultValue }: any) => (
    <input
      data-testid="linkInput"
      name="link"
      defaultValue={defaultValue}
      onChange={(e) =>
        onInputEnd?.({
          value: e.target.value,
          isValid: true,
          event: e,
        })
      }
    />
  ),
}));

// Avoid SwitchSetting's heavy theme / tooltip dependencies in tests.
jest.mock('~/src/components/Switch/SwitchSetting', () => ({
  __esModule: true,
  default: ({ value, isDisabled, onChange, testId }: any) => (
    <button
      type="button"
      data-testid={testId}
      data-value={String(value)}
      data-disabled={String(!!isDisabled)}
      onClick={() => onChange?.(!value)}
    />
  ),
}));

// Replace the icon-input with a minimal text input that exposes 'text' name
// so Form-submit can read it. The default impl is heavy and renders things
// like icons + dialogs that we don't need.
jest.mock('../TextInputWithIconButton', () => ({
  __esModule: true,
  default: ({ defaultValue, name }: any) => (
    <input data-testid="textInput" name={name} defaultValue={defaultValue} />
  ),
}));

jest.mock('~/src/components/InputLabel', () => ({
  __esModule: true,
  default: ({ children }: any) => <div>{children}</div>,
}));

jest.mock('~/src/lib/getServiceDisplayData', () => ({
  __esModule: true,
  resolveServiceDataFromUrl: jest.fn(),
}));

const mockResolveServiceDataFromUrl =
  resolveServiceDataFromUrl as jest.MockedFunction<
    typeof resolveServiceDataFromUrl
  >;

const SERVICE_MATCH = {
  match: true,
  name: 'Spotify',
  Icon: (() => null) as any,
  key: 'spotify' as const,
};

const NON_SERVICE_MATCH = {
  match: false,
  name: 'Example',
  Icon: (() => null) as any,
  key: 'example' as const,
};

beforeEach(() => {
  mockResolveServiceDataFromUrl.mockImplementation((url: string) => {
    if (url && url.includes('spotify.com')) return SERVICE_MATCH as any;
    return NON_SERVICE_MATCH as any;
  });
});

const setLinkInputValue = (input: HTMLElement, value: string) => {
  act(() => {
    fireEvent.change(input, { target: { value } });
  });
};

describe('EditLinkForm - email-and-opt-ins toggle', () => {
  it('starts disabled with value=false when link does not resolve to a service (auto-flip)', () => {
    const { getByTestId } = render(
      <EditLinkForm
        onSubmit={jest.fn()}
        initialLinkValue="https://example.com"
        initialEmailAndOptInsValue
        withEmailAndOptInsSetting
      />
    );

    const toggle = getByTestId('emailAndOptInsForLinkSwitch');
    expect(toggle.dataset.disabled).toBe('true');
    expect(toggle.dataset.value).toBe('false');
  });

  it('becomes enabled when user types a known service URL', () => {
    const { getByTestId } = render(
      <EditLinkForm
        onSubmit={jest.fn()}
        initialLinkValue=""
        withEmailAndOptInsSetting
      />
    );

    const linkInput = getByTestId('linkInput');
    setLinkInputValue(linkInput, 'https://open.spotify.com/album/xyz');

    const toggle = getByTestId('emailAndOptInsForLinkSwitch');
    expect(toggle.dataset.disabled).toBe('false');
  });

  it('disables and resets value to false when user goes from service URL back to a non-service URL', () => {
    const { getByTestId } = render(
      <EditLinkForm
        onSubmit={jest.fn()}
        initialLinkValue=""
        withEmailAndOptInsSetting
      />
    );

    const linkInput = getByTestId('linkInput');

    // start with service URL
    setLinkInputValue(linkInput, 'https://open.spotify.com/album/xyz');

    // toggle ON
    act(() => {
      getByTestId('emailAndOptInsForLinkSwitch').click();
    });

    expect(getByTestId('emailAndOptInsForLinkSwitch').dataset.value).toBe(
      'true'
    );

    // change to non-service URL
    setLinkInputValue(linkInput, 'https://example.com');

    const toggle = getByTestId('emailAndOptInsForLinkSwitch');
    expect(toggle.dataset.disabled).toBe('true');
    expect(toggle.dataset.value).toBe('false');
  });
});

describe('EditLinkForm - submit payload', () => {
  it('passes withEmailAndOptIns: undefined when withEmailAndOptInsSetting is false', async () => {
    const onSubmit = jest.fn();
    const formApiRef = createRef<FormApi>() as RefObject<FormApi>;

    render(
      <EditLinkForm
        onSubmit={onSubmit}
        initialLinkValue="https://open.spotify.com/album/xyz"
        formApiRef={formApiRef}
        withEmailAndOptInsSetting={false}
      />
    );

    await act(async () => {
      await formApiRef.current?.submit();
    });

    expect(onSubmit).toHaveBeenCalledTimes(1);
    expect(onSubmit.mock.calls[0][0]).toMatchObject({
      withEmailAndOptIns: undefined,
    });
  });

  it('passes withEmailAndOptIns: true when toggled ON with a service URL', async () => {
    const onSubmit = jest.fn();
    const formApiRef = createRef<FormApi>() as RefObject<FormApi>;

    const { getByTestId } = render(
      <EditLinkForm
        onSubmit={onSubmit}
        initialLinkValue=""
        formApiRef={formApiRef}
        withEmailAndOptInsSetting
      />
    );

    setLinkInputValue(
      getByTestId('linkInput'),
      'https://open.spotify.com/album/xyz'
    );

    act(() => {
      getByTestId('emailAndOptInsForLinkSwitch').click();
    });

    await act(async () => {
      await formApiRef.current?.submit();
    });

    expect(onSubmit).toHaveBeenCalledTimes(1);
    expect(onSubmit.mock.calls[0][0]).toMatchObject({
      withEmailAndOptIns: true,
    });
  });

  it('auto-flip wins: passes withEmailAndOptIns: false when URL becomes non-service after toggling ON', async () => {
    const onSubmit = jest.fn();
    const formApiRef = createRef<FormApi>() as RefObject<FormApi>;

    const { getByTestId } = render(
      <EditLinkForm
        onSubmit={onSubmit}
        initialLinkValue=""
        formApiRef={formApiRef}
        withEmailAndOptInsSetting
      />
    );

    // service URL -> toggle on
    setLinkInputValue(
      getByTestId('linkInput'),
      'https://open.spotify.com/album/xyz'
    );

    act(() => {
      getByTestId('emailAndOptInsForLinkSwitch').click();
    });

    // user then changes to a non-service URL
    setLinkInputValue(getByTestId('linkInput'), 'https://example.com');

    await act(async () => {
      await formApiRef.current?.submit();
    });

    expect(onSubmit).toHaveBeenCalledTimes(1);
    expect(onSubmit.mock.calls[0][0]).toMatchObject({
      withEmailAndOptIns: false,
    });
  });
});
