import {Given, Then, When} from "@cucumber/cucumber";
import compareText from "../helpers/framework-helpers/check/compareText";
import checkEqualsText from "../helpers/framework-helpers/check/checkEqualsText";
import checkContainsText from "../helpers/framework-helpers/check/checkContainsText";
import checkIsEmpty from "../helpers/framework-helpers/check/checkIsEmpty";
import checkProperty from "../helpers/framework-helpers/check/checkProperty";
import checkDimension from "../helpers/framework-helpers/check/checkDimension";
import checkOffset from "../helpers/framework-helpers/check/checkOffset";
import clickElement from "../helpers/framework-helpers/action/clickElement";
import clearInputField from "../helpers/framework-helpers/action/clearInputField";
import dragElement from "../helpers/framework-helpers/action/dragElement";
import selectOptionByIndex from "../helpers/framework-helpers/action/selectOptionByIndex";
import selectOption from "../helpers/framework-helpers/action/selectOption";
import moveTo from "../helpers/framework-helpers/action/mouseHoverToCoordinates";
import scroll from "../helpers/framework-helpers/action/scroll";
import typeSlowlyIntoInputBox from "../helpers/framework-helpers/action/typeSlowlyIntoInputBox";
import checkIfElementExists from "../helpers/framework-helpers/check/checkIfElementExists";
import checkWithinViewport from "../helpers/framework-helpers/check/checkWithinViewport";
import checkClass from "../helpers/framework-helpers/check/checkClass";
import waitForElementState from "../helpers/framework-helpers/action/waitForElementState";
import setInputField from "../helpers/framework-helpers/action/setInputField";
import mouseHoverToCoordinates from "../helpers/framework-helpers/action/mouseHoverToCoordinates";
import mouseHoverToElement from "../helpers/framework-helpers/action/mouseHoverToElement";
import mouseHoverToElementByIndex from "../helpers/framework-helpers/action/mouseHoverToElementByIndex";
import checkIfElementExistsByIndex from "../helpers/framework-helpers/check/checkIfElementExistsByIndex";
import checkElementTextByIndex from "../helpers/framework-helpers/check/checkElementTextByIndex";
import checkElementText from "../helpers/framework-helpers/check/checkElementText";
import checkElementTextMatchesByIndex from "../helpers/framework-helpers/check/checkElementTextMatchesByIndex";
import waitForElementStateByIndex from "../helpers/framework-helpers/action/waitForElementStateByIndex";
import {ConfigHelper} from "../helpers/common-helpers/config-helper";
import {PageConfigTypes} from "../custom-types/pageConfig-types";

let pageConfig: PageConfigTypes;

Given(/^I expect that element "([^"]*)?" is( not)? (Clickable|Displayed|Selected|Existing|Enabled|Focused|Checked)$/, async (pageObject: string, falseCase: boolean, elementState: 'Clickable' | 'Displayed' | 'Selected' | 'Existing' | 'Enabled' | 'Focused' | 'Checked') => {
    console.log(`I expect that element ${pageObject} is ${falseCase ? 'not' : ''} ${elementState}`);
    pageConfig = ConfigHelper.getPageConfig(pageObject);
    await waitForElementState(pageConfig, falseCase, elementState, 0, 0);
});

Given(/^I wait and expect that element "([^"]*)?" is( not)? (Clickable|Displayed|Selected|Existing|Enabled|Focused|Checked)$/, async (pageObject: string, falseCase: boolean, elementState: 'Clickable' | 'Displayed' | 'Selected' | 'Existing' | 'Enabled' | 'Focused' | 'Checked') => {
    console.log(`I wait and expect that element ${pageObject} is ${falseCase ? 'not' : ''} ${elementState}`);
    pageConfig = ConfigHelper.getPageConfig(pageObject);
    await waitForElementState(pageConfig, falseCase, elementState, 55, 1000);
});

Given(/^I expect that (\d+)(?:th|st|nd|rd) "([^"]*)" (?:button|link|icon|element) is( not)? (Clickable|Displayed|Selected|Existing|Enabled|Focused|Checked)$/, async (elementPosition: number, pageObject: string, falseCase: boolean, elementState: 'Clickable' | 'Displayed' | 'Selected' | 'Existing' | 'Enabled' | 'Focused' | 'Checked') => {
    console.log(`I expect that ${elementPosition} ${pageObject} element is ${falseCase ? 'not' : ''} ${elementState}`);
    pageConfig = ConfigHelper.getPageConfig(pageObject);
    await waitForElementStateByIndex(elementPosition, pageConfig, falseCase, elementState, 0, 0)
});

