import { faker } from '@faker-js/faker';
import { getCustomerCreateData, getCustomerPhone } from '@/manager/modules/customer/customer-factories';
import { test } from '@/manager/modules/customer/customer-fixtures';
import { applyCustomerEditGeneral } from '@/manager/modules/customer/customer-mappers';
import { formatCustomerName } from '@/manager/modules/customer/customer-utils';
import { customerEditGeneralHappyTestCases } from '@/manager/modules/customer/test-cases/customer-edit-general-happy-test-cases';
import {
  expectDataTableChipsColumnToHaveText,
  expectDataTableTextColumnToHaveText,
} from '@/manager/modules/ui/data-table/data-table-assertions';
import { customerTableColumnTestIds } from '@/manager/modules/ui/data-table/data/data-table-column-test-ids';
import { expectMultiLocatorToHaveText, expectSingleLocatorToHaveText } from '@/manager/shared/utils/expect-utils';
import { countries } from '@/shared/data/seed-countries';
import { vatTypes } from '@/shared/data/seed-vat-types';
import { formatAddress, formatDate, formatId, formatPhone } from '@/shared/utils/formatters';
import { expect } from '@/shared/utils/matchers';

for (const tc of customerEditGeneralHappyTestCases) {
  test(tc.description, async ({ createCustomer, customerDetailsPage }) => {
    const customer = await createCustomer(getCustomerCreateData(tc.initialOptions));
    const newCustomer = applyCustomerEditGeneral(customer, tc.changes);
    const customerFullName = formatCustomerName(newCustomer.firstName, newCustomer.lastName);
    const customerTitle = newCustomer.type === 'business' ? newCustomer.companyName : customerFullName;
    const customerPhone = formatPhone(newCustomer.phone);
    const customerAddress = formatAddress(
      newCustomer.street,
      newCustomer.postalCode,
      newCustomer.city,
      newCustomer.country.name
    );
    const customerId = formatId(newCustomer.id, '-', 4);
    const customerCreatedAt = formatDate(newCustomer.createdAt);
    const customerLocationNames = newCustomer.locations.map((l) => l.name);

    await customerDetailsPage.goto(customer.id);

    const newDetailsPage = await test.step('edit customer general', async () => {
      const dialog = await customerDetailsPage.openCustomerEditGeneralDialog();
      return dialog.edit(tc.changes);
    });

    await test.step('verify new customer on details page', async () => {
      await expectSingleLocatorToHaveText(newDetailsPage.title, customerTitle);
      await expectSingleLocatorToHaveText(newDetailsPage.status, newCustomer.status);
      await expectMultiLocatorToHaveText(newDetailsPage.generalCard.locations, customerLocationNames);
      await expectSingleLocatorToHaveText(newDetailsPage.generalCard.type, newCustomer.type);

      if (newCustomer.type === 'business') {
        await expectSingleLocatorToHaveText(newDetailsPage.generalCard.companyName, newCustomer.companyName);
        await expectSingleLocatorToHaveText(newDetailsPage.generalCard.vatType, newCustomer.vatType?.name);
        await expectSingleLocatorToHaveText(newDetailsPage.generalCard.vatNumber, newCustomer.vatNumber);
      } else {
        await expect(newDetailsPage.generalCard.companyName).toBeHidden();
        await expect(newDetailsPage.generalCard.vatType).toBeHidden();
        await expect(newDetailsPage.generalCard.vatNumber).toBeHidden();
      }

      await expectSingleLocatorToHaveText(newDetailsPage.generalCard.name, customerFullName);
      await expectSingleLocatorToHaveText(newDetailsPage.generalCard.email, newCustomer.email);
      await expectSingleLocatorToHaveText(newDetailsPage.generalCard.phoneNumber, customerPhone);
      await expectSingleLocatorToHaveText(newDetailsPage.generalCard.language, newCustomer.language.id);
      await expectSingleLocatorToHaveText(newDetailsPage.generalCard.address, customerAddress);
      await expectSingleLocatorToHaveText(newDetailsPage.generalCard.note, newCustomer.note);
    });

    await test.step('verify new customer on list page', async () => {
      const listPage = await newDetailsPage.returnToCustomerListPage();

      await listPage.searchTextField.fill(newCustomer.email);

      await expect(listPage.dataTable.getRows()).toHaveCount(1);
      await expectDataTableTextColumnToHaveText(listPage.dataTable, customerTableColumnTestIds.id, customerId);
      await expectDataTableTextColumnToHaveText(listPage.dataTable, customerTableColumnTestIds.name, customerTitle);
      await expectDataTableChipsColumnToHaveText(
        listPage.dataTable,
        customerTableColumnTestIds.locations,
        customerLocationNames
      );
      await expectDataTableTextColumnToHaveText(
        listPage.dataTable,
        customerTableColumnTestIds.email,
        newCustomer.email
      );
      await expectDataTableTextColumnToHaveText(
        listPage.dataTable,
        customerTableColumnTestIds.phoneNumber,
        customerPhone
      );
      await expectDataTableChipsColumnToHaveText(
        listPage.dataTable,
        customerTableColumnTestIds.status,
        newCustomer.status
      );
      await expectDataTableTextColumnToHaveText(
        listPage.dataTable,
        customerTableColumnTestIds.createdAt,
        customerCreatedAt
      );
    });
  });
}

test('edits the email', async ({ createCustomer, customerDetailsPage }) => {
  const customer = await createCustomer(getCustomerCreateData());
  const newEmail = faker.internet.email();

  await customerDetailsPage.goto(customer.id);

  const newDetailsPage = await test.step('edit customer email', async () => {
    const dialog = await customerDetailsPage.openCustomerEditGeneralDialog();
    return dialog.edit({ email: newEmail });
  });

  await test.step('verify new email on details page', async () => {
    await expectSingleLocatorToHaveText(newDetailsPage.generalCard.email, newEmail);
  });

  await test.step('verify new email on list page', async () => {
    const listPage = await newDetailsPage.returnToCustomerListPage();
    await listPage.searchTextField.fill(newEmail);
    await expect(listPage.dataTable.getRows()).toHaveCount(1);
  });
});

test('edits the phone number', async ({ createCustomer, customerDetailsPage }) => {
  const customer = await createCustomer(getCustomerCreateData());
  const newCountry = countries.BR;
  const newPhone = getCustomerPhone(newCountry);
  const formattedNewPhone = formatPhone(newPhone);

  await customerDetailsPage.goto(customer.id);

  const newDetailsPage = await test.step('edit customer phone number', async () => {
    const dialog = await customerDetailsPage.openCustomerEditGeneralDialog();
    return dialog.edit({ phone: newPhone, country: newCountry });
  });

  await test.step('verify new phone number on details page', async () => {
    await expectSingleLocatorToHaveText(newDetailsPage.generalCard.phoneNumber, formattedNewPhone);
  });

  await test.step('verify new phone number on list page', async () => {
    const listPage = await newDetailsPage.returnToCustomerListPage();
    await listPage.searchTextField.fill(formattedNewPhone);
    await expect(listPage.dataTable.getRows()).toHaveCount(1);
  });
});

test('rejects an edit with a duplicate email', async ({ createCustomer, customerDetailsPage }) => {
  const existing = await createCustomer(getCustomerCreateData());
  const editing = await createCustomer(getCustomerCreateData());

  await customerDetailsPage.goto(editing.id);

  const dialog = await test.step('submit edit with duplicate email', async () => {
    const editDialog = await customerDetailsPage.openCustomerEditGeneralDialog();
    await editDialog.fill({ email: existing.email });
    await editDialog.submit();
    return editDialog;
  });

  await test.step('verify errors on form', async () => {
    await expect(dialog.errors).toHaveCountGreaterThan(0);
  });
});

test('rejects an edit with a duplicate phone number', async ({ createCustomer, customerDetailsPage }) => {
  const existing = await createCustomer(getCustomerCreateData());
  const editing = await createCustomer(getCustomerCreateData());

  await customerDetailsPage.goto(editing.id);

  const dialog = await test.step('submit edit with duplicate phone number', async () => {
    const editDialog = await customerDetailsPage.openCustomerEditGeneralDialog();
    await editDialog.fill({ phone: existing.phone, country: existing.country });
    await editDialog.submit();
    return editDialog;
  });

  await test.step('verify errors on form', async () => {
    await expect(dialog.errors).toHaveCountGreaterThan(0);
  });
});

test('rejects an edit with a vat number that does not match the vat type', async ({
  createCustomer,
  customerDetailsPage,
}) => {
  const customer = await createCustomer(getCustomerCreateData({ type: 'business' }));

  await customerDetailsPage.goto(customer.id);

  const dialog = await test.step('submit edit with mismatched vat type and number', async () => {
    const editDialog = await customerDetailsPage.openCustomerEditGeneralDialog();
    await editDialog.fill({ vatType: vatTypes.eu_vat, vatNumber: 'INVALID-VAT-1234' });
    await editDialog.submit();
    return editDialog;
  });

  await test.step('verify errors on form', async () => {
    await expect(dialog.errors).toHaveCountGreaterThan(0);
  });
});

test('rejects an edit without all required fields', async ({ createCustomer, customerDetailsPage }) => {
  const customer = await createCustomer(getCustomerCreateData({ type: 'private' }));

  await customerDetailsPage.goto(customer.id);

  const dialog = await test.step('submit edit clearing required fields', async () => {
    const editDialog = await customerDetailsPage.openCustomerEditGeneralDialog();
    await editDialog.fill({ firstName: '', lastName: '' });
    await editDialog.submit();
    return editDialog;
  });

  await test.step('verify errors on form', async () => {
    await expect(dialog.errors).toHaveCountGreaterThan(0);
  });
});
