/**
 * Tests for the removeSubtask function
 */
import { jest } from '@jest/globals';
import path from 'path';

// Mock dependencies
const mockReadJSON = jest.fn();
const mockWriteJSON = jest.fn();
const mockGenerateTaskFiles = jest.fn();

// Mock path module
jest.mock('path', () => ({
	dirname: jest.fn()
}));

// Define test version of the removeSubtask function
const testRemoveSubtask = (
	tasksPath,
	subtaskId,
	convertToTask = false,
	generateFiles = true,
	context = { tag: 'master' }
) => {
	const { projectRoot = undefined, tag = 'master' } = context;
	// Read the existing tasks
	const data = mockReadJSON(tasksPath, projectRoot, tag);
	if (!data || !data.tasks) {
		throw new Error(`Invalid or missing tasks file at ${tasksPath}`);
	}

	// Parse the subtask ID (format: "parentId.subtaskId")
	if (!subtaskId.includes('.')) {
		throw new Error(`Invalid subtask ID format: ${subtaskId}`);
	}

	const [parentIdStr, subtaskIdStr] = subtaskId.split('.');
	const parentId = parseInt(parentIdStr, 10);
	const subtaskIdNum = parseInt(subtaskIdStr, 10);

	// Find the parent task
	const parentTask = data.tasks.find((t) => t.id === parentId);
	if (!parentTask) {
		throw new Error(`Parent task with ID ${parentId} not found`);
	}

	// Check if parent has subtasks
	if (!parentTask.subtasks || parentTask.subtasks.length === 0) {
		throw new Error(`Parent task ${parentId} has no subtasks`);
	}

	// Find the subtask to remove
	const subtaskIndex = parentTask.subtasks.findIndex(
		(st) => st.id === subtaskIdNum
	);
	if (subtaskIndex === -1) {
		throw new Error(`Subtask ${subtaskId} not found`);
	}

	// Get a copy of the subtask before removing it
	const removedSubtask = { ...parentTask.subtasks[subtaskIndex] };

	// Remove the subtask from the parent
	parentTask.subtasks.splice(subtaskIndex, 1);

	// If parent has no more subtasks, remove the subtasks array
	if (parentTask.subtasks.length === 0) {
		delete parentTask.subtasks;
	}

	let convertedTask = null;

	// Convert the subtask to a standalone task if requested
	if (convertToTask) {
		// Find the highest task ID to determine the next ID
		const highestId = Math.max(...data.tasks.map((t) => t.id));
		const newTaskId = highestId + 1;

		// Create the new task from the subtask
		convertedTask = {
			id: newTaskId,
			title: removedSubtask.title,
			description: removedSubtask.description || '',
			details: removedSubtask.details || '',
			status: removedSubtask.status || 'pending',
			dependencies: removedSubtask.dependencies || [],
			priority: parentTask.priority || 'medium' // Inherit priority from parent
		};

		// Add the parent task as a dependency if not already present
		if (!convertedTask.dependencies.includes(parentId)) {
			convertedTask.dependencies.push(parentId);
		}

		// Add the converted task to the tasks array
		data.tasks.push(convertedTask);
	}

	// Write the updated tasks back to the file
	mockWriteJSON(tasksPath, data, projectRoot, tag);

	// Generate task files if requested
	if (generateFiles) {
		mockGenerateTaskFiles(tasksPath, path.dirname(tasksPath));
	}

	return convertedTask;
};