Given(/^I wait and expect that (\d+)(?:th|st|nd|rd) "([^"]*)" (?:button|link|icon|element) is( not)? (Clickable|Displayed|Selected|Existing|Enabled|Focused|Checked)$/, async (elementPosition: number, pageObject: string, falseCase: boolean, elementState: 'Clickable' | 'Displayed' | 'Selected' | 'Existing' | 'Enabled' | 'Focused' | 'Checked') => {
    console.log(`I expect that ${elementPosition} ${pageObject} element is ${falseCase ? 'not' : ''} ${elementState}`);
    pageConfig = ConfigHelper.getPageConfig(pageObject);
    await waitForElementStateByIndex(elementPosition, pageConfig, falseCase, elementState, 5, 1000)
});

Given(/^the element "([^"]*)?"( not)? contains the same text as element "([^"]*)?"$/, async (pageObject1: string, falseCase: boolean, pageObject2: string) => {
    console.log(`the element ${pageObject1} ${falseCase ? 'not' : ''} contains the same text as element ${pageObject2}`);
    const pageConfig1 = ConfigHelper.getPageConfig(pageObject1);
    const pageConfig2 = ConfigHelper.getPageConfig(pageObject2);
    await compareText(pageConfig1, falseCase, pageConfig2);
});

Given(/^the (button|element|field) "([^"]*)?" is( not)? empty$/, async (elementType: 'button' | 'element' | 'field', pageObject: string, falseCase: boolean) => {
    console.log(`The ${elementType} ${pageObject} is ${falseCase ? 'not' : ''} empty`);
    pageConfig = ConfigHelper.getPageConfig(pageObject);
    await checkIsEmpty(elementType, pageConfig, falseCase);
});

Given(/^the( css)? attribute "([^"]*)?" from element "([^"]*)?" is( not)? expected to be "([^"]*)?"$/, async (isCSS: boolean, attrName: string, pageObject: string, falseCase: boolean, expectedValue: number | string) => {
    console.log(`The ${isCSS} attribute ${attrName} from element ${pageObject} is ${falseCase ? 'not' : ''} expected to be ${expectedValue}`);
    pageConfig = ConfigHelper.getPageConfig(pageObject);
    await checkProperty(isCSS, attrName, pageConfig, falseCase, expectedValue);
});

When(/^I (add|set) "([^"]*)?" to the input field "([^"]*)?"$/, async (method: 'add' | 'set', value: string, pageObject: string) => {
    console.log(`I ${method} ${value} to the input field ${pageObject}`);
    pageConfig = ConfigHelper.getPageConfig(pageObject);
    await setInputField(method, value, pageConfig);
});

When(/^I slowly type "([^"]*)?" into the input field "([^"]*)?" at a speed of "([^"]*)" milliseconds$/, async (value: string, pageObject: string, timeInMillis: number) => {
    console.log(`I slowly type ${value} into the input field ${pageObject} at a speed of ${timeInMillis} milliseconds`);
    pageConfig = ConfigHelper.getPageConfig(pageObject);
    await typeSlowlyIntoInputBox(value, pageConfig, timeInMillis);
});

When(/^I clear the input field "([^"]*)?"$/, async (pageObject: string) => {
    console.log(`I clear the input field ${pageObject}`);
    pageConfig = ConfigHelper.getPageConfig(pageObject);
    await clearInputField(pageConfig);
});

When(/^I select the (\d+)(?:st|nd|rd|th) option from the list of element "([^"]*)?"$/, async (index: number, pageObject: string) => {
    console.log(`I select the ${index} option from the list of element ${pageObject}`);
    pageConfig = ConfigHelper.getPageConfig(pageObject);
    await selectOptionByIndex(index, pageConfig);
});

When(/^I select the option with the (value|text) "([^"]*)?" from the element "([^"]*)?"$/, async (selectionType: 'value' | 'text', selectionValue: string, pageObject: string) => {
    console.log(`I select the option with the ${selectionType} from the element ${pageObject}`);
    pageConfig = ConfigHelper.getPageConfig(pageObject);
    await selectOption(selectionType, selectionValue, pageConfig);
});

When(/^I move to element "([^"]*)?"(?: with an offset of (\d+),(\d+))*$/, async (pageObject: string, xCoordinate: number, yCoordinate: number) => {
    console.log(`I move to element ${pageObject} with an offset of ${xCoordinate},${yCoordinate}`)
    pageConfig = ConfigHelper.getPageConfig(pageObject);
    await moveTo(pageConfig, xCoordinate, yCoordinate);
});

