import { randomString } from "../../utils/array";
import { DashboardPage } from "../dashboard.page";
import { Locator, Page, expect } from "@playwright/test";
import { getInputValueWithRetry } from "../utils/retry-get-input";
import { contactDetailView, createAnotherContact, createModalBody, createModalFooter, createModalHeader, emptyState, haveRecord, headingAndCreateButton, updateModalBody, updateModalFooter, updateModalHeader } from "./contact.snapshot";

type ApiContactData = {
  id: string;
  avatar: string;
  first_name: string;
  last_name: string;
  full_name: string;
  title: string[];
  birthday: string | null;
  phone_number: string | null;
  email_address: string | null;
  full_address: string;
  address_1: string;
  address_2: string;
  city: string;
  state_code: string;
  zip_code: string;
  gender: string;
  roles: string[];
  jobs: string[];
  groups: any | null;
  tags: string[];
  note: string | null;
  use_business_address: boolean;
  business: { id: string; name: string } | null;
  is_current_member: boolean;
  is_archived: boolean;
  created_at: string;
  updated_at: string;
}

type ContactDetail = {
  contactName: string;
  emailAddress: string | null;
  phoneNumber: string | null;
  business: string | null;
  role: string[];
  jobTitle: string[];
  note: string | null;
  tags: string[];
};

type ContactUpdate = {
  first_name: string;
  last_name: string;
  emailAddress: string | null;
  phoneNumber: string | null;
  business: string | null;
  note: string | null;
};

export class ContactPage extends DashboardPage {
  constructor(page: Page) {
    super(page);
  }

