/**
 * Unit test to ensure fixDependenciesCommand writes JSON with the correct
 * projectRoot and tag arguments so that tag data is preserved.
 */

import { jest } from '@jest/globals';

// Mock process.exit to prevent test termination
const mockProcessExit = jest.fn();
const originalExit = process.exit;
process.exit = mockProcessExit;

// Mock utils.js BEFORE importing the module under test
jest.unstable_mockModule('../../../../../scripts/modules/utils.js', () => ({
	readJSON: jest.fn(),
	writeJSON: jest.fn(),
	log: jest.fn(),
	findProjectRoot: jest.fn(() => '/mock/project/root'),
	getCurrentTag: jest.fn(() => 'master'),
	taskExists: jest.fn(() => true),
	formatTaskId: jest.fn((id) => id),
	findCycles: jest.fn(() => []),
	traverseDependencies: jest.fn((sourceTasks, allTasks, options = {}) => []),
	isSilentMode: jest.fn(() => true),
	resolveTag: jest.fn(() => 'master'),
	getTasksForTag: jest.fn(() => []),
	setTasksForTag: jest.fn(),
	enableSilentMode: jest.fn(),
	disableSilentMode: jest.fn(),
	isEmpty: jest.fn((value) => {
		if (value === null || value === undefined) return true;
		if (Array.isArray(value)) return value.length === 0;
		if (typeof value === 'object' && value !== null)
			return Object.keys(value).length === 0;
		return false; // Not an array or object
	}),
	resolveEnvVariable: jest.fn()
}));

// Mock ui.js
jest.unstable_mockModule('../../../../../scripts/modules/ui.js', () => ({
	displayBanner: jest.fn(),
	formatDependenciesWithStatus: jest.fn()
}));

// Mock task-manager.js
jest.unstable_mockModule(
	'../../../../../scripts/modules/task-manager.js',
	() => ({
		generateTaskFiles: jest.fn()
	})
);

// Mock external libraries
jest.unstable_mockModule('chalk', () => ({
	default: {
		green: jest.fn((text) => text),
		cyan: jest.fn((text) => text),
		bold: jest.fn((text) => text)
	}
}));

jest.unstable_mockModule('boxen', () => ({
	default: jest.fn((text) => text)
}));

// Import the mocked modules
const { readJSON, writeJSON, log, taskExists } = await import(
	'../../../../../scripts/modules/utils.js'
);

// Import the module under test
const { fixDependenciesCommand } = await import(
	'../../../../../scripts/modules/dependency-manager.js'
);

describe('fixDependenciesCommand tag preservation', () => {
	beforeEach(() => {
		jest.clearAllMocks();
		mockProcessExit.mockClear();
	});

	afterAll(() => {
		// Restore original process.exit
		process.exit = originalExit;
	});

	it('calls writeJSON with projectRoot and tag parameters when changes are made', async () => {
		const tasksPath = '/mock/tasks.json';
		const projectRoot = '/mock/project/root';
		const tag = 'master';

		// Mock data WITH dependency issues to trigger writeJSON
		const tasksDataWithIssues = {
			tasks: [
				{
					id: 1,
					title: 'Task 1',
					dependencies: [999] // Non-existent dependency to trigger fix
				},
				{
					id: 2,
					title: 'Task 2',
					dependencies: []
				}
			],
			tag: 'master',
			_rawTaggedData: {
				master: {
					tasks: [
						{
							id: 1,
							title: 'Task 1',
							dependencies: [999]
						}
					]
				}
			}
		};

		readJSON.mockReturnValue(tasksDataWithIssues);
		taskExists.mockReturnValue(false); // Make dependency invalid to trigger fix

		await fixDependenciesCommand(tasksPath, {
			context: { projectRoot, tag }
		});

		// Verify readJSON was called with correct parameters
		expect(readJSON).toHaveBeenCalledWith(tasksPath, projectRoot, tag);

		// Verify writeJSON was called (should be triggered by removing invalid dependency)
		expect(writeJSON).toHaveBeenCalled();

		// Check the writeJSON call parameters
		const writeJSONCalls = writeJSON.mock.calls;
		const lastWriteCall = writeJSONCalls[writeJSONCalls.length - 1];
		const [calledPath, _data, calledProjectRoot, calledTag] = lastWriteCall;

		expect(calledPath).toBe(tasksPath);
		expect(calledProjectRoot).toBe(projectRoot);
		expect(calledTag).toBe(tag);

		// Verify process.exit was NOT called (meaning the function succeeded)
		expect(mockProcessExit).not.toHaveBeenCalled();
	});

	it('does not call writeJSON when no changes are needed', async () => {
		const tasksPath = '/mock/tasks.json';
		const projectRoot = '/mock/project/root';
		const tag = 'master';

		// Mock data WITHOUT dependency issues (no changes needed)
		const cleanTasksData = {
			tasks: [
				{
					id: 1,
					title: 'Task 1',
					dependencies: [] // Clean, no issues
				}
			],
			tag: 'master'
		};

		readJSON.mockReturnValue(cleanTasksData);
		taskExists.mockReturnValue(true); // All dependencies exist

		await fixDependenciesCommand(tasksPath, {
			context: { projectRoot, tag }
		});

		// Verify readJSON was called
		expect(readJSON).toHaveBeenCalledWith(tasksPath, projectRoot, tag);

		// Verify writeJSON was NOT called (no changes needed)
		expect(writeJSON).not.toHaveBeenCalled();

		// Verify process.exit was NOT called
		expect(mockProcessExit).not.toHaveBeenCalled();
	});

	it('handles early exit when no valid tasks found', async () => {
		const tasksPath = '/mock/tasks.json';

		// Mock invalid data to trigger early exit
		readJSON.mockReturnValue(null);

		await fixDependenciesCommand(tasksPath, {
			context: { projectRoot: '/mock', tag: 'master' }
		});

		// Verify readJSON was called
		expect(readJSON).toHaveBeenCalled();

		// Verify writeJSON was NOT called (early exit)
		expect(writeJSON).not.toHaveBeenCalled();

		// Verify process.exit WAS called due to invalid data
		expect(mockProcessExit).toHaveBeenCalledWith(1);
	});
});
