import { Provider } from "react-redux";

import ImageDragAndDrop from "@ax/components/ImageDragAndDrop";
import { VALID_IMAGE_FORMATS } from "@ax/constants";
import { parseTheme } from "@ax/helpers";
import globalTheme from "@ax/themes/theme.json";

import { cleanup, fireEvent, render, screen } from "@testing-library/react";
import configureStore from "redux-mock-store";
import thunk from "redux-thunk";
import { ThemeProvider } from "styled-components";

jest.mock("@ax/containers/Gallery", () => ({
	galleryActions: {
		uploadError: jest.fn(() => ({ type: "UPLOAD_ERROR" })),
		resetError: jest.fn(() => ({ type: "RESET_ERROR" })),
		uploadImage: jest.fn(() => async () => null),
		replaceImage: jest.fn(() => async () => null),
		setUploadSuccess: jest.fn(() => ({ type: "SET_UPLOAD_SUCCESS" })),
	},
}));

afterEach(cleanup);

const mockStore = configureStore([thunk]);

const initialStore = {
	gallery: {
		isUploading: false,
		isSuccess: false,
		isError: false,
		errorMsg: "",
	},
};

const renderImageDragAndDrop = (props = {}, storeData = initialStore) => {
	const store = mockStore(storeData);
	return render(
		<Provider store={store}>
			<ThemeProvider theme={parseTheme(globalTheme)}>
				<ImageDragAndDrop siteID="global" handleUpload={jest.fn()} {...props} />
			</ThemeProvider>
		</Provider>,
	);
};

describe("ImageDragAndDrop rendering", () => {
	it("should render file-drag-and-drop-wrapper testid", () => {
		renderImageDragAndDrop();
		expect(screen.getByTestId("file-drag-and-drop-wrapper")).toBeTruthy();
	});

	it("should render Drag your image here text", () => {
		renderImageDragAndDrop();
		expect(screen.getByText("Drag your image here")).toBeTruthy();
	});

	it("should render Select images button", () => {
		renderImageDragAndDrop();
		expect(screen.getByText("Select images")).toBeTruthy();
	});

	it("should render valid formats text", () => {
		renderImageDragAndDrop();
		const validFormatsTexts = screen.getAllByText((_, element) => {
			return VALID_IMAGE_FORMATS.every((format) => element?.textContent?.includes(format));
		});
		expect(validFormatsTexts.length).toBeGreaterThan(0);
	});

	it("should render valid formats in subtitle", () => {
		renderImageDragAndDrop();
		const subtitles = screen.getAllByText(/Max. size: 50MB/);
		expect(subtitles.length).toBeGreaterThan(0);
	});

	it("should render file input element", () => {
		renderImageDragAndDrop();
		const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
		expect(fileInput).toBeTruthy();
		const expectedAccept = VALID_IMAGE_FORMATS.map((format) => `.${format}`).join(",");
		expect(fileInput.accept).toBe(expectedAccept);
	});

	it("should render placeholder when isAllowedToUpload is false", () => {
		renderImageDragAndDrop({ isAllowedToUpload: false });
		expect(screen.getByText("Select an image to see details")).toBeTruthy();
		expect(screen.queryByText("Drag your image here")).toBeFalsy();
	});

	it("should render Uploading text when isUploading is true", () => {
		renderImageDragAndDrop({}, { gallery: { isUploading: true, isSuccess: false, isError: false, errorMsg: "" } });
		expect(screen.getByText("Uploading...")).toBeTruthy();
	});

	it("should render Image loaded text when isSuccess is true", () => {
		renderImageDragAndDrop({}, { gallery: { isUploading: false, isSuccess: true, isError: false, errorMsg: "" } });
		expect(screen.getByText("Image loaded!")).toBeTruthy();
	});

	it("should render error message when isError is true", () => {
		renderImageDragAndDrop(
			{},
			{ gallery: { isUploading: false, isSuccess: false, isError: true, errorMsg: "Upload failed" } },
		);
		expect(screen.getByText("Error uploading image")).toBeTruthy();
		expect(screen.getByText("Upload failed")).toBeTruthy();
	});
});

describe("ImageDragAndDrop crop flow", () => {
	it("should NOT render CropStep initially when withCrop is true", () => {
		renderImageDragAndDrop({ withCrop: true });
		expect(screen.queryByTestId("cropper-mock")).toBeFalsy();
	});

	it("should support withCrop prop - renders drag area for crop workflow", () => {
		renderImageDragAndDrop({ withCrop: true });
		// Verify component is rendered and ready for crop workflow
		const dragArea = screen.getByTestId("file-drag-and-drop-wrapper");
		expect(dragArea).toBeTruthy();
		// CropStep will appear after a valid file is selected and read as data URL
		expect(screen.queryByTestId("cropper-mock")).toBeFalsy();
	});
});

describe("ImageDragAndDrop file validation", () => {
	it("should accept multiple files when maxImages is not set", () => {
		renderImageDragAndDrop();
		const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
		expect(fileInput.multiple).toBe(true);
	});

	it("should not allow multiple files when maxImages is 1", () => {
		renderImageDragAndDrop({ maxImages: 1 });
		const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
		expect(fileInput.multiple).toBe(false);
	});

	it("should allow multiple files when maxImages is greater than 1", () => {
		renderImageDragAndDrop({ maxImages: 5 });
		const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
		expect(fileInput.multiple).toBe(true);
	});
});

describe("ImageDragAndDrop TRY AGAIN", () => {
	it("should render TRY AGAIN button", () => {
		renderImageDragAndDrop();
		expect(screen.getByText("TRY AGAIN")).toBeTruthy();
	});

	it("should be clickable", () => {
		renderImageDragAndDrop();
		const tryAgainButton = screen.getByText("TRY AGAIN");
		fireEvent.click(tryAgainButton);
		expect(tryAgainButton).toBeTruthy();
	});
});

describe("ImageDragAndDrop inverse theme", () => {
	it("should render with normal button style by default", () => {
		renderImageDragAndDrop();
		const selectButton = screen.getByText("Select images").closest("button");
		expect(selectButton?.className).not.toContain("inverse");
	});

	it("should render with inverse button style when inverse prop is true", () => {
		renderImageDragAndDrop({ inverse: true });
		const selectButton = screen.getByText("Select images").closest("button");
		expect(selectButton?.className).toContain("inverse");
	});
});