  get contactLoc() {
    return {
      btnShowArchiveContact: this.genLoc("//span[text()='Archived Contact']"),
      singleBoxContactPin: (nameContact: string) => this.genLoc(`//div[@class='box-text']/p[text()='${nameContact}']`),
      btnPin: this.genLoc("//a[@data-tooltip-content='Click here to pin!']"),
      btnUnPin: this.genLoc("//a[@data-tooltip-content='Click here to unpin!']"),
      inputGroup: this.genLoc("//input[@name='group_ids']"),
      modalFilterHeader: this.genLoc("//div[@class='modal-dialog modal-dialog-centered modal-lg']/descendant::h3[text()='Filter']"),
      optionSelectTag: (tag: string) => this.genLoc(`//div[contains(@class,'title-option') and contains(.,'${tag}')]`),
      optionInSelect: (option: string) => this.genLoc(`//div[text()='${option}']/ancestor::li[@class='dropdown-item']`),
      boxSelectOption: (option: string) => this.genLoc(`//div[@class='box-selected']/descendant::span[text()='${option}']`),
      boxAfterSelected: (option: string) => this.genLoc(`(//div[@class='box-selected']/span[text()='${option}'])[1]`),
      btnFilter: this.genLoc("//button[text()='Filter']"),
      showingEntries: this.genLoc("//p[contains(text(),'Showing')]"),
      tableRow: this.genLoc("table tbody tr"),
      inputEntries: this.genLoc("//input[@name='per_page']"),
      noResult: this.genLoc("//div[text()='No matching results found. Try again.']"),
      labelContactGroup: this.genLoc("//div[text()='Contact Group']"),
      searchInput: this.genLoc("//input[@class='form-control' and @placeholder='Search...']"),
      nameInrow: (name: string) => this.genLoc(`//a[text()='${name}']`),
      emailAlreadyExist: (email: string) => this.genLoc(`//a[text()='${email}']`),
      modalForm: this.genLoc("//div[@class='modal-header p-3']/ancestor::div[@class='modal-dialog modal-dialog-centered modal-lg']"),
      root: this.genLoc("//div[@id='root']"),
      createNewBtn: this.genLoc("//button[text()='New Contact']"),
      textMsgDuplicateEmail: this.genLoc("//p[contains(text(), 'Duplicate entry matching your input')]/following::p/span"),
      msgErrorTooLongInput: (field: string) => this.genLoc(`//span[@class='error d-block error-message' and contains(text(), 'The ${field} field must not be greater than')]`),
      listing: {
        heading: {},
        tableHeading: {},
        tableFilter: {},
        table: {},
      },
      creating: {
        modalNotProvided: this.genLoc("//div[@class='swal2-popup swal2-modal swal2-show']"),
        btnConfirmModalProvided: this.genLoc("//button[@class='swal2-confirm swal-btn-primary swal2-styled']"),
        firstName: this.genLoc("//input[@name='first_name']"),
        email: this.genLoc("//div[@class='form-input-wrapper']/descendant::input[@name='email_address']"),
        createAnotherContact: this.genLoc("//input[@name='create_contact']"),
        createBtn: this.genLoc("//button[text()='Create']"),
        messageCreatetSuccess: this.genLoc("//p[contains(text(),'Added New Successfully!')]"),
        messageCraeteInvalidEmail: this.genLoc("//span[text()='The email address field is not in the correct format']"),
        blockMsgDuplicateEmail: this.genLoc("//div[@class='box-alert-danger-notification d-flex align-items-start ga-16']"),
        dataFirstNameAdd: (firstName: string) => this.genLoc(`//tbody/tr[1]/td[2]/a[text()='${firstName}']`),
        dataEmailAdd: (email: string) => this.genLoc(`//tbody/tr[1]/td[4]/a[text()='${email}']`),
      },
      updating: {
        optionBusiness: (businessName: string) => this.genLoc(`//div[@class='box-text box-text-business' and contains(.,'${businessName}')]`),
        contactAlreadyExits: (email: string) => this.genLoc(`//a[text()='${email}']`),
        dropdownAddTag: (tag: string) => this.genLoc(`//strong[contains(text(), '${tag}')]/ancestor::li`),
        //Locator Detail Contact
        fullNameDetail: this.genLoc("//span[contains(@class,'business-contact-name info-name')]"),
        emailDetail: this.genLoc("//div[@class='list-info-items'][1]/span[1]"),
        phoneDetail: this.genLoc("//div[@class='list-info-items'][1]/span[3]"),
        businessDetail: this.genLoc("//div[@class='list-info-items'][2]/span[1]"),
        rolesDetail: this.genLoc("//div[@class='list-info-items'][2]/span[3]"),
        jobTitlesDetail: this.genLoc("//div[@class='list-info-items'][2]/span[5]"),
        noteDetail: this.genLoc("//div[@class='list-info-items'][3]/span[1]"),
        tagsDetail: this.genLoc("//div[@class='list-request-tag']"),
        tagHadCreateDetail: this.genLoc(`//div[@class='list-request-tag']/p`),

        //Locator Update contact
        inputByName: (name: string) => this.genLoc(`//input[@name='${name}']`),
        inputEmail: this.genLoc("//input[@name='email_address']"),
        inputPhoneNumber: this.genLoc("//input[@name='phone_number']"),
        inputTag: this.genLoc("//input[@name='tags']"),
        inputAddress: this.genLoc("//input[@name='address_1']"),
        inputCity: this.genLoc("//input[@name='city']"),
        inputBusiness: this.genLoc("//input[@name='business_provider_id']"),
        inputFname: this.genLoc("//input[@name='first_name']"),
        inputLname: this.genLoc("//input[@name='last_name']"),
        inputNote: this.genLoc("//textarea[@name='note']"),
        tagHadCreateUpdate: (tag: string) => this.genLoc(`//div[@class='box-selected']/descendant::span[text()='${tag}']`),
        msgUpdateSuccess: this.genLoc("//p[text()='Updated Successfully!']"),
        msgErrorInvalidEmail: this.genLoc("//span[@class='error d-block error-message']"),
        msgArchiveSucess: this.genLoc("//p[text()='Archived Successfully!']"),
        msgUnArchiveSucess: this.genLoc("//p[text()='Unarchived Successfully!']"),
        headerWarningContact: this.genLoc("//div[@class='box-notification-archive']")
      },
      contactGroup: {
        btnDeleteContactGroup: this.genLoc("//div[contains(@class,'btn-delete-member-group')]"),
        btnEditContactGroup: this.genLoc("//a[contains(@class,'btn-open-modal-edit-member-group')]"),
        optionDropdown: (value: string) => this.genLoc(`//div[contains(@class,'title-option') and text()='${value}'] | //span[contains(@class,'date-option') and contains(.,'${value}')]/span[contains(text(),'${value}')]`),
        heading: (heading: string) => this.genLoc(`//span[@class='line-height-normal' and text()='${heading}']`),
        totalEmail: this.genLoc("//a[@href='/contacts?group_ids=38']") //Will change if data in sql change
      }
    };
  }