When(/^I scroll to element "([^"]*)?"$/, async (pageObject: string) => {
    console.log(`I scroll to element ${pageObject}`);
    pageConfig = ConfigHelper.getPageConfig(pageObject);
    await scroll(pageConfig);
});

When(/^I (click|doubleClick) on the element "([^"]*)?"$/, async (action: 'click' | 'doubleClick', pageObject: string) => {
    console.log(`I ${action} on the element ${pageObject}`);
    pageConfig = ConfigHelper.getPageConfig(pageObject);
    await clickElement(action, pageConfig);
});

Then(/^I expect that element "([^"]*)?" does (not )?appear (\d+) times$/, async (pageObject: string, falseCase: boolean, exactly: number) => {
    console.log(`I expect that element ${pageObject} does ${falseCase ? 'not' : ''} appear ${exactly} times`);
    pageConfig = ConfigHelper.getPageConfig(pageObject);
    await checkIfElementExists(pageConfig, falseCase, exactly);
});

Then(/^I expect that element "([^"]*)?" is (not )?within the viewport$/, async (pageObject: string, falseCase: boolean) => {
    console.log(`I expect that element ${pageObject} is ${falseCase ? 'not' : ''} within the viewport`);
    pageConfig = ConfigHelper.getPageConfig(pageObject);
    await checkWithinViewport(pageConfig, falseCase);
});

Then(/^I expect that element "([^"]*)?" does( not)? have the class contains "([^"]*)?"$/, async (pageObject: string, falseCase: boolean, expectedClassName: string) => {
    console.log(`I expect that element ${pageObject} does ${falseCase ? 'not' : ''} have the class contains ${expectedClassName}`);
    pageConfig = ConfigHelper.getPageConfig(pageObject);
    await checkClass(pageConfig, falseCase, expectedClassName);
});

When(/^I mouse hover to the element "([^"]*)?"$/, async (pageObject: string) => {
    console.log(`I mouse hover to the element ${pageObject}`)
    pageConfig = ConfigHelper.getPageConfig(pageObject);
    await mouseHoverToElement(pageConfig);
});


When(/^I mouse hover to the (\d+)(?:th|st|nd|rd) "([^"]*)" element$/, async (elementPosition: number, pageObject: string) => {
    console.log(`I mouse hover to the ${elementPosition} ${pageObject} element`)
    pageConfig = ConfigHelper.getPageConfig(pageObject);
    await mouseHoverToElementByIndex(elementPosition, pageConfig);
});

When(/^I mouse hover to element "([^"]*)?"(?: with an offset of (\d+),(\d+))*$/, async (pageObject: string, xCoordinate: number, yCoordinate: number) => {
    console.log(`I mouse hover to element ${pageObject} with an offset of ${xCoordinate},${yCoordinate}`)
    pageConfig = ConfigHelper.getPageConfig(pageObject);
    await mouseHoverToCoordinates(pageConfig, xCoordinate, yCoordinate);
});

// Can't implement this step in the existing website, keeping it for future
Given(/^the element "([^"]*)?" is( not)? ([\d]+)px (broad|tall)$/, async (pageObject: string, falseCase: boolean, expectedValue: string, dimension: 'broad' | 'tall') => {
    console.log(`The element ${pageObject} is ${falseCase ? 'not' : ''} ${expectedValue} ${dimension}`);
    pageConfig = ConfigHelper.getPageConfig(pageObject);
    await checkDimension(pageConfig, falseCase, expectedValue, dimension);
});

// Can't implement this step in the existing website, keeping it for future
Given(/^the element "([^"]*)?" is( not)? positioned at (\d+)px on the (x|y) axis$/, async (pageObject: string, falseCase: boolean, expectedPosition: string, axis: 'x' | 'y') => {
    console.log(`The element ${pageObject} is ${falseCase ? 'not' : ''} positioned at ${expectedPosition} on the ${axis}`);
    pageConfig = ConfigHelper.getPageConfig(pageObject);
    await checkOffset(pageConfig, falseCase, expectedPosition, axis);
});

