import React, { Dispatch, useEffect, useState } from "react";
import { act, render } from "@testing-library/react-native";
import { View } from "react-native";
import { FreezeWithCallback } from "../index";

const SimpleContent = () => <View testID="simple-content" />;

interface InnerProps {
  value: number;
}

// eslint-disable-next-line unused-imports/no-unused-vars
const Inner = ({ value }: InnerProps) => (
  <View testID="inner-component" data-value={value} />
);

interface TestSubscriber {
  renderCount: number;
  subscription: Dispatch<number>;
}

function createSubscriberComponent(): [React.FC, TestSubscriber] {
  const testState: TestSubscriber = {
    renderCount: 0,
    subscription: () => {},
  };

  const Subscriber: React.FC = () => {
    const [value, setValue] = useState(0);

    useEffect(() => {
      testState.subscription = setValue;
    }, []);

    testState.renderCount += 1;

    return <Inner value={value} />;
  };

  return [Subscriber, testState];
}

interface ContainerProps {
  freeze: boolean;
  children: React.ReactNode;
}

const Container = ({ freeze, children }: ContainerProps) => (
  <FreezeWithCallback freeze={freeze}>{children}</FreezeWithCallback>
);

function setupTest(initialFreeze: boolean = false) {
  const [Subscriber, subscriberState] = createSubscriberComponent();

  const renderResult = render(
    <Container freeze={initialFreeze}>
      <Subscriber />
    </Container>
  );

  return {
    renderResult,
    subscriberState,
    updateFreeze: (freeze: boolean) => {
      act(() =>
        renderResult.rerender(
          <Container freeze={freeze}>
            <Subscriber />
          </Container>
        )
      );
    },
  };
}

describe("FreezeWithCallback", () => {
  test("Renders stuff not frozen", () => {
    const { getByTestId } = render(
      <Container freeze={false}>
        <SimpleContent />
      </Container>
    );

    expect(getByTestId("simple-content")).toBeTruthy();
  });

  test("Does not render stuff when frozen", () => {
    const { queryByTestId } = render(
      <Container freeze>
        <SimpleContent />
      </Container>
    );

    expect(queryByTestId("simple-content")).toBe(null);
  });

  test("Stuff is gone after freeze", () => {
    const { toJSON, getByTestId, rerender } = render(
      <Container freeze={false}>
        <SimpleContent />
      </Container>
    );

    expect(getByTestId("simple-content")).toBeTruthy();

    act(() =>
      rerender(
        <Container freeze>
          <SimpleContent />
        </Container>
      )
    );

    expect(toJSON()).toBe(null);
  });

  test("Updates work when not frozen", () => {
    const { renderResult, subscriberState } = setupTest();

    expect(
      renderResult.getByTestId("inner-component").props["data-value"]
    ).toEqual(0);

    act(() => subscriberState.subscription(1));

    expect(
      renderResult.getByTestId("inner-component").props["data-value"]
    ).toEqual(1);

    expect(subscriberState.renderCount).toBe(2);
  });

  test("Updates does not propagate when frozen", () => {
    const { renderResult, subscriberState, updateFreeze } = setupTest();

    expect(
      renderResult.getByTestId("inner-component").props["data-value"]
    ).toEqual(0);

    updateFreeze(true);
    act(() => subscriberState.subscription(1));

    expect(
      renderResult.getByTestId("inner-component").props["data-value"]
    ).toEqual(0);

    expect(subscriberState.renderCount).toBe(1);
  });

  test("State persists after defrost", () => {
    const { renderResult, subscriberState, updateFreeze } = setupTest();

    expect(
      renderResult.getByTestId("inner-component").props["data-value"]
    ).toEqual(0);

    act(() => subscriberState.subscription(1));

    expect(
      renderResult.getByTestId("inner-component").props["data-value"]
    ).toEqual(1);

    updateFreeze(true);
    expect(renderResult.toJSON()).toBe(null);

    updateFreeze(false);
    expect(renderResult.getByTestId("inner-component")).toBeTruthy();

    expect(
      renderResult.getByTestId("inner-component").props["data-value"]
    ).toEqual(1);
  });

  test("Update propagate after defrost", () => {
    const { renderResult, subscriberState, updateFreeze } = setupTest();

    updateFreeze(true);
    act(() => subscriberState.subscription(1));
    act(() => subscriberState.subscription(2));
    act(() => subscriberState.subscription(3));

    expect(
      renderResult.getByTestId("inner-component").props["data-value"]
    ).toEqual(0);

    updateFreeze(false);

    expect(
      renderResult.getByTestId("inner-component").props["data-value"]
    ).toEqual(3);

    expect(subscriberState.renderCount).toBe(2);
  });
});
