import { Builder, WebDriver } from 'selenium-webdriver';
import { TestAppConfig } from './selenium.conf';
import { OpenViduComponentsPO } from './utils.po.test';

const url = TestAppConfig.appUrl;

describe('E2E: Screensharing features', () => {
	let browser: WebDriver;
	let utils: OpenViduComponentsPO;

	async function createChromeBrowser(): Promise<WebDriver> {
		return await new Builder()
			.forBrowser(TestAppConfig.browserName)
			.withCapabilities(TestAppConfig.browserCapabilities)
			.setChromeOptions(TestAppConfig.browserOptions)
			.usingServer(TestAppConfig.seleniumAddress)
			.build();
	}

	beforeEach(async () => {
		browser = await createChromeBrowser();
		utils = new OpenViduComponentsPO(browser);
	});

	afterEach(async () => {
		try {
			await utils.leaveRoom();
		} catch (error) {}
		await browser.quit();
	});

	it('should toggle screensharing on and off twice, updating video count', async () => {
		await browser.get(`${url}&prejoin=false`);
		await utils.checkLayoutPresent();

		// Enable screensharing
		await utils.waitForElement('#screenshare-btn');
		await utils.clickOn('#screenshare-btn');
		await browser.sleep(500);
		await utils.waitForElement('.OV_big');
		expect(await utils.getNumberOfElements('video')).toEqual(2);

		// Disable screensharing
		await utils.disableScreenShare();
		expect(await utils.getNumberOfElements('video')).toEqual(1);

		// Enable again
		await utils.clickOn('#screenshare-btn');
		await browser.sleep(500);
		await utils.waitForElement('.OV_big');
		expect(await utils.getNumberOfElements('video')).toEqual(2);

		// Disable again
		await utils.disableScreenShare();
		expect(await utils.getNumberOfElements('video')).toEqual(1);
	});

	it('should show screenshare and muted camera (camera off, screenshare on)', async () => {
		await browser.get(`${url}&prejoin=false`);
		await utils.checkLayoutPresent();

		// Mute camera
		await utils.waitForElement('#camera-btn');
		await utils.clickOn('#camera-btn');

		// Enable screensharing
		const screenshareButton = await utils.waitForElement('#screenshare-btn');
		expect(await screenshareButton.isDisplayed()).toBeTrue();
		await screenshareButton.click();
		await browser.sleep(500);
		await utils.waitForElement('.OV_big');
		expect(await utils.getNumberOfElements('video')).toEqual(2);

		// Disable screensharing
		await utils.disableScreenShare();
		expect(await utils.getNumberOfElements('video')).toEqual(1);
	});

	it('should display screensharing with a single pinned video', async () => {
		await browser.get(`${url}&prejoin=false`);
		await utils.checkLayoutPresent();

		// Enable screensharing
		const screenshareButton = await utils.waitForElement('#screenshare-btn');
		expect(await screenshareButton.isDisplayed()).toBeTrue();
		await screenshareButton.click();
		await utils.waitForElement('.OV_big');
		expect(await utils.getNumberOfElements('.OV_big')).toEqual(1);
	});

	it('should replace pinned video when a second participant starts screensharing', async () => {
		const roomName = 'screensharingE2E';
		const fixedUrl = `${url}&roomName=${roomName}&prejoin=false`;
		await browser.get(fixedUrl);
		await utils.checkLayoutPresent();

		// First participant screenshares
		await utils.waitForElement('#screenshare-btn');
		await utils.clickOn('#screenshare-btn');
		await utils.waitForElement('.OV_big');
		expect(await utils.getNumberOfElements('.OV_big')).toEqual(1);

		// Second participant joins and screenshares
		const newTabScript = `window.open("${fixedUrl}")`;
		await browser.executeScript(newTabScript);
		const tabs = await browser.getAllWindowHandles();
		await browser.switchTo().window(tabs[1]);
		await utils.checkLayoutPresent();
		await utils.waitForElement('#screenshare-btn');
		await utils.clickOn('#screenshare-btn');
		await browser.sleep(500);
		expect(await utils.getNumberOfElements('video')).toEqual(4);
		await utils.waitForElement('.OV_big');
		expect(await utils.getNumberOfElements('.OV_big')).toEqual(1);

		// Switch back to first tab and check
		await browser.switchTo().window(tabs[0]);
		await browser.sleep(500);
		expect(await utils.getNumberOfElements('video')).toEqual(4);
		await utils.waitForElement('.OV_big');
		expect(await utils.getNumberOfElements('.OV_big')).toEqual(1);
	});

	it('should unpin screensharing and restore previous pinned video when disabled', async () => {
		const roomName = 'screensharingtwoE2E';
		const fixedUrl = `${url}&roomName=${roomName}&prejoin=false`;
		await browser.get(fixedUrl);
		await utils.checkLayoutPresent();

		// First participant screenshares
		await utils.waitForElement('#screenshare-btn');
		await utils.clickOn('#screenshare-btn');
		await browser.sleep(500);
		await utils.waitForElement('.OV_big');
		expect(await utils.getNumberOfElements('.OV_big')).toEqual(1);

		// Second participant joins and screenshares
		const tabs = await utils.openTab(fixedUrl);
		await browser.switchTo().window(tabs[1]);
		await utils.checkLayoutPresent();
		await utils.waitForElement('#screenshare-btn');
		await utils.clickOn('#screenshare-btn');
		await browser.sleep(500);
		expect(await utils.getNumberOfElements('video')).toEqual(4);
		await utils.waitForElement('.OV_big');
		expect(await utils.getNumberOfElements('.OV_big')).toEqual(1);

		// Disable screensharing for second participant
		await utils.disableScreenShare();
		expect(await utils.getNumberOfElements('video')).toEqual(3);
		await utils.waitForElement('.OV_big');
		expect(await utils.getNumberOfElements('.OV_big')).toEqual(1);

		// Switch back to first tab and check
		await browser.switchTo().window(tabs[0]);
		await browser.sleep(500);
		expect(await utils.getNumberOfElements('video')).toEqual(3);
		await utils.waitForElement('.OV_big');
		expect(await utils.getNumberOfElements('.OV_big')).toEqual(1);
	});

	it('should correctly share screen with microphone muted and maintain proper track state', async () => {
		// Helper for inspecting stream tracks
		const getMediaTracks = (className: string) => {
			return `
			const tracks = document.getElementsByClassName('${className}')[0].srcObject.getTracks();
			return tracks.map(track => ({
				kind: track.kind,
				enabled: track.enabled,
				id: track.id,
				label: track.label
			}));`;
		};

		// Setup: Navigate to room and skip prejoin
		await browser.get(`${url}&prejoin=false`);
		await utils.checkLayoutPresent();

		// Step 1: First mute the microphone
		const micButton = await utils.waitForElement('#mic-btn');
		await micButton.click();

		// Step 2: Start screen sharing
		await utils.clickOn('#screenshare-btn');

		// Step 3: Verify both streams are present
		await utils.waitForElement('.screen-type');
		expect(await utils.getNumberOfElements('video')).toEqual(2);

		// Step 4: Verify screen share track properties
		const screenTracks: any[] = await browser.executeScript(getMediaTracks('screen-type'));
		expect(screenTracks.length).toEqual(1);
		expect(screenTracks[0].kind).toEqual('video');
		expect(screenTracks[0].enabled).toBeTrue();

		// Step 5: Verify microphone status indicators for both streams
		// await utils.waitForElement('#status-mic');
		// const micStatusCount = await utils.getNumberOfElements('#status-mic');
		// expect(micStatusCount).toEqual(2);

		// Step 6: Stop screen sharing and verify stream count
		await utils.clickOn('#screenshare-btn');
		await browser.sleep(500);
		await utils.clickOn('#disable-screen-button');
		await browser.sleep(500);
		expect(await utils.getNumberOfElements('video')).toEqual(1);
	});

	// ==================== PIN/UNPIN TESTS ====================
	// These tests demonstrate bugs in the pin system:
	// 1. Multiple screens can be auto-pinned simultaneously
	// 2. Manual unpins can be overridden by auto-pin logic when participants join

	it('should NOT have multiple screens pinned when both participants share screen', async () => {
		const roomName = 'pinBugCase1';
		const fixedUrl = `${url}&roomName=${roomName}&prejoin=false`;

		// Participant A joins and shares screen
		await browser.get(fixedUrl);
		await utils.checkLayoutPresent();
		await utils.waitForElement('#screenshare-btn');
		await utils.clickOn('#screenshare-btn');
		await browser.sleep(500);

		// Verify A's screen is pinned
		await utils.waitForElement('.OV_big');
		expect(await utils.getNumberOfPinnedStreams()).toEqual(1);
		const pinnedCountA1 = await utils.getNumberOfPinnedStreams();
		console.log(`[Tab A] After A shares: ${pinnedCountA1} pinned stream(s)`);

		// Participant B joins
		const tabs = await utils.openTab(fixedUrl);
		await browser.switchTo().window(tabs[1]);
		await utils.checkLayoutPresent();
		await browser.sleep(1000);

		// B should see A's screen pinned
		expect(await utils.getNumberOfElements('video')).toEqual(3); // 2 cameras + 1 screen
		expect(await utils.getNumberOfPinnedStreams()).toEqual(1);
		const pinnedCountB1 = await utils.getNumberOfPinnedStreams();
		console.log(`[Tab B] After B joins: ${pinnedCountB1} pinned stream(s)`);

		// B shares screen
		await utils.waitForElement('#screenshare-btn');
		await utils.clickOn('#screenshare-btn');
		await browser.sleep(500);

		// B should see only their own screen pinned (auto-pin + unpin previous)
		expect(await utils.getNumberOfElements('video')).toEqual(4); // 2 cameras + 2 screens
		await utils.waitForElement('.OV_big');
		const pinnedCountB2 = await utils.getNumberOfPinnedStreams();
		console.log(`[Tab B] After B shares: ${pinnedCountB2} pinned stream(s)`);
		expect(pinnedCountB2).toEqual(1); // Should be 1, but implementation might show different

		// Switch to Tab A and check
		await browser.switchTo().window(tabs[0]);
		await browser.sleep(1000);
		expect(await utils.getNumberOfElements('video')).toEqual(4); // 2 cameras + 2 screens

		// BUG: In A's view, BOTH screens are pinned
		const pinnedCountA2 = await utils.getNumberOfPinnedStreams();
		console.log(`[Tab A] After B shares: ${pinnedCountA2} pinned stream(s)`);

		// EXPECTED: Only B's screen should be pinned (the most recent one)
		// ACTUAL: Both A's and B's screens are pinned
		expect(pinnedCountA2).toEqual(1, 'BUG DETECTED: Multiple screens are pinned. Expected only the most recent screen to be pinned.');
	});

	it('should NOT re-pin manually unpinned screen when new participant joins', async () => {
		const roomName = 'pinBugCase2';
		const fixedUrl = `${url}&roomName=${roomName}&prejoin=false`;

		// Participant A joins and shares screen
		await browser.get(fixedUrl);
		await utils.checkLayoutPresent();
		await utils.waitForElement('#screenshare-btn');
		await utils.clickOn('#screenshare-btn');
		await browser.sleep(500);

		// Verify A's screen is auto-pinned
		await utils.waitForElement('.OV_big');
		expect(await utils.getNumberOfPinnedStreams()).toEqual(1);

		// Participant B joins and shares screen
		const tabs = await utils.openTab(fixedUrl);
		await browser.switchTo().window(tabs[1]);
		await utils.checkLayoutPresent();
		await browser.sleep(1000);
		await utils.waitForElement('#screenshare-btn');
		await utils.clickOn('#screenshare-btn');
		await browser.sleep(500);

		// B should see their own screen pinned
		expect(await utils.getNumberOfElements('video')).toEqual(4); // 2 cameras + 2 screens
		await utils.waitForElement('.OV_big');
		let pinnedCountB = await utils.getNumberOfPinnedStreams();
		console.log(`[Tab B] After B shares: ${pinnedCountB} pinned stream(s)`);

		// B manually unpins their own screen
		const screenStreams = await utils.getScreenShareStreams();
		if (screenStreams.length > 0) {
			// Find B's own screen (it should be the pinned one)
			await utils.toggleStreamPin('.OV_big');
			await browser.sleep(1000);
		}

		// Verify B's screen is now unpinned
		pinnedCountB = await utils.getNumberOfPinnedStreams();
		console.log(`[Tab B] After manually unpinning B's screen: ${pinnedCountB} pinned stream(s)`);
		expect(pinnedCountB).toEqual(0, 'B should have no pinned streams after manual unpin');

		// B manually pins A's screen
		const screenElements = await utils.getScreenShareStreams();
		if (screenElements.length >= 2) {
			// Pin the first screen that is not already pinned (should be A's screen)
			await utils.toggleStreamPin('.OV_stream.remote .screen-type');
			await utils.toggleStreamPin('#pin-btn');
			await browser.sleep(500);
		}

		// Verify A's screen is now pinned in B's view
		pinnedCountB = await utils.getNumberOfPinnedStreams();
		console.log(`[Tab B] After manually pinning A's screen: ${pinnedCountB} pinned stream(s)`);
		expect(pinnedCountB).toEqual(1, "Only A's screen should be pinned");

		// Participant C joins the room
		const tab3 = await utils.openTab(fixedUrl);
		await browser.switchTo().window(tab3[2]);
		await utils.checkLayoutPresent();
		await browser.sleep(1500);

		// Switch back to B's tab
		await browser.switchTo().window(tabs[1]);
		await browser.sleep(1000);

		// B's screen should still be unpinned, but might get re-pinned automatically
		pinnedCountB = await utils.getNumberOfPinnedStreams();
		console.log(`[Tab B] After C joins: ${pinnedCountB} pinned stream(s)`);

		// EXPECTED: No screens should be pinned (B manually unpinned everything)
		// ACTUAL: B's screen gets re-pinned automatically
		expect(pinnedCountB).toEqual(1, 'BUG DETECTED: Only one screen should be pinned after C joins.');

		// Switch back to A's tab to verify
		await browser.switchTo().window(tabs[0]);
		await browser.sleep(500);

		const pinnedCountA2 = await utils.getNumberOfPinnedStreams();
		console.log(`[Tab A] After C joins: ${pinnedCountA2} pinned stream(s)`);

		// EXPECTED: Only A's screen should be pinned
		// ACTUAL: A's screen remains pinned
		expect(pinnedCountA2).toEqual(1, "BUG DETECTED: A's screen should remain pinned after C joins.");
	});
});
