import { within } from '@testing-library/vue';
import userEvent from '@testing-library/user-event';
import { createComponentRenderer } from '@/__tests__/render';
import { getDropdownItems, getSelectedDropdownValue } from '@/__tests__/utils';
import { createProjectListItem, createProjectSharingData } from '@/__tests__/data/projects';
import ProjectSharing from '@/components/Projects/ProjectSharing.vue';

const renderComponent = createComponentRenderer(ProjectSharing);

const personalProjects = Array.from({ length: 3 }, createProjectListItem);
const teamProjects = Array.from({ length: 3 }, () => createProjectListItem('team'));
const homeProject = createProjectSharingData();

describe('ProjectSharing', () => {
	it('should render empty select when projects is empty and no selected project existing', async () => {
		const { getByTestId, queryByTestId } = renderComponent({
			props: {
				projects: [],
				modelValue: [],
			},
		});

		expect(getByTestId('project-sharing-select')).toBeInTheDocument();
		expect(queryByTestId('project-sharing-list-item')).not.toBeInTheDocument();
		expect(queryByTestId('project-sharing-owner')).not.toBeInTheDocument();
	});

	it('should filter, add and remove projects', async () => {
		const { getByTestId, getAllByTestId, queryByTestId, queryAllByTestId, emitted } =
			renderComponent({
				props: {
					projects: personalProjects,
					modelValue: [personalProjects[0]],
					roles: [
						{
							role: 'project:admin',
							name: 'Admin',
						},
						{
							role: 'project:editor',
							name: 'Editor',
						},
					],
				},
			});

		expect(queryByTestId('project-sharing-owner')).not.toBeInTheDocument();
		// Check the initial state (one selected project comes from the modelValue prop)
		expect(getAllByTestId('project-sharing-list-item')).toHaveLength(1);

		const projectSelect = getByTestId('project-sharing-select');
		const projectSelectInput = projectSelect.querySelector('input') as HTMLInputElement;

		// Get the dropdown items
		let projectSelectDropdownItems = await getDropdownItems(projectSelect);
		expect(projectSelectDropdownItems).toHaveLength(2);

		// Add a project (first from the dropdown list)
		await userEvent.click(projectSelectDropdownItems[0]);
		expect(emitted()['update:modelValue']).toEqual([[[expect.any(Object), expect.any(Object)]]]);

		expect(getAllByTestId('project-sharing-list-item')).toHaveLength(2);
		expect(projectSelectInput.value).toBe('');
		projectSelectDropdownItems = await getDropdownItems(projectSelect);
		expect(projectSelectDropdownItems).toHaveLength(1);

		let actionDropDownItems = await getDropdownItems(
			getAllByTestId('project-sharing-list-item')[0],
		);
		expect(actionDropDownItems).toHaveLength(2);

		// Click on the remove button
		await userEvent.click(
			within(getAllByTestId('project-sharing-list-item')[0]).getByTestId('project-sharing-remove'),
		);
		expect(emitted()['update:modelValue']).toEqual([
			[[expect.any(Object), expect.any(Object)]],
			[[expect.any(Object)]],
		]);

		// Check the state
		expect(getAllByTestId('project-sharing-list-item')).toHaveLength(1);
		projectSelectDropdownItems = await getDropdownItems(projectSelect);
		expect(projectSelectDropdownItems).toHaveLength(2);

		actionDropDownItems = await getDropdownItems(getAllByTestId('project-sharing-list-item')[0]);
		expect(actionDropDownItems).toHaveLength(2);

		// Remove the last selected project
		await userEvent.click(
			within(getAllByTestId('project-sharing-list-item')[0]).getByTestId('project-sharing-remove'),
		);
		expect(emitted()['update:modelValue']).toEqual([
			[[expect.any(Object), expect.any(Object)]],
			[[expect.any(Object)]],
			[[]],
		]);

		// Check the final state
		expect(queryAllByTestId('project-sharing-list-item')).toHaveLength(0);
		projectSelectDropdownItems = await getDropdownItems(projectSelect);
		expect(projectSelectDropdownItems).toHaveLength(3);
	});

	it('should work as a simple select when model is not an array', async () => {
		const { getByTestId, queryByTestId, emitted } = renderComponent({
			props: {
				projects: teamProjects,
				modelValue: null,
			},
		});
		expect(queryByTestId('project-sharing-owner')).not.toBeInTheDocument();

		const projectSelect = getByTestId('project-sharing-select');

		// Get the dropdown items
		let projectSelectDropdownItems = await getDropdownItems(projectSelect);
		expect(projectSelectDropdownItems).toHaveLength(3);

		// Select the first project from the dropdown list
		await userEvent.click(projectSelectDropdownItems[0]);
		expect(queryByTestId('project-sharing-list-item')).not.toBeInTheDocument();
		projectSelectDropdownItems = await getDropdownItems(projectSelect);
		expect(projectSelectDropdownItems).toHaveLength(3);

		const selectedValue = await getSelectedDropdownValue(projectSelectDropdownItems);
		expect(selectedValue).toBeTruthy();
		expect(emitted()['update:modelValue']).toEqual([
			[
				expect.objectContaining({
					name: selectedValue,
				}),
			],
		]);

		// Select another project from the dropdown list
		await userEvent.click(projectSelectDropdownItems[1]);
		projectSelectDropdownItems = await getDropdownItems(projectSelect);
		expect(projectSelectDropdownItems).toHaveLength(3);
		const newSelectedValue = await getSelectedDropdownValue(projectSelectDropdownItems);
		expect(newSelectedValue).toBeTruthy();
		expect(emitted()['update:modelValue']).toEqual([
			expect.any(Array),
			[
				expect.objectContaining({
					name: newSelectedValue,
				}),
			],
		]);
	});

	it('should render home project as owner when defined', async () => {
		const { getByTestId, queryByTestId } = renderComponent({
			props: {
				projects: personalProjects,
				modelValue: [],
				homeProject,
			},
		});

		expect(getByTestId('project-sharing-select')).toBeInTheDocument();
		expect(queryByTestId('project-sharing-list-item')).not.toBeInTheDocument();
		expect(getByTestId('project-sharing-owner')).toBeInTheDocument();
	});
});