  get contactSnapshot() {
    return {
      headingAndCreateButton: headingAndCreateButton,
      emptyState: emptyState,
      haveRecord: haveRecord,
      createModalHeader: createModalHeader,
      createModalBody: createModalBody,
      createModalFooter: createModalFooter,
      contactDetailView: contactDetailView,
      updateModalHeader: updateModalHeader,
      updateModalBody: updateModalBody,
      updateModalFooter: updateModalFooter,
      createAnotherContact: createAnotherContact
    }
  }

  get filterProperties() {
    return {
      filterName: [
        {
          name: 'contact_member_name',
          type: 'input',
          paramFilter: 'full_name'
        },
        {
          name: 'group_ids',
          type: 'option',
          paramFilter: 'group_ids'
        },
        {
          name: 'created',
          type: 'input',
          paramFilter: 'created_at'
        },
        {
          name: 'updated',
          type: 'input',
          paramFilter: 'updated_at'
        }
      ],
      msgNoOption: this.genLoc("//div[text()='No options found.']")
    }
  }

  get contactProps() {
    return {
      threeDotDropdownMenus: {
        customizedColumn: "Customized Columns",
        archivedContact: "Archived Contact",
      },
      customizedColumns: [
        {
          name: "Pin",
          default: true,
          tableDisplayName: "Pin",
        },
        {
          name: "Name",
          default: true,
          tableDisplayName: "Name",
        },
        {
          name: "Title",
          default: true,
          tableDisplayName: "Title",
        },
        {
          name: "Email",
          default: true,
          tableDisplayName: "Email",
        },
        {
          name: "Phone",
          default: true,
          tableDisplayName: "Phone",
        },
        {
          name: "Address",
          default: true,
          tableDisplayName: "Address",
        },
        {
          name: "Group",
          default: true,
          tableDisplayName: "Group",
        },
        {
          name: "Business",
          default: true,
          tableDisplayName: "Business",
        },
        {
          name: "Role",
          default: false,
          tableDisplayName: "Role",
        },
        {
          name: "Created",
          default: false,
          tableDisplayName: "Created",
        },
        {
          name: "Updated",
          default: false,
          tableDisplayName: "Updated",
        },
      ],
    };
  }

  async getRanmdomEmail(email: string) {
    const randomNum = randomString(5);
    const randomEmail = await this.renderTemplate(email, { randomNum });
    return randomEmail;
  }

  async fetchDetailContact(): Promise<ApiContactData> {
    return await this.getInfoDetail<ApiContactData>("contacts");
  }

  async getContactDetail(): Promise<ContactDetail> {
    await this.contactLoc.updating.emailDetail.waitFor({ state: 'visible', timeout: 3000 });

    const fullName = await this.contactLoc.updating.fullNameDetail.textContent() || '';
    const email = await this.contactLoc.updating.emailDetail.textContent() || '';
    const phone = await this.contactLoc.updating.phoneDetail.textContent() || '';
    const business = await this.contactLoc.updating.businessDetail.textContent() || '';
    const roles = await this.contactLoc.updating.rolesDetail.textContent() || '';
    const jobs = await this.contactLoc.updating.jobTitlesDetail.textContent() || '';
    const note = await this.contactLoc.updating.noteDetail.textContent() || '';
    const tags = await this.contactLoc.updating.tagsDetail.allTextContents();

    // Delete unnecessary values
    const cleanEmail = email.replace('Email Address:', '').trim().split(' - ')[0];
    const cleanPhone = phone.replace('Phone Number:', '').trim();
    const cleanBusiness = business.replace('Business:', '').trim().split(' - ')[0];
    const cleanRole = roles.replace('Role:', '').trim().split(' - ')[0];
    const cleanJobTitle = jobs.replace('Job Title:', '').trim();
    const cleanNote = note.replace('Note:', '').trim();

    // Parse role and jobTitle as arrays, handling '--'
    const role = cleanRole === '--' ? [] : cleanRole.split(',').map((r) => r.trim()).filter((r) => r);
    const jobTitle = cleanJobTitle === '--' ? [] : cleanJobTitle.split(',').map((t) => t.trim()).filter((t) => t);
    const cleanTags = tags
      .flatMap((tag) => {
        const cleanTag = tag.trim();
        // Handle '--' or 'Tag' cases
        if (cleanTag === '--' || cleanTag === 'Tag') return [];
        // Split by '#' if concatenated (e.g., '#tag1#tag2'), preserving the '#' prefix
        return cleanTag.includes('#', 1) ? cleanTag.split(/(?=#)/).filter((t) => t && t !== '#') : [cleanTag];
      })
      .map((t) => t.trim())
      .filter((t) => t);

    return {
      contactName: fullName.trim(),
      emailAddress: cleanEmail === '--' ? null : cleanEmail,
      phoneNumber: cleanPhone === '--' ? '' : cleanPhone,
      business: cleanBusiness === '--' ? '' : cleanBusiness,
      role,
      jobTitle,
      note: cleanNote === '--' ? '' : cleanNote,
      tags: cleanTags,
    };
  }

  async open() {
    await this.page.goto("contacts");
  }

  async cleanDashInContactDetail(detailContact: ContactDetail): Promise<ContactDetail> {
    const emailAddress = detailContact.emailAddress?.replace(/\s*-\s*$/, '').trim() ?? null;
    const business = detailContact.business?.replace(/\s*-\s*$/, '').trim() ?? null;
    const role = detailContact.role.map((r) => r.replace(/\s*-\s*$/, '').trim()).filter((r) => r && r !== '--');
    const tags = detailContact.tags.map((r) => r.replace("#", ''));

    return {
      ...detailContact,
      tags,
      emailAddress,
      business,
      role,
    };
  }


  async getContactUpdateDetail(): Promise<ContactUpdate> {
    const firstName = await getInputValueWithRetry(this.contactLoc.updating.inputFname);
    const lastName = await getInputValueWithRetry(this.contactLoc.updating.inputLname);
    const emailAddress = await getInputValueWithRetry(this.contactLoc.updating.inputEmail);
    const phoneNumber = await getInputValueWithRetry(this.contactLoc.updating.inputPhoneNumber);
    const business = await getInputValueWithRetry(this.contactLoc.updating.inputBusiness);
    const note = await getInputValueWithRetry(this.contactLoc.updating.inputNote);

    return {
      first_name: firstName.trim(),
      last_name: lastName.trim(),
      emailAddress: emailAddress.replace(/\s*-\s*$/, '').trim(),
      phoneNumber: await this.normalizePhone(phoneNumber),
      business: business.replace(/\s*-\s*$/, '').trim(),
      note: note.trim()
    };
  }

  async convertToContactDetail(apiData: ApiContactData): Promise<ContactDetail> {

    return {
      emailAddress: apiData.email_address || "",
      phoneNumber: apiData.phone_number || "",
      business: apiData.business ? apiData.business.name : "",
      role: apiData.roles || [],
      jobTitle: apiData.jobs || [],
      note: apiData.note || "",
      tags: apiData.tags || [],
      contactName: apiData.full_name || `${apiData.first_name} ${apiData.last_name}`.trim()
    };
  }

  async convertToContactUpdate(apiData: ApiContactData): Promise<ContactUpdate> {
    const phoneNumber = await this.normalizePhone(apiData.phone_number) || "";
    return {
      emailAddress: apiData.email_address || "",
      phoneNumber,
      business: apiData.business ? apiData.business.name : "",
      note: apiData.note || "",
      first_name: apiData.first_name || "",
      last_name: apiData.last_name || ""
    };
  }

  async clickCreatePage() {
    await this.contactLoc.createNewBtn.click();
  }

  async getTotalFilter(): Promise<number> {
    const resp = await this.page.request.get(`${this.baseUrl}contacts?return_type=json&page=1&group_ids=37`);
    const respJson = await resp.json();
    return respJson.total;
  }

  async getDataTable() {
    const order = await this.getTableHeadingOrder("Group");
    await this.waitForSecond(3); // TODO: refactor to wait api completed or table rendered
    const data = await this.getTableData(order);
    return data;
  }


  async createContactGroupComplete(fields: Array<{ id: string; type: string; value: string; }>): Promise<void> {
    // Open modal and verify visibility
    await this.dashboardLoc.buttonByText("Add New").click();
    await this.dashboardLoc.modal.headerModal("Add New Contact Group").waitFor({ state: 'visible' });
    await this.waitForSecond(1);
    
    for (const field of fields) {
      switch (field.type) {
        case "input":
          await this.dashboardLoc.modal.inputByID(field.id).fill(field.value);
          await expect(this.dashboardLoc.modal.inputByID(field.id)).toHaveValue(field.value);
          break;
        case "select":
          await this.dashboardLoc.modal.inputByID(field.id).fill(field.value);
          await this.contactLoc.contactGroup.optionDropdown(field.value).waitFor({ state: 'visible' });
          await this.waitForSecond(1);
          await this.contactLoc.contactGroup.optionDropdown(field.value).click();
          await this.dashboardLoc.modal.headerModal("Add New Contact Group").click();
          break;
      }
    }

    // Create and verify success
    await this.dashboardLoc.buttonByText("Create").click();
    await this.dashboardLoc.common.pText("Added New Successfully!").waitFor({ state: 'visible' });
    await this.dashboardLoc.btnClosePopupNotification.click();
    await this.dashboardLoc.modal.headerModal("Add New Contact Group").waitFor({ state: 'hidden' });
  }

  async updateContactGroupComplete(fields: Array<{ id: string; type: string; value: string; value_update: string; }>): Promise<void> {
    // Open edit modal and verify
    await this.contactLoc.contactGroup.btnEditContactGroup.first().click();
    await this.dashboardLoc.modal.headerModal("Edit Contact Group").waitFor({ state: 'visible' });
    
    const contactMemberField = fields.find(field => field.id === "multi_select_contact_member_ids");
    if (contactMemberField) {
      await this.dashboardLoc.modal.inputByID(contactMemberField.id).waitFor({ state: 'hidden' });
    }

    for (const field of fields) {
      if (field.id === "multi_select_contact_member_ids") continue;

      switch (field.type) {
        case "input":
          await expect(this.dashboardLoc.modal.inputByID(field.id)).toHaveValue(field.value);
          await this.dashboardLoc.modal.inputByID(field.id).clear();
          await this.dashboardLoc.modal.inputByID(field.id).fill(field.value_update);
          await expect(this.dashboardLoc.modal.inputByID(field.id)).toHaveValue(field.value_update);
          break;
        case "select":
          await this.dashboardLoc.modal.tag(field.value).waitFor({ state: 'visible' });
          await this.waitForSecond(2);
          await this.dashboardLoc.tag.btnClearAllTag.click();
          await this.dashboardLoc.modal.inputByID(field.id).fill(field.value_update);
          await this.contactLoc.contactGroup.optionDropdown(field.value_update).waitFor({ state: 'visible' });
          await this.contactLoc.contactGroup.optionDropdown(field.value_update).click();
          await this.page.mouse.click(0, 0);
          break;
      }
    }

    // Update and verify success
    await this.dashboardLoc.buttonByText("Update").click();
    await this.dashboardLoc.common.pText("Updated Successfully!").waitFor({ state: 'visible' });
    await this.dashboardLoc.btnClosePopupNotification.click();
    await this.dashboardLoc.modal.headerModal("Edit Contact Group").waitFor({ state: 'hidden' });
  }

  async verifyContactGroupInNewTab(contactName: string, groupName: string | null | undefined): Promise<void> {
    const newPage = await this.page.context().newPage();
    const newContactPage = new ContactPage(newPage);
    const cookies = await this.page.context().cookies();
    await newPage.context().addCookies(cookies);

    await newContactPage.open();
    await newContactPage.getDetailCase(contactName);
    await newContactPage.dashboardLoc.buttonByText("Update").first().click();

    if (groupName) {
      await newContactPage.dashboardLoc.modal.tag(groupName).waitFor({ state: 'visible' });
    } else {
      const fieldValue = await newContactPage.dashboardLoc.modal.inputByID("multi_select_group_ids").inputValue();
      if (fieldValue !== "") {
        await newPage.close();
        throw new Error(`Contact group field should be empty after deletion, but contains: "${fieldValue}"`);
      }
    }

    await newPage.close();
  }
}

