using ESuite.UI.E2E.Helpers;
using ESuite.UI.E2E.Models;
using ESuite.UI.E2E.Pages;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenQA.Selenium;
using OpenQA.Selenium.Support.UI;

namespace ESuite.UI.E2E.StepDefinitions
{
    [Binding]
    public class UserAuthenticationStepDefinitions
    {
        private readonly IWebDriver driver;
        private readonly LoginPage loginPage;
        private readonly EditUsersPage editUsersPage;
        private readonly ScenarioContext _scenarioContext;
        private readonly AutomationTestManagerHelper automationTestManagerHelper;
        private readonly TestEnvironment testEnvironment;
        private readonly ConfigHelper configHelper;
        private readonly APIHelper apiHelper;

        public UserAuthenticationStepDefinitions(ScenarioContext scenarioContext)
        {
            driver = WebDriverHelper.GetWebDriver();
            loginPage = new LoginPage(driver);
            editUsersPage = new EditUsersPage(driver);
            _scenarioContext = scenarioContext;
            automationTestManagerHelper = new(_scenarioContext);
            testEnvironment = new TestEnvironment();
            configHelper = new ConfigHelper();
            apiHelper = new(_scenarioContext);

        }

        [BeforeScenario("auth", Order = 500)]
        [When(@"I login as Admin")]
        public void BeforeScenarioWithAuth()
        {
            AuthenticationHelper.LoginWithCredentials(testEnvironment.Username, testEnvironment.Password, configHelper.BaseUrl);
        }

        [BeforeScenario("createuser", Order = 80)]
        [Given(@"I create a new user via API")]
        public async Task CreateUserAsync()
        {
            await automationTestManagerHelper.CreateUser();
            var users = await apiHelper.GetUsers(configHelper.APIUrl);
            var data = users["data"]!.ElementAt(0);
            int userId = (int)data["id"]!;
            string userEmail = (string)data["email"]!;
            Guid userGuid = Guid.Parse((string)data["guid"]!);
            Assert.AreEqual(userEmail, _scenarioContext["UserEmail"].ToString()!);
            _scenarioContext["UserId"] = userId;
            _scenarioContext["UserGUID"] = userGuid;
        }

        [AfterScenario("deleteuser", Order = 2)]
        [Given(@"I delete existing user")]
        [When(@"I delete existing user")]
        [Then(@"I delete existing user")]
        public void DeleteUser()
        {
            automationTestManagerHelper.DeleteUser();
        }

        [AfterScenario("deleteuserapi", Order = 22)]
        [Given(@"I delete existing user via API")]
        [When(@"I delete existing user via API")]
        [Then(@"I delete existing user via API")]
        public async Task DeleteUserViaAPIAsync()
        {
            await automationTestManagerHelper.DeleteUserAPI();
        }

        [Given(@"I enter valid credentials via login page")]
        public void WhenIEnterValidCredentials()
        {
            AuthenticationHelper.LoginWithCredentials(testEnvironment.Username, testEnvironment.Password, testEnvironment.BaseUrl);
        }

        [AfterScenario("logout", Order = 200)]
        [Given(@"I logout")]
        [When(@"I logout")]
        [Then(@"I logout")]
        public void Logout()
        {
            AuthenticationHelper.Logout(configHelper.BaseUrl);
        }

        [Then(@"I successfully login with new credentials and I do not see the login page anymore")]
        [Given(@"I successfully login with new credentials and I do not see the login page anymore")]
        public void ThenISuccessfullyLoginWithNewCredentials()
        {
            AuthenticationHelper.LoginWithCredentials(_scenarioContext["UserEmail"].ToString()!, _scenarioContext["Password"].ToString()!, configHelper.BaseUrl);
            WebDriverWait wait = new(driver, TimeSpan.FromSeconds(configHelper.ClickWaitSeconds));
            wait.Until(btn => !btn.Url.Contains("login"));
        }

        [Then(@"I successfully login with new credentials and 2FA")]
        [Given(@"I successfully login with new credentials and 2FA")]
        public void ThenISuccessfullyLoginWithNewCredentialsAnd2FA()
        {
            AuthenticationHelper.LoginWithCredentials2FA(_scenarioContext["UserEmail"].ToString()!, _scenarioContext["Password"].ToString()!, configHelper.BaseUrl, _scenarioContext);
            WebDriverWait wait = new(driver, TimeSpan.FromSeconds(configHelper.ClickWaitSeconds));
            wait.Until(btn => !btn.Url.Contains("login"));
        }

