import React, { useRef } from "react";

import { ThemeProvider } from "styled-components";
import { render, cleanup, screen, fireEvent, act } from "@testing-library/react";
import { parseTheme } from "@ax/helpers";
import "@testing-library/jest-dom";

import ResizePanel, { IResizePanelProps } from "@ax/components/ResizePanel";
import globalTheme from "@ax/themes/theme.json";

afterEach(cleanup);

beforeEach(() => {
	global.ResizeObserver = jest.fn().mockImplementation(() => ({
		observe: jest.fn(),
		unobserve: jest.fn(),
		disconnect: jest.fn(),
	}));
});

jest.mock("react", () => {
	const originReact = jest.requireActual("react");
	const mUseRef = jest.fn();
	return {
		...originReact,
		useRef: mUseRef,
	};
});

const useMockRef = useRef as jest.MockedFunction<typeof useRef>;

describe("ResizePanel component rendering", () => {
	it("should render the component with fixed panel", () => {
		const defaultProps: IResizePanelProps = {
			leftPanel: <div>Left Panel</div>,
			rightPanel: <div>Right Panel</div>,
		};

		const rightPanelRef = { current: {} };
		useMockRef.mockReturnValue(rightPanelRef);

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

		const leftPanel = screen.getByTestId("left-panel");
		expect(leftPanel).toBeTruthy();
		const fixedPanel = screen.getByTestId("fixed-panel");
		expect(fixedPanel).toBeTruthy();
		const rightPanel = screen.getByTestId("right-panel");
		expect(rightPanel).toBeTruthy();
	});

	it("should render the component with without fixed panel", () => {
		const defaultProps: IResizePanelProps = {
			leftPanel: <div>Left Panel</div>,
			rightPanel: <div>Right Panel</div>,
			fixed: false,
		};

		const rightPanelRef = { current: {} };
		useMockRef.mockReturnValueOnce(rightPanelRef);

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

		const leftPanel = screen.getByTestId("left-panel");
		expect(leftPanel).toBeTruthy();
		const fixedPanel = screen.queryByTestId("fixed-panel");
		expect(fixedPanel).toBeFalsy();
		const rightPanel = screen.getByTestId("right-panel");
		expect(rightPanel).toBeTruthy();
	});
});

describe("ResizePanel mouse events", () => {
	it("right panel should change width if handle moves past its width", () => {
		const defaultProps: IResizePanelProps = {
			leftPanel: <div>Left Panel</div>,
			rightPanel: <div>Right Panel</div>,
			fixed: true,
		};

		const windowWidth = 1440;
		const mousePositionX = 1000;

		Object.defineProperty(document.body, "offsetWidth", {
			value: windowWidth,
			configurable: true,
		});

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

		const rightPanel = screen.getByTestId("right-panel");
		expect(rightPanel).toBeTruthy();

		jest.spyOn(rightPanel, "offsetWidth", "get").mockReturnValue(344);

		// Forzar un re-render para que ResizePanel use el ancho mockeado
		// (React mide en requestAnimationFrame, así que usamos act)
		act(() => {});

		const handler = screen.getByTestId("handler");
		expect(handler).toBeTruthy();

		fireEvent.mouseDown(handler);

		fireEvent.mouseMove(window, { clientX: mousePositionX, clientY: 100 });

		fireEvent.mouseUp(window);

		const width = getComputedStyle(rightPanel).width;
		expect(width).toBe(`${windowWidth - mousePositionX}px`);
	});

	it("right panel shouldn't change width if handle not moves past its width", () => {
		const defaultProps: IResizePanelProps = {
			leftPanel: <div>Left Panel</div>,
			rightPanel: <div>Right Panel</div>,
			fixed: true,
		};

		const windowWidth = 1440;

		Object.defineProperty(document.body, "offsetWidth", {
			value: windowWidth,
			configurable: true,
		});

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

		const rightPanel = screen.getByTestId("right-panel");
		expect(rightPanel).toBeTruthy();

		jest.spyOn(rightPanel, "offsetWidth", "get").mockReturnValue(344);

		// Forzar un re-render para que ResizePanel use el ancho mockeado
		// (React mide en requestAnimationFrame, así que usamos act)
		act(() => {});

		const handler = screen.getByTestId("handler");
		expect(handler).toBeTruthy();

		fireEvent.mouseDown(handler);
		fireEvent.mouseMove(window, { clientX: 1200, clientY: 100 });
		fireEvent.mouseUp(window);

		const width = getComputedStyle(rightPanel).width;
		expect(width).toBe("368px");
	});
});
