import type { RouteHandler } from 'cypress/types/net-stubbing';

import executionOutOfMemoryServerResponse from '../fixtures/responses/execution-out-of-memory-server-response.json';
import { WorkflowPage } from '../pages';
import { WorkflowExecutionsTab } from '../pages/workflow-executions-tab';
import { getVisibleSelect } from '../utils';

const workflowPage = new WorkflowPage();
const executionsTab = new WorkflowExecutionsTab();
const executionsRefreshInterval = 4000;

// Test suite for executions tab
describe('Workflow Executions', () => {
	describe('when workflow is saved', () => {
		beforeEach(() => {
			workflowPage.actions.visit();
			cy.createFixtureWorkflow('Test_workflow_4_executions_view.json', 'My test workflow');
		});

		it('should render executions tab correctly', () => {
			createMockExecutions();
			cy.intercept('GET', '/rest/executions?filter=*').as('getExecutions');

			executionsTab.actions.switchToExecutionsTab();

			cy.wait(['@getExecutions']);

			executionsTab.getters.executionsList().scrollTo(0, 500).wait(0);

			executionsTab.getters.executionListItems().should('have.length', 11);
			executionsTab.getters.successfulExecutionListItems().should('have.length', 9);
			executionsTab.getters.failedExecutionListItems().should('have.length', 2);
			executionsTab.getters
				.executionListItems()
				.first()
				.invoke('attr', 'class')
				.should('match', /_active_/);
		});

		it('should not redirect back to execution tab when request is not done before leaving the page', () => {
			cy.intercept('GET', '/rest/executions?filter=*');
			cy.intercept('GET', '/rest/executions/active?filter=*');

			executionsTab.actions.switchToExecutionsTab();
			executionsTab.actions.switchToEditorTab();
			cy.wait(executionsRefreshInterval);
			cy.url().should('not.include', '/executions');
			executionsTab.actions.switchToExecutionsTab();
			executionsTab.actions.switchToEditorTab();
			executionsTab.actions.switchToExecutionsTab();
			executionsTab.actions.switchToEditorTab();
			executionsTab.actions.switchToExecutionsTab();
			executionsTab.actions.switchToEditorTab();
			cy.wait(executionsRefreshInterval);
			cy.url().should('not.include', '/executions');
			executionsTab.actions.switchToExecutionsTab();
			cy.wait(1000);
			executionsTab.actions.switchToEditorTab();
			cy.wait(executionsRefreshInterval);
			cy.url().should('not.include', '/executions');
		});

		it('should not redirect back to execution tab when slow request is not done before leaving the page', () => {
			const throttleResponse: RouteHandler = async (req) => {
				return await new Promise((resolve) => {
					setTimeout(() => resolve(req.continue()), 2000);
				});
			};

			cy.intercept('GET', '/rest/executions?filter=*', throttleResponse);
			cy.intercept('GET', '/rest/executions/active?filter=*', throttleResponse);

			executionsTab.actions.switchToExecutionsTab();
			executionsTab.actions.switchToEditorTab();
			cy.wait(executionsRefreshInterval);
			cy.url().should('not.include', '/executions');
		});

		it('should error toast when server error message returned without stack trace', () => {
			executionsTab.actions.createManualExecutions(1);
			const message = 'Workflow did not finish, possible out-of-memory issue';
			cy.intercept('GET', '/rest/executions/*', {
				statusCode: 200,
				body: executionOutOfMemoryServerResponse,
			}).as('getExecution');

			executionsTab.actions.switchToExecutionsTab();
			cy.wait(['@getExecution']);

			executionsTab.getters
				.workflowExecutionPreviewIframe()
				.should('be.visible')
				.its('0.contentDocument.body') // Access the body of the iframe document
				.should('not.be.empty') // Ensure the body is not empty

				.then(cy.wrap)
				.find('.el-notification:has(.el-notification--error)')
				.should('be.visible')
				.filter(`:contains("${message}")`)
				.should('be.visible');
		});

		it('should show workflow data in executions tab after hard reload and modify name and tags', () => {
			executionsTab.actions.switchToExecutionsTab();
			checkMainHeaderELements();
			workflowPage.getters.saveButton().find('button').should('not.exist');
			workflowPage.getters.tagPills().should('have.length', 2);

			workflowPage.getters.workflowTags().click();
			getVisibleSelect().find('li:contains("Manage tags")').click();
			cy.get('button:contains("Add new")').click();
			cy.getByTestId('tags-table').find('input').type('nutag').type('{enter}');
			cy.get('button:contains("Done")').click();

			cy.reload();
			checkMainHeaderELements();
			workflowPage.getters.saveButton().find('button').should('not.exist');
			workflowPage.getters.workflowTags().click();
			workflowPage.getters.tagsInDropdown().first().should('have.text', 'nutag').click();
			workflowPage.getters.tagPills().should('have.length', 3);

			let newWorkflowName = 'Renamed workflow';
			workflowPage.actions.renameWorkflow(newWorkflowName);
			workflowPage.getters.isWorkflowSaved();
			workflowPage.getters
				.workflowNameInputContainer()
				.invoke('attr', 'title')
				.should('eq', newWorkflowName);

			executionsTab.actions.switchToEditorTab();
			checkMainHeaderELements();
			workflowPage.getters.saveButton().find('button').should('not.exist');
			workflowPage.getters.tagPills().should('have.length', 3);
			workflowPage.getters
				.workflowNameInputContainer()
				.invoke('attr', 'title')
				.should('eq', newWorkflowName);

			executionsTab.actions.switchToExecutionsTab();
			checkMainHeaderELements();
			workflowPage.getters.saveButton().find('button').should('not.exist');
			workflowPage.getters.tagPills().should('have.length', 3);
			workflowPage.getters
				.workflowNameInputContainer()
				.invoke('attr', 'title')
				.should('eq', newWorkflowName);

			executionsTab.actions.switchToEditorTab();
			checkMainHeaderELements();
			workflowPage.getters.saveButton().find('button').should('not.exist');
			workflowPage.getters.tagPills().should('have.length', 3);
			workflowPage.getters
				.workflowNameInputContainer()
				.invoke('attr', 'title')
				.should('eq', newWorkflowName);

			newWorkflowName = 'New workflow';
			workflowPage.actions.renameWorkflow(newWorkflowName);
			workflowPage.getters.isWorkflowSaved();
			workflowPage.getters
				.workflowNameInputContainer()
				.invoke('attr', 'title')
				.should('eq', newWorkflowName);
			workflowPage.getters.workflowTags().click();
			workflowPage.getters.tagsDropdown().find('.el-tag__close').first().click();
			cy.get('body').click(0, 0);
			workflowPage.getters.saveButton().find('button').should('not.exist');
			workflowPage.getters.tagPills().should('have.length', 2);

			executionsTab.actions.switchToExecutionsTab();
			checkMainHeaderELements();
			workflowPage.getters.saveButton().find('button').should('not.exist');
			workflowPage.getters.tagPills().should('have.length', 2);
			workflowPage.getters
				.workflowNameInputContainer()
				.invoke('attr', 'title')
				.should('eq', newWorkflowName);

			executionsTab.actions.switchToEditorTab();
			checkMainHeaderELements();
			workflowPage.getters.saveButton().find('button').should('not.exist');
			workflowPage.getters.tagPills().should('have.length', 2);
			workflowPage.getters
				.workflowNameInputContainer()
				.invoke('attr', 'title')
				.should('eq', newWorkflowName);
		});

		it('should load items and auto scroll after filter change', () => {
			createMockExecutions();
			createMockExecutions();
			cy.intercept('GET', '/rest/executions?filter=*').as('getExecutions');

			executionsTab.actions.switchToExecutionsTab();

			cy.wait(['@getExecutions']);

			executionsTab.getters.executionsList().scrollTo(0, 500).wait(0);

			executionsTab.getters.executionListItems().eq(10).click();

			cy.getByTestId('executions-filter-button').click();
			cy.getByTestId('executions-filter-status-select').should('be.visible').click();
			getVisibleSelect().find('li:contains("Error")').click();

			executionsTab.getters.executionListItems().should('have.length', 5);
			executionsTab.getters.successfulExecutionListItems().should('have.length', 1);
			executionsTab.getters.failedExecutionListItems().should('have.length', 4);

			cy.getByTestId('executions-filter-button').click();
			cy.getByTestId('executions-filter-status-select').should('be.visible').click();
			getVisibleSelect().find('li:contains("Success")').click();

			// check if the list is scrolled
			executionsTab.getters.executionListItems().eq(10).should('be.visible');
			executionsTab.getters.executionsList().then(($el) => {
				const { scrollTop, scrollHeight, clientHeight } = $el[0];
				expect(scrollTop).to.be.greaterThan(0);
				expect(scrollTop + clientHeight).to.be.lessThan(scrollHeight);

				// scroll to the bottom
				$el[0].scrollTo(0, scrollHeight);
				executionsTab.getters.executionListItems().should('have.length', 18);
				executionsTab.getters.successfulExecutionListItems().should('have.length', 18);
				executionsTab.getters.failedExecutionListItems().should('have.length', 0);
			});

			cy.getByTestId('executions-filter-button').click();
			cy.getByTestId('executions-filter-reset-button').should('be.visible').click();
			executionsTab.getters.executionListItems().eq(11).should('be.visible');
		});

		it('should redirect back to editor after seeing a couple of execution using browser back button', () => {
			createMockExecutions();
			cy.intercept('GET', '/rest/executions?filter=*').as('getExecutions');

			executionsTab.actions.switchToExecutionsTab();

			cy.wait(['@getExecutions']);
			executionsTab.getters.workflowExecutionPreviewIframe().should('exist');

			executionsTab.getters.executionListItems().eq(2).click();
			executionsTab.getters.workflowExecutionPreviewIframe().should('exist');
			executionsTab.getters.executionListItems().eq(4).click();
			executionsTab.getters.workflowExecutionPreviewIframe().should('exist');
			executionsTab.getters.executionListItems().eq(6).click();
			executionsTab.getters.workflowExecutionPreviewIframe().should('exist');

			cy.go('back');
			executionsTab.getters.workflowExecutionPreviewIframe().should('exist');
			cy.go('back');
			executionsTab.getters.workflowExecutionPreviewIframe().should('exist');
			cy.go('back');
			executionsTab.getters.workflowExecutionPreviewIframe().should('exist');
			cy.go('back');

			cy.url().should('not.include', '/executions');
			cy.url().should('include', '/workflow/');
			workflowPage.getters.nodeViewRoot().should('be.visible');
		});
	});

	describe('when new workflow is not saved', () => {
		beforeEach(() => {
			workflowPage.actions.visit();
		});

		it('should open executions tab', () => {
			executionsTab.actions.switchToExecutionsTab();
			executionsTab.getters.executionsSidebar().should('be.visible');
			executionsTab.getters.executionsEmptyList().should('be.visible');
			cy.getByTestId('workflow-execution-no-trigger-content').should('be.visible');
			cy.get('button:contains("Add first step")').should('be.visible').click();

			cy.getByTestId('node-creator-item-name')
				.should('be.visible')
				.filter(':contains("Trigger")')
				.click();
			executionsTab.actions.switchToExecutionsTab();
			executionsTab.getters.executionsSidebar().should('be.visible');
			executionsTab.getters.executionsEmptyList().should('be.visible');
			cy.getByTestId('workflow-execution-no-content').should('be.visible');

			workflowPage.getters.saveButton().find('button').should('be.enabled').click();
			workflowPage.getters.isWorkflowSaved();
			workflowPage.getters.nodeViewRoot().should('be.visible');
		});
	});
});

const createMockExecutions = () => {
	executionsTab.actions.createManualExecutions(5);
	// Make some failed executions by enabling Code node with syntax error
	executionsTab.actions.toggleNodeEnabled('Error');
	workflowPage.getters.disabledNodes().should('have.length', 0);
	executionsTab.actions.createManualExecutions(2);
	// Then add some more successful ones
	executionsTab.actions.toggleNodeEnabled('Error');
	workflowPage.getters.disabledNodes().should('have.length', 1);
	executionsTab.actions.createManualExecutions(4);
};

const checkMainHeaderELements = () => {
	workflowPage.getters.workflowNameInputContainer().should('be.visible');
	workflowPage.getters.workflowTagsContainer().should('be.visible');
	workflowPage.getters.workflowMenu().should('be.visible');
	workflowPage.getters.saveButton().should('be.visible');
};