describe('removeSubtask function', () => {
	// Reset mocks before each test
	beforeEach(() => {
		jest.clearAllMocks();

		// Default mock implementations
		mockReadJSON.mockImplementation((p, root, tag) => {
			expect(tag).toBeDefined();
			expect(tag).toBe('master');
			return {
				tasks: [
					{
						id: 1,
						title: 'Parent Task',
						description: 'This is a parent task',
						status: 'pending',
						dependencies: [],
						subtasks: [
							{
								id: 1,
								title: 'Subtask 1',
								description: 'This is subtask 1',
								status: 'pending',
								dependencies: [],
								parentTaskId: 1
							},
							{
								id: 2,
								title: 'Subtask 2',
								description: 'This is subtask 2',
								status: 'in-progress',
								dependencies: [1], // Depends on subtask 1
								parentTaskId: 1
							}
						]
					},
					{
						id: 2,
						title: 'Another Task',
						description: 'This is another task',
						status: 'pending',
						dependencies: [1]
					}
				]
			};
		});

		// Setup success write response
		mockWriteJSON.mockImplementation((path, data, root, tag) => {
			expect(tag).toBe('master');
			return data;
		});
	});

	test('should remove a subtask from its parent task', async () => {
		// Execute the test version of removeSubtask to remove subtask 1.1
		testRemoveSubtask('tasks/tasks.json', '1.1', false, true, {
			tag: 'master'
		});

		// Verify readJSON was called with the correct path
		expect(mockReadJSON).toHaveBeenCalledWith(
			'tasks/tasks.json',
			undefined,
			'master'
		);

		// Verify writeJSON was called with updated data
		expect(mockWriteJSON).toHaveBeenCalled();

		// Verify generateTaskFiles was called
		// expect(mockGenerateTaskFiles).toHaveBeenCalled();
	});

	test('should convert a subtask to a standalone task', async () => {
		// Execute the test version of removeSubtask to convert subtask 1.1 to a standalone task
		const result = testRemoveSubtask('tasks/tasks.json', '1.1', true, true, {
			tag: 'master'
		});

		// Verify the result is the new task
		expect(result).toBeDefined();
		expect(result.id).toBe(3);
		expect(result.title).toBe('Subtask 1');
		expect(result.dependencies).toContain(1);

		// Verify writeJSON was called
		expect(mockWriteJSON).toHaveBeenCalled();

		// Verify generateTaskFiles was called
		// expect(mockGenerateTaskFiles).toHaveBeenCalled();
	});

	test('should throw an error if subtask ID format is invalid', async () => {
		// Expect an error for invalid subtask ID format
		expect(() =>
			testRemoveSubtask('tasks/tasks.json', '1', false, true, { tag: 'master' })
		).toThrow(/Invalid subtask ID format/);

		// Verify writeJSON was not called
		expect(mockWriteJSON).not.toHaveBeenCalled();
	});

	test('should throw an error if parent task does not exist', async () => {
		// Expect an error for non-existent parent task
		expect(() =>
			testRemoveSubtask('tasks/tasks.json', '999.1', false, true, {
				tag: 'master'
			})
		).toThrow(/Parent task with ID 999 not found/);

		// Verify writeJSON was not called
		expect(mockWriteJSON).not.toHaveBeenCalled();
	});

	test('should throw an error if subtask does not exist', async () => {
		// Expect an error for non-existent subtask
		expect(() =>
			testRemoveSubtask('tasks/tasks.json', '1.999', false, true, {
				tag: 'master'
			})
		).toThrow(/Subtask 1.999 not found/);

		// Verify writeJSON was not called
		expect(mockWriteJSON).not.toHaveBeenCalled();
	});

	test('should remove subtasks array if last subtask is removed', async () => {
		// Create a data object with just one subtask
		mockReadJSON.mockImplementationOnce((p, root, tag) => {
			expect(tag).toBe('master');
			return {
				tasks: [
					{
						id: 1,
						title: 'Parent Task',
						description: 'This is a parent task',
						status: 'pending',
						dependencies: [],
						subtasks: [
							{
								id: 1,
								title: 'Last Subtask',
								description: 'This is the last subtask',
								status: 'pending',
								dependencies: [],
								parentTaskId: 1
							}
						]
					},
					{
						id: 2,
						title: 'Another Task',
						description: 'This is another task',
						status: 'pending',
						dependencies: [1]
					}
				]
			};
		});

		// Mock the behavior of writeJSON to capture the updated tasks data
		const updatedTasksData = { tasks: [] };
		mockWriteJSON.mockImplementation((path, data, root, tag) => {
			expect(tag).toBe('master');
			// Store the data for assertions
			updatedTasksData.tasks = [...data.tasks];
			return data;
		});

		// Remove the last subtask
		testRemoveSubtask('tasks/tasks.json', '1.1', false, true, {
			tag: 'master'
		});

		// Verify writeJSON was called
		expect(mockWriteJSON).toHaveBeenCalled();

		// Verify the subtasks array was removed completely
		const parentTask = updatedTasksData.tasks.find((t) => t.id === 1);
		expect(parentTask).toBeDefined();
		expect(parentTask.subtasks).toBeUndefined();

		// Verify generateTaskFiles was called
		// expect(mockGenerateTaskFiles).toHaveBeenCalled();
	});

	test('should not regenerate task files if generateFiles is false', async () => {
		// Execute the test version of removeSubtask with generateFiles = false
		testRemoveSubtask('tasks/tasks.json', '1.1', false, false, {
			tag: 'master'
		});

		// Verify writeJSON was called
		expect(mockWriteJSON).toHaveBeenCalled();

		// Verify task files were not regenerated
		expect(mockGenerateTaskFiles).not.toHaveBeenCalled();
	});
});
