import * as React from "react";
import Axios from "axios";
import { ThemeProvider } from "styled-components";
import { render, cleanup, screen, fireEvent, waitFor, act } from "@testing-library/react";
import "@testing-library/jest-dom";

import { parseTheme } from "@ax/helpers";
import RecoveryModal, { IRecoveryProps } from "@ax/components/Login/LoginForm/RecoveryModal";
import globalTheme from "@ax/themes/theme.json";

afterEach(cleanup);

jest.mock("axios"); // This overwrites axios methods with jest Mock
const mockedAxios = Axios as jest.MockedFunction<typeof Axios>;

describe("Recovery modal component rendering", () => {
	it("should render the component if closed", () => {
		const defaultProps: IRecoveryProps = {
			isOpen: true,
			toggleModal: jest.fn(),
		};

		render(
			<ThemeProvider theme={parseTheme(globalTheme)}>
				<RecoveryModal {...defaultProps} />
			</ThemeProvider>,
		);

		const modalContent = screen.getByTestId("recovery-modal-content");
		expect(modalContent).toBeTruthy();
	});

	it("should not render the component content if closed", () => {
		const defaultProps: IRecoveryProps = {
			isOpen: false,
			toggleModal: jest.fn(),
		};

		render(
			<ThemeProvider theme={parseTheme(globalTheme)}>
				<RecoveryModal {...defaultProps} />
			</ThemeProvider>,
		);

		const modalContent = screen.queryByTestId("recovery-modal-content");
		expect(modalContent).toBeFalsy();
	});
});

describe("Login component events", () => {
	it("should call toggleModal on cancel button click", () => {
		const toggleModalMock = jest.fn();
		const defaultProps: IRecoveryProps = {
			isOpen: true,
			toggleModal: toggleModalMock,
		};

		render(
			<ThemeProvider theme={parseTheme(globalTheme)}>
				<RecoveryModal {...defaultProps} />
			</ThemeProvider>,
		);

		const button = screen.getByText("Cancel");
		expect(button).toBeTruthy();
		fireEvent.click(button);

		expect(toggleModalMock).toBeCalledTimes(1);
	});

	it("should disable button if email is not valid", () => {
		const toggleModalMock = jest.fn();
		const defaultProps: IRecoveryProps = {
			isOpen: true,
			toggleModal: toggleModalMock,
		};

		render(
			<ThemeProvider theme={parseTheme(globalTheme)}>
				<RecoveryModal {...defaultProps} />
			</ThemeProvider>,
		);

		const input = screen.getByLabelText("email");
		expect(input).toBeTruthy();
		fireEvent.change(input, { target: { value: "asdfasfsdf" } });

		const button = screen.getByText("Reset Password").closest("button");
		expect(button).toBeTruthy();
		expect(button).toBeDisabled();
	});

	it("should enable button if email is valid", async () => {
		const toggleModalMock = jest.fn();
		const defaultProps: IRecoveryProps = {
			isOpen: true,
			toggleModal: toggleModalMock,
		};

		render(
			<ThemeProvider theme={parseTheme(globalTheme)}>
				<RecoveryModal {...defaultProps} />
			</ThemeProvider>,
		);

		const input = screen.getByLabelText("email");
		expect(input).toBeTruthy();
		fireEvent.change(input, { target: { value: "prueba@email.com" } });

		const button = screen.getByText("Reset Password").closest("button");

		await waitFor(() => expect(button).not.toBeDisabled(), {
			timeout: 5000,
		});
	});

	it("should hide input if api response is ok", async () => {
		const defaultProps: IRecoveryProps = {
			isOpen: true,
			toggleModal: jest.fn(),
		};

		const data = { data: [], status: 200, statusText: "Ok", headers: {}, config: {} };
		mockedAxios.mockResolvedValue(data);

		const Component = (
			<ThemeProvider theme={parseTheme(globalTheme)}>
				<RecoveryModal {...defaultProps} />
			</ThemeProvider>
		);

		const { rerender } = render(Component);

		const input = screen.getByLabelText("email");
		expect(input).toBeTruthy();
		fireEvent.change(input, { target: { value: "prueba@email.com" } });
		const button = screen.getByTestId("button-default");

		await waitFor(() => expect(screen.getByTestId("button-default")).not.toBeDisabled(), {
			timeout: 5000,
		});

		button.click();

		await act(async () => rerender(Component));

		expect(screen.queryByLabelText("email")).toBeFalsy();

		const button2 = screen.getByText("Got it!").closest("button");
		expect(button2).toBeTruthy();
	});

	it("should show error if api response is not ok", async () => {
		const defaultProps: IRecoveryProps = {
			isOpen: true,
			toggleModal: jest.fn(),
		};

		const data = { data: [], status: 400, statusText: "error", headers: {}, config: {} };
		mockedAxios.mockResolvedValue(data);

		const Component = (
			<ThemeProvider theme={parseTheme(globalTheme)}>
				<RecoveryModal {...defaultProps} />
			</ThemeProvider>
		);

		const { rerender } = render(Component);

		const input = screen.getByLabelText("email");
		expect(input).toBeTruthy();
		fireEvent.change(input, { target: { value: "prueba@email.com" } });
		const button = screen.getByTestId("button-default");

		await waitFor(() => expect(screen.getByTestId("button-default")).not.toBeDisabled(), {
			timeout: 5000,
		});

		button.click();

		await act(async () => rerender(Component));

		expect(screen.getByText("Sorry, this email doesn't exist.")).toBeTruthy();
	});
});
