import React from "react";
import { View, Text, TouchableOpacity } from "react-native";
import { render, fireEvent, act } from "@testing-library/react-native";
import { useErrorStore } from "../store";
import { ErrorBoundary } from "../index";

jest.mock(
  "@applicaster/zapp-react-native-ui-components/Components/ErrorScreen",
  () => ({
    // eslint-disable-next-line react/prop-types
    ErrorScreen: ({ dismissError, message, buttons }) => (
      <View testID="error-screen">
        <Text testID="error-message">{message || "Default error message"}</Text>
        {/* eslint-disable-next-line react/prop-types */}
        {buttons?.map((button, index) => (
          <TouchableOpacity
            key={index}
            onPress={button.onPress}
            testID={`button-${index}`}
          >
            <Text>{button.text}</Text>
          </TouchableOpacity>
        ))}

        {dismissError ? (
          <TouchableOpacity onPress={dismissError} testID="dismiss-button">
            <Text>Dismiss Error</Text>
          </TouchableOpacity>
        ) : null}
      </View>
    ),
  })
);

jest.mock("@applicaster/zapp-react-native-bridge/QuickBrick", () => ({
  sendQuickBrickEvent: jest.fn(),
  QUICK_BRICK_EVENTS: {
    FORCE_APP_RELOAD: "force_app_reload",
  },
}));

jest.mock("@applicaster/zapp-react-native-utils/logger", () => ({
  coreLogger: {
    addSubsystem: () => ({
      error: jest.fn(),
    }),
  },
}));

// eslint-disable-next-line react/prop-types
const TestComponent = ({ error, errorMessage = "error !!" }) => {
  if (error) {
    throw new Error(errorMessage);
  }

  return (
    <View>
      <Text>all is good</Text>
    </View>
  );
};

describe("<ErrorBoundary />", () => {
  // Clear all error states between tests
  beforeEach(() => {
    useErrorStore.getState().dismissError();
  });

  describe("when there is no error", () => {
    it("renders children correctly", () => {
      const { getByText } = render(
        <ErrorBoundary>
          <TestComponent />
        </ErrorBoundary>
      );

      expect(getByText("all is good")).toBeTruthy();
    });
  });

  describe("when an error is caught", () => {
    const _consoleLogs = {
      error: console.error,
      log: console.log,
    };

    beforeAll(() => {
      console.error = jest.fn();
      console.log = jest.fn();
    });

    afterAll(() => {
      console.error = _consoleLogs.error;
      console.log = _consoleLogs.log;
    });

    it("renders error state with default values (recoverable error)", () => {
      const { getByTestId } = render(
        <ErrorBoundary>
          <TestComponent error />
        </ErrorBoundary>
      );

      expect(getByTestId("error-screen")).toBeTruthy();
      expect(getByTestId("dismiss-button")).toBeTruthy(); // Should be recoverable by default
    });

    it("handles non-recoverable errors (no dismiss button)", () => {
      const { getByTestId, queryByTestId } = render(
        <ErrorBoundary recoverable={false}>
          <TestComponent error />
        </ErrorBoundary>
      );

      expect(getByTestId("error-screen")).toBeTruthy();
      expect(queryByTestId("dismiss-button")).toBeFalsy(); // Should not be recoverable
    });

    it("shows custom error message from caught error", () => {
      const customErrorMessage = "Custom error message for testing";

      const { getByTestId } = render(
        <ErrorBoundary>
          <TestComponent error errorMessage={customErrorMessage} />
        </ErrorBoundary>
      );

      // The error info should be passed to the ErrorScreen
      expect(getByTestId("error-message").props.children).toBeTruthy();
    });

    it("can dismiss the error and restore the UI", () => {
      // First render with error
      const { getByTestId, queryByText, rerender } = render(
        <ErrorBoundary>
          <TestComponent error />
        </ErrorBoundary>
      );

      // Verify error screen is shown
      expect(getByTestId("error-screen")).toBeTruthy();

      // Dismiss the error
      fireEvent.press(getByTestId("dismiss-button"));

      // Force rerender to see the change
      rerender(
        <ErrorBoundary>
          <TestComponent />
        </ErrorBoundary>
      );

      // Now the regular content should be visible
      expect(queryByText("all is good")).toBeTruthy();
    });
  });

  describe("error store functionality", () => {
    it("properly sets error state", () => {
      const error = new Error("Test error");
      const errorInfo = "Test error info";

      act(() => {
        useErrorStore.getState().setError(error, errorInfo, true);
      });

      const state = useErrorStore.getState();
      expect(state.hasError).toBe(true);
      expect(state.error).toBe(error);
      expect(state.info).toBe(errorInfo);
      expect(state.recoverable).toBe(true);
    });

    it("properly resets error state", () => {
      // First set an error
      act(() => {
        useErrorStore
          .getState()
          .setError(new Error("Test error"), "Info", true);
      });

      // Then dismiss it
      act(() => {
        useErrorStore.getState().dismissError();
      });

      const state = useErrorStore.getState();
      expect(state.hasError).toBe(false);
      expect(state.error).toBeUndefined();
      expect(state.info).toBeUndefined();
      expect(state.recoverable).toBe(true); // Default value when reset
    });

    it("remembers recoverable state", () => {
      act(() => {
        useErrorStore
          .getState()
          .setError(new Error("Test error"), "Info", false);
      });

      const state = useErrorStore.getState();
      expect(state.recoverable).toBe(false);
    });
  });

  describe("integration with ErrorScreen", () => {
    it("renders restart app button for non-recoverable errors", () => {
      const { getAllByTestId } = render(
        <ErrorBoundary recoverable={false}>
          <TestComponent error />
        </ErrorBoundary>
      );

      // Should have the restart app button
      const buttons = getAllByTestId(/button-/);
      expect(buttons.length).toBeGreaterThan(0);
    });

    it("passes error info to the ErrorScreen component", () => {
      const customErrorMessage = "Very specific error message";

      const { getByTestId } = render(
        <ErrorBoundary>
          <TestComponent error errorMessage={customErrorMessage} />
        </ErrorBoundary>
      );

      // The error info should be visible in the UI
      expect(getByTestId("error-message")).toBeTruthy();
    });
  });
});