// Can't implement this step in the existing website, keeping it for future
When(/^I drag element "([^"]*)?" to element "([^"]*)?"$/, async (SelectedPageObject: string, DestinationPageObject: string) => {
    console.log(`I drag element ${SelectedPageObject} to element ${DestinationPageObject}`);
    const SelectedPageConfig: PageConfigTypes = ConfigHelper.getPageConfig(SelectedPageObject);
    const DestinationPageConfig: PageConfigTypes = ConfigHelper.getPageConfig(DestinationPageObject);
    await dragElement(SelectedPageConfig, DestinationPageConfig);
});

Given(/^the (\d+)(?:th|st|nd|rd) "([^"]*)" (?:button|link|icon|element) is( not)? present$/, async (elementPosition: number, pageObject: string, falseCase: boolean) => {
    console.log(`The ${elementPosition} ${pageObject} element is ${falseCase ? 'not' : ''} present`);
    pageConfig = ConfigHelper.getPageConfig(pageObject);
    await checkIfElementExistsByIndex(elementPosition, pageConfig, falseCase)
});


Given(/^the (button|element|field) "([^"]*)?"( not)? matches the text "([^"]*)?"( ignoring-case)?$/, async (elementType: 'button' | 'element' | 'field', pageObject: string, falseCase: boolean, expectedText: string, ignoreCase: boolean) => {
    console.log(`The ${elementType} ${pageObject} ${falseCase ? 'not' : ''} matches the text ${expectedText} ${ignoreCase ? 'ignoring case' : ''}`);
    pageConfig = ConfigHelper.getPageConfig(pageObject);
    await checkEqualsText(elementType, pageConfig, falseCase, expectedText, ignoreCase);
});

Given(/^the (?:button|element|container) "([^"]*)?" (not )?contains the text "([^"]*)?"( ignoring-case)?$/, async (pageObject: string, falseCase: boolean, expectedText: string, ignoreCase: boolean) => {
    console.log(`The (button|element|container) ${pageObject} ${falseCase ? 'not' : ''} contains the text ${expectedText} ${ignoreCase ? 'ignoring case' : ''}`);
    pageConfig = ConfigHelper.getPageConfig(pageObject);
    await checkContainsText(pageConfig, falseCase, expectedText, ignoreCase);
});


Given(/^the "([^"]*)" (?:button|link|icon|element) (not )?contains the text "([^"]*)"( ignoring-case)?$/, async (pageObject: string, falseCase: boolean, expectedText: string, ignoreCase: boolean) => {
    console.log(`The ${pageObject} element ${falseCase ? 'not' : ''} contains the text ${expectedText} ${ignoreCase ? 'ignoring case' : ''}`);
    pageConfig = ConfigHelper.getPageConfig(pageObject);
    await checkElementText(pageConfig, falseCase, expectedText, 10, ignoreCase)
});

Given(/^I wait and expect the "([^"]*)" (?:button|link|icon|element) (not )?contains the text "([^"]*)"( ignoring-case)?$/, async (pageObject: string, falseCase: boolean, expectedText: string, ignoreCase: boolean) => {
    console.log(`I wait and expect the ${pageObject} element ${falseCase ? 'not' : ''} contains the text ${expectedText} ${ignoreCase ? 'ignoring case' : ''}`);
    pageConfig = ConfigHelper.getPageConfig(pageObject);
    await checkElementText(pageConfig, falseCase, expectedText, 50, ignoreCase);
});

Given(/^the (\d+)(?:th|st|nd|rd) "([^"]*)" (?:button|link|icon|element) (not )?contains the text "([^"]*)"( ignoring-case)?$/, async (elementPosition: number, pageObject: string, falseCase: boolean, expectedText: string, ignoreCase: boolean) => {
    console.log(`The ${elementPosition} ${pageObject} element ${falseCase ? 'not' : ''} contains the text ${expectedText} ${ignoreCase ? 'ignoring case' : ''}`);
    pageConfig = ConfigHelper.getPageConfig(pageObject);
    await checkElementTextByIndex(elementPosition, pageConfig, falseCase, expectedText, ignoreCase)
});


Given(/^the (\d+)(?:th|st|nd|rd) "([^"]*)" (?:button|link|icon|element) (not )?matches the text "([^"]*)"( ignoring-case)?$/, async (elementPosition: number, pageObject: string, falseCase: boolean, expectedText: string, ignoreCase: boolean) => {
    console.log(`The ${elementPosition} th|st|nd|rd ${pageObject} element ${falseCase ? 'not' : ''} matches the text ${expectedText} ${ignoreCase ? 'ignoring case' : ''}`);
    pageConfig = ConfigHelper.getPageConfig(pageObject);
    await checkElementTextMatchesByIndex(elementPosition, pageConfig, falseCase, expectedText, ignoreCase)
});