        [Then(@"I enter a new password and confirm it")]
        [Then(@"I enter a new password and confirm it and add 2FA")]
        public void ThenIEnterANewPasswordAndConfirmIt(Table dataTable)
        {
            var users = dataTable.CreateSet<UsersData>();
            I.WaitForPageToLoad();

            foreach (var user in users)
            {
                _scenarioContext["Password"] = user.Password + _scenarioContext["GUIDPostfix"];

                EnterNewPasswordAndConfirmIt();

                if (string.Equals(user.MFA ?? "", "true", StringComparison.OrdinalIgnoreCase))
                {
                    EnterNew2FA();
                }
                I.Click(loginPage.ActivateButton);
                AutomationTestManagerHelper.SaveTimingForAction(_scenarioContext);
                WebDriverWait wait = new(driver, TimeSpan.FromSeconds(configHelper.ClickWaitSeconds));
                wait.Until(btn => btn.Url.Contains("login"));
                I.WaitForVisible(loginPage.EmailInput);
            }
        }

        [Then(@"I assign Admin role to the user")]
        public async Task AssignAdminRoleToUser()
        {

            var userId = int.Parse(_scenarioContext["UserId"].ToString()!);
            Guid userGuid = new(_scenarioContext["UserGUID"].ToString()!);
            await automationTestManagerHelper.AssignAdminRole(userId, userGuid);
        }

        [Then(@"I am resetting the password for the newly created user")]
        public void ResettingThePasswordForTheNewlyCreatedUser()
        {
            I.NavigateToURL($"{configHelper.BaseUrl}/login");
            I.FillField(loginPage.EmailInput, _scenarioContext["UserEmail"].ToString()!);
            I.Click(loginPage.NextButton);
            I.Click(loginPage.ForgottenPasswordButton);
            I.WaitForVisible(loginPage.ForgottenPasswordMessageAlert);
        }

        [Then(@"I enter a new password and save it")]
        public void ThenIEnterNewPasswordAndSaveIt(Table dataTable)
        {
            var users = dataTable.CreateSet<UsersData>();

            foreach (var user in users)
            {
                _scenarioContext["Password"] = user.Password + _scenarioContext["GUIDPostfix"];

                EnterNewPasswordAndConfirmIt();
                I.Click(loginPage.SaveButton);
                AutomationTestManagerHelper.SaveTimingForAction(_scenarioContext);
            }
        }

        [When("I try to delete existing user")]
        public void WhenITryToDeleteExistingUser()
        {
            var entityName = _scenarioContext["UserName"].ToString()!;
            AutomationTestManagerHelper.NavigateToMenu("Users");
            BasicPage.SearchObjectNameInTableViaSearchInputField(entityName, AddUsersPage.DisplayNameSearchField);
        }

        [Then(@"^I (can|can't) delete myself$")]
        public void ThenICantDeleteMyself(string presence)
        {
            var entityName = _scenarioContext["UserName"].ToString()!;
            switch (presence)
            {
                case "can":
                    I.WaitForVisible(BasicPage.DeleteObjectButton(entityName));
                    break;
                case "can't":
                    I.WaitTillInvisible(BasicPage.DeleteObjectButton(entityName), 2);
                    break;
            }
        }

        public void EnterNewPasswordAndConfirmIt()
        {
            I.FillField(loginPage.PasswordInput, _scenarioContext["Password"].ToString()!);
            I.FillField(loginPage.ConfirmPasswordInput, _scenarioContext["Password"].ToString()!);
        }

        private void EnterNew2FA()
        {
            I.Click(editUsersPage.MFACheckbox);
            var secretKey = I.GetText(editUsersPage.MFAKey);
            _scenarioContext["SecretKey"] = secretKey;
            var code = AuthenticationHelper.GenerateOTP(_scenarioContext["SecretKey"].ToString()!, _scenarioContext);
            I.FillField(editUsersPage.MFACodeField, code);
        }
    }
}