import { createGuestDetails } from '@support-user/index';
import { UserModal, ProfileInterceptApis, TravelerModal } from '@profile-lib/index';
import { TripTileType, Timeouts, Random } from '@utility-lib/index';
import { SharedIntercepts } from '@intercepts-lib/shared_intercepts';

const selectors = {
  // Selectors for contact info section
  givenNameInput: 'input[formcontrolname="givenName"]',
  familyNameInput: 'input[formcontrolname="familyName"]',
  phoneInput: 'ta-phone-input[formcontrolname="mobilePhone"]',
  workPhoneInput: 'ta-phone-input[formcontrolname="workPhoneNumber"]',
  phoneValue: 'input.ta-phone-input__template',
  personalEmailValue: 'input[formcontrolname="alternateEmail"]',
  workEmailValue: 'input[formcontrolname="email"]',
  loyaltyButton: '#mat-checkbox-1 > .mat-checkbox-layout > .mat-checkbox-inner-container',
  loyaltyButtonStatus: 'ta-checkbox[formcontrolname="marketingCommEmailOptIn"] input',
  saveFooterButton: '[data-testid="stickyPrimaryButton"]',
  // Selectors for Delegate info section
  asDelegateCheckbox: 'ta-checkbox[formcontrolname="iAmDelegateForCheckbox"]',
  asDelegateInput: 'ta-user-group-simple[formcontrolname="iAmDelegateForUsers"] input',
  asDelegateEntry:
    'ta-user-group-simple[formcontrolname="iAmDelegateForUsers"] span.ta-user-tag-simple__name',
  asDelegateRemove:
    'ta-user-group-simple[formcontrolname="iAmDelegateForUsers"] .ta-user-tag-simple__remove',
  setDelegateCheckbox: 'ta-checkbox[formcontrolname="myDelegatesCheckbox"]',
  setDelegateInput: 'ta-user-group-simple[formcontrolname="myDelegatesUsers"] input',
  setDelegateEntry:
    'ta-user-group-simple[formcontrolname="myDelegatesUsers"] span.ta-user-tag-simple__name',
  setDelegateRemove:
    'ta-user-group-simple[formcontrolname="myDelegatesUsers"] .ta-user-tag-simple__remove',
  searchDelgateResult: '.ta-user-search-simple-popover__item',
  // Selectors for Assistant info section
  searchAssistantInput:
    '.ta-profile-assistant-info__search-wrapper > ta-user-search > .ta-user-search',
  searchAssistantResult: '.ta-user-search-popover__item',
  removeAssistant: '.ta-profile-assistant-info__cancel',
  existingAssistantButtonNo: 'input[value="no"]',
  existingAssistantButtonYes: 'input[value="yes"]',
  newAssistantNameInput: '.ta-profile-assistant-info__form input[formcontrolname="name"]',
  newAssistantEmailInput:
    '.ta-profile-assistant-info__form input[formcontrolname="email"]',
  newAssistantPhoneInput: '.ta-profile-assistant-info__form ta-form-wrap [type="tel"]',
  addSecondAssistantButton:
    '.ta-profile-contact-info__second-info > .ta-button > .ta-button__content',
  // Selectors for Emergency Contact info section
  emergencyNameInput:
    'div[formgroupname="emergencyContact"] input[formcontrolname="name"]',
  emergencyRelationshipDropdown: 'div[formgroupname="emergencyContact"] .ta-select',
  emergencyRelationshipFriend: 'div[aria-label="Friend"]',
  emergencyRelationshipValue: 'ta-select[formcontrolname="relation"] button',
  emergencyEmailInput:
    'div[formgroupname="emergencyContact"] input[formcontrolname="email"]',
  emergencyPhoneInput:
    'div[formgroupname="emergencyContact"] .ta-phone-input [type="tel"]'
};

type gender = 'MALE' | 'FEMALE';
type age = number;

export class ContactModal {
  static visitPage(): void {
    cy.allure().logStep('Visit profile page on contact info tab');
    cy.visit('app/user2/profile?tab=contact');
  }

  static interceptApis(numRetries: number): void {
    if (numRetries === 0) {
      throw new Error(
        `Could not find property '${'given_name'}' after multiple attempts`
      );
    }
    SharedIntercepts.intercepts();
    cy.allure().logStep('Visit profile page on contact info tab');
    ContactModal.visitPage();
    cy.wait('@waitForUserInfo').then((interception) => {
      const response = interception.response;
      if (response && Object.prototype.hasOwnProperty.call(response.body, 'given_name')) {
        // Response has the expected property, proceed with the test
        cy.wrap(response.body['given_name']).as('givenName');
      } else {
        // Response is missing the expected property, retry after 1 second with a new intercept
        cy.log('Bad API response, retrying...');
        cy.wait(Timeouts.SHORT_TIMEOUT_3_SEC.timeout);
        this.interceptApis(numRetries - 1);
      }
    });
  }

  static getContactInfo(): void {
    cy.wait('@waitForUserInfo').then((interception) => {
      // Gets current contact information and saves them as aliases
      const response = interception.response;
      if (response) {
        const properties = ['given_name', 'family_name', 'phone_number', 'email'];
        properties.forEach((property) => {
          cy.wrap(response.body)
            .should('have.property', property)
            .as(`org${property.charAt(0).toUpperCase()}${property.slice(1)}`);
        });
      }
    });
  }
  static compareContactInfo(): void {
    // Proper handling of the latest request needs unique alias each time
    const userInfoInterceptAlias = 'waitForUserInfo_' + Random.randomString();
    cy.intercept('GET', '/api/uaa/userinfo').as(userInfoInterceptAlias);
    cy.reload();
    cy.wait(`@${userInfoInterceptAlias}`).then((interception) => {
      // Gets new contact information and saves them as new aliases
      const response = interception.response;

      function setAliasForProperty(propertyName: string) {
        cy.wrap(response?.body)
          .should('have.property', propertyName)
          .as(`new${propertyName.charAt(0).toUpperCase()}${propertyName.slice(1)}`);
      }

      if (response) {
        setAliasForProperty('given_name');
        setAliasForProperty('family_name');
        setAliasForProperty('phone_number');
        setAliasForProperty('email');
      }
    });
    // Confirms on the API level that the contact info has been updated
    const properties = {
      orgGiven_name: 'newGiven_name',
      orgFamily_name: 'newFamily_name',
      orgPhone_number: 'newPhone_number'
    };

    for (const [orgProp, newProp] of Object.entries(properties)) {
      TravelerModal.compareAPIProperties(orgProp, newProp);
    }
    // Confirms that the UI is also displaying the updated contact info
    TravelerModal.compareUIProperties(selectors.givenNameInput, properties.orgGiven_name);
    TravelerModal.compareUIProperties(
      selectors.familyNameInput,
      properties.orgFamily_name
    );
    // Phone numbers requires unique comparisons
    cy.get(selectors.phoneValue)
      .first() // Checks mobile number field
      .invoke('val')
      .then((displayedPhoneNumber) => {
        const phoneStr = displayedPhoneNumber?.toString().replace(/\s/g, '');
        cy.get('@newPhone_number').then((value) => {
          expect(phoneStr).to.equal(value);
        });
      });
    cy.get(selectors.phoneValue)
      .eq(1) // Checks work phone field
      .invoke('val')
      .then((displayedWorkNumber) => {
        const phoneStr = displayedWorkNumber?.toString().replace(/\s/g, '');
        cy.get('@newPhone_number').then((value) => {
          expect(phoneStr).to.equal(value);
        });
      });
    // Confirm personal email field is not blank (since there was none added before)
    cy.get(selectors.personalEmailValue).invoke('val').should('not.be.empty');
    // Confirms the work email address field is displaying (field cannot be edited; checks existing email is displaying)
    cy.get(selectors.workEmailValue)
      .invoke('val')
      .then((displayedEmail) => {
        cy.get('@newEmail').then((value) => {
          expect(displayedEmail).to.equal(value);
        });
      });
    // Confirms marketing email checkbox is now enabled (previously unchecked)
    cy.get(selectors.loyaltyButtonStatus).should('have.attr', 'aria-checked', 'true');
  }
  static createNewContactInfo = (
    age: age,
    gender: gender,
    travelerType: TripTileType
  ) => {
    const {
      givenName = '',
      familyName = '',
      phoneNumber = '',
      email = ''
    } = createGuestDetails(age, gender, travelerType);
    // Trim the generated phone number down to 11 characters (with a '+')
    const maxLength = 12;
    cy.wrap(phoneNumber)
      .invoke('substring', 0, maxLength)
      .then((number) => {
        const trimNumber = number;
        // Adds and saves new contact information
        UserModal.fillTravelerName(givenName, familyName);
        UserModal.fillMobilePhoneNumber(trimNumber);
        UserModal.fillWorkPhoneNumber(trimNumber);
        UserModal.fillAltEmail(email);
        cy.get(selectors.loyaltyButton).click(); // Checks marketing email box, which was previously not checked
        cy.get(selectors.saveFooterButton).click();
        cy.wait('@userInfoSave');
      });
  };
}

export class DelegateModal {
  static interceptApis(numRetries: number): void {
    if (numRetries === 0) {
      throw new Error(
        `Could not find property '${'delegatedUsers'}' after multiple attempts`
      );
    }
    ProfileInterceptApis.interceptProfileApis();
    cy.allure().logStep('Visit profile page on contact info tab');
    ContactModal.visitPage();
    cy.wait('@waitForDelegatesInfo').then((interception) => {
      const response = interception.response;
      if (
        response &&
        Object.prototype.hasOwnProperty.call(response.body, 'delegatedUsers')
      ) {
        // Response has the expected property, proceed with the test
        cy.wrap(response.body['delegatedUsers']).as('delegates');
      } else {
        // Response is missing the expected property, retry after 1 second with a new intercept
        cy.log('Bad API response, retrying...');
        cy.wait(Timeouts.SHORT_TIMEOUT_3_SEC.timeout);
        this.interceptApis(numRetries - 1);
      }
    });
  }
  static addAsDelegate(delegate: string): void {
    // Proper handling of the latest request needs unique alias each time
    const delegatesInterceptAlias = 'waitForDelegatesInfoSave_' + Random.randomString();
    cy.intercept('PUT', 'api/user/profile/delegates').as(delegatesInterceptAlias);
    cy.get(selectors.asDelegateCheckbox).click();
    cy.get(selectors.asDelegateInput).type(delegate);
    cy.get(selectors.searchDelgateResult).click();
    cy.get(selectors.saveFooterButton).click();

    cy.wait(`@${delegatesInterceptAlias}`).then((interception) => {
      // Confirms user has a saved delegate
      const response = interception.response;
      if (response) {
        const properties = ['givenName', 'familyName'];
        properties.forEach((property) => {
          cy.wrap(response.body.delegatedUsers[0].user).should('have.property', property);
        });
      }
    });
  }
  static verifyAsDelegate(delegate: string) {
    cy.get(selectors.asDelegateEntry).should('contain.text', delegate);
  }
  static deleteAsDelegate() {
    // Proper handling of the latest request needs unique alias each time
    const delegatesInterceptAlias = 'waitForDelegatesInfoSave_' + Random.randomString();
    cy.intercept('PUT', 'api/user/profile/delegates').as(delegatesInterceptAlias);
    cy.get(selectors.asDelegateRemove).click();
    cy.get(selectors.saveFooterButton).click();

    cy.wait(`@${delegatesInterceptAlias}`).then((interception) => {
      // Confirms user has no saved delegates
      const response = interception.response;
      if (response) {
        const properties = ['user'];
        properties.forEach((property) => {
          cy.wrap(response.body.delegatedUsers).should('not.have.property', property);
        });
      }
    });
    cy.get(selectors.asDelegateEntry).should('not.exist');
  }
  static addSetDelegate(delegate: string) {
    // Proper handling of the latest request needs unique alias each time
    const delegatesInterceptAlias = 'waitForDelegatesInfoSave_' + Random.randomString();
    cy.intercept('PUT', 'api/user/profile/delegates').as(delegatesInterceptAlias);
    cy.get(selectors.setDelegateCheckbox).click();
    cy.get(selectors.setDelegateInput).type(delegate);
    cy.get(selectors.searchDelgateResult).click();
    cy.get(selectors.saveFooterButton).click();

    cy.wait(`@${delegatesInterceptAlias}`).then((interception) => {
      // Confirms user has a set delegate
      const response = interception.response;
      console.log(response);
      if (response) {
        const properties = ['givenName'];
        properties.forEach((property) => {
          cy.wrap(response.body.delegates[0].user).should('have.property', property);
        });
      }
    });
  }
  static verifySetDelegate(delegate: string): void {
    cy.get(selectors.setDelegateEntry).should('contain.text', delegate);
  }
}
export class AssistantModal {
  static addExistingAssistant(assistant: string): void {
    cy.allure().logStep('Adding existing assistant on contact info tab');
    cy.get(selectors.searchAssistantInput).as('assistantInput');
    cy.get('@assistantInput').scrollIntoView();
    cy.get('@assistantInput').should('be.visible').click();
    cy.get('@assistantInput').should('be.visible').type(assistant);
    cy.get(selectors.searchAssistantResult).should('be.visible').click();
    cy.get(selectors.saveFooterButton).click();
  }
  static removeAssistant(): void {
    cy.allure().logStep('Removing assistant on contact info tab');
    cy.get(selectors.removeAssistant).click();
    cy.get(selectors.saveFooterButton).click();
  }
  static validateAssistant(
    assistantName: string,
    assistantEmail: string,
    assistantPhone: string
  ): void {
    cy.allure().logStep('Validating assistant on contact info tab');
    cy.get(selectors.newAssistantNameInput).should('have.value', assistantName);
    cy.get(selectors.newAssistantEmailInput).should('have.value', assistantEmail);
    cy.get(selectors.newAssistantPhoneInput)
      .invoke('val')
      .then((phoneNumber) => {
        const actualPhoneNumber = Cypress._.trim(
          phoneNumber?.toString().replace(/\D/g, '').substring(1)
        ); // removes all non-numeric characters
        expect(actualPhoneNumber).to.eq(assistantPhone);
      });
  }
  static addNewAssistant(
    assistantName: string,
    assistantEmail: string,
    assistantPhone: string
  ): void {
    cy.allure().logStep('Adding new assistant on contact info tab');
    cy.get(selectors.existingAssistantButtonNo).click();
    cy.get(selectors.newAssistantNameInput).type(assistantName);
    cy.get(selectors.newAssistantEmailInput).type(assistantEmail);
    cy.get(selectors.newAssistantPhoneInput).type(assistantPhone);
    cy.get(selectors.saveFooterButton).click();
  }
  static addSecondAssistant(assistant: string): void {
    cy.allure().logStep('Adding second assistant on contact info tab');
    cy.get(selectors.addSecondAssistantButton).click();
    cy.get(selectors.searchAssistantInput).type(assistant);
    cy.get(selectors.searchAssistantResult).click();
    cy.get(selectors.saveFooterButton).click();
  }
}

export class EmergencyContactModal {
  static addEmergencyContact(
    emergencyName: string,
    emergencyEmail: string,
    emergencyNumber: string
  ): void {
    cy.allure().logStep('Adding emergency contact info on contact info tab');
    cy.get(selectors.emergencyNameInput).type(emergencyName);
    cy.get(selectors.emergencyRelationshipDropdown).click();
    // Defaults to relationship type "Friend"
    cy.get(selectors.emergencyRelationshipFriend).click();
    cy.get(selectors.emergencyEmailInput).type(emergencyEmail);
    cy.get(selectors.emergencyPhoneInput).type(emergencyNumber);
    cy.get(selectors.saveFooterButton).click();
  }
  static verifyEmergencyContact(
    emergencyName: string,
    emergencyEmail: string,
    emergencyNumber: string
  ) {
    cy.allure().logStep('Verifying emergency contact info on contact info tab');
    cy.get(selectors.emergencyNameInput).should('have.value', emergencyName);
    cy.get(selectors.emergencyEmailInput).should('have.value', emergencyEmail);
    cy.get(selectors.emergencyPhoneInput)
      .invoke('val')
      .then((displayedPhoneNumber) => {
        const phoneStr = displayedPhoneNumber
          ?.toString()
          .replace(/\s/g, '')
          .replace(/^\+1/, '');
        expect(phoneStr).to.equal(emergencyNumber);
      });
    // Verifies the default relationship type, friend
    cy.get(selectors.emergencyRelationshipValue).should('have.value', 'FRIEND');
  }
  static clearEmergencyContact(): void {
    cy.allure().logStep('Removing emergency contact info on contact info tab');
    cy.get(selectors.emergencyNameInput).clear();
    // Relationship dropdown cannot be cleared
    cy.get(selectors.emergencyEmailInput).clear();
    cy.get(selectors.emergencyPhoneInput).clear();
    cy.get(selectors.saveFooterButton).click();
  }
}
