import { expectDataTableTextColumnToHaveText } from '@/manager/modules/ui/data-table/data-table-assertions';
import { unitTableColumnTestIds } from '@/manager/modules/ui/data-table/data/data-table-column-test-ids';
import { unitEditGeneralHappyTestCases } from '@/manager/modules/unit/test-cases/unit-edit-general-happy-test-cases';
import { getUnitCreateData } from '@/manager/modules/unit/unit-factories';
import { test } from '@/manager/modules/unit/unit-fixtures';
import { applyUnitEditGeneral } from '@/manager/modules/unit/unit-mappers';
import { getUnitTypeCreateData } from '@/manager/modules/unit-type/unit-type-factories';
import { expectSingleLocatorToHaveText } from '@/manager/shared/utils/expect-utils';
import { locations } from '@/shared/data/seed-locations';
import { formatId } from '@/shared/utils/formatters';
import { expect } from '@/shared/utils/matchers';

for (const tc of unitEditGeneralHappyTestCases) {
  test(tc.description, async ({ createUnit, createUnitType, unitDetailsPage }) => {
    const unitType = await createUnitType(getUnitTypeCreateData());
    const unit = await createUnit(getUnitCreateData(unitType, tc.initialOptions));
    const newUnit = applyUnitEditGeneral(unit, tc.changes);
    const id = formatId(unit.id);

    await unitDetailsPage.goto(unit.id);

    const newDetailsPage = await test.step('edit unit general', async () => {
      const dialog = await unitDetailsPage.openUnitEditGeneralDialog();
      return dialog.edit(tc.changes);
    });

    await test.step('verify new unit on details page', async () => {
      await expectSingleLocatorToHaveText(newDetailsPage.title, newUnit.name);
      await expectSingleLocatorToHaveText(newDetailsPage.generalCard.name, newUnit.name);
      await expectSingleLocatorToHaveText(newDetailsPage.generalCard.location, newUnit.location.name);
      await expectSingleLocatorToHaveText(newDetailsPage.generalCard.unitType, newUnit.unitType.name);
      await expectSingleLocatorToHaveText(newDetailsPage.generalCard.note, newUnit.note);
    });

    await test.step('verify new unit on list page', async () => {
      const listPage = await newDetailsPage.returnToUnitListPage();
      await listPage.searchTextField.fill(id);
      await expect(listPage.dataTable.getRows()).toHaveCount(1);
      await expectDataTableTextColumnToHaveText(listPage.dataTable, unitTableColumnTestIds.name, newUnit.name);
      await expectDataTableTextColumnToHaveText(
        listPage.dataTable,
        unitTableColumnTestIds.location,
        newUnit.location.name
      );
      await expectDataTableTextColumnToHaveText(
        listPage.dataTable,
        unitTableColumnTestIds.unitType,
        newUnit.unitType.name
      );
    });
  });
}

test('edits the unit type', async ({ createUnit, createUnitType, unitDetailsPage }) => {
  const unitType = await createUnitType(getUnitTypeCreateData());
  const [newUnitType, unit] = await Promise.all([
    createUnitType(getUnitTypeCreateData({ location: unitType.location })),
    createUnit(getUnitCreateData(unitType)),
  ]);
  const id = formatId(unit.id);

  await unitDetailsPage.goto(unit.id);

  const newDetailsPage = await test.step('edit unit type', async () => {
    const dialog = await unitDetailsPage.openUnitEditGeneralDialog();
    return dialog.edit({ unitType: newUnitType });
  });

  await test.step('verify new unit type on details page', async () => {
    await expectSingleLocatorToHaveText(newDetailsPage.generalCard.unitType, newUnitType.name);
  });

  await test.step('verify new unit type on list page', async () => {
    const listPage = await newDetailsPage.returnToUnitListPage();
    await listPage.searchTextField.fill(id);
    await expect(listPage.dataTable.getRows()).toHaveCount(1);
    await expectDataTableTextColumnToHaveText(listPage.dataTable, unitTableColumnTestIds.unitType, newUnitType.name);
  });
});

test('edits the location and unit type', async ({ createUnit, createUnitType, unitDetailsPage }) => {
  const [unitType, newUnitType] = await Promise.all([
    createUnitType(getUnitTypeCreateData({ location: locations.viennaSouth })),
    createUnitType(getUnitTypeCreateData({ location: locations.viennaNorth })),
  ]);
  const unit = await createUnit(getUnitCreateData(unitType));
  const id = formatId(unit.id);

  await unitDetailsPage.goto(unit.id);

  const newDetailsPage = await test.step('edit location and unit type', async () => {
    const dialog = await unitDetailsPage.openUnitEditGeneralDialog();
    return dialog.edit({ location: newUnitType.location, unitType: newUnitType });
  });

  await test.step('verify new unit on details page', async () => {
    await expectSingleLocatorToHaveText(newDetailsPage.generalCard.location, newUnitType.location.name);
    await expectSingleLocatorToHaveText(newDetailsPage.generalCard.unitType, newUnitType.name);
  });

  await test.step('verify new unit on list page', async () => {
    const listPage = await newDetailsPage.returnToUnitListPage();
    await listPage.searchTextField.fill(id);
    await expect(listPage.dataTable.getRows()).toHaveCount(1);
    await expectDataTableTextColumnToHaveText(
      listPage.dataTable,
      unitTableColumnTestIds.location,
      newUnitType.location.name
    );
    await expectDataTableTextColumnToHaveText(listPage.dataTable, unitTableColumnTestIds.unitType, newUnitType.name);
  });
});

test('rejects an edit with a duplicate name', async ({ createUnit, createUnitType, unitDetailsPage }) => {
  const unitType = await createUnitType(getUnitTypeCreateData());
  const [existing, editing] = await Promise.all([
    createUnit(getUnitCreateData(unitType)),
    createUnit(getUnitCreateData(unitType)),
  ]);

  await unitDetailsPage.goto(editing.id);

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

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

test('rejects an edit changing location without a unit type', async ({
  createUnit,
  createUnitType,
  unitDetailsPage,
}) => {
  const unitType = await createUnitType(getUnitTypeCreateData({ location: locations.viennaSouth }));
  const unit = await createUnit(getUnitCreateData(unitType));

  await unitDetailsPage.goto(unit.id);

  const dialog = await test.step('submit edit changing only the location', async () => {
    const editDialog = await unitDetailsPage.openUnitEditGeneralDialog();
    await editDialog.fill({ location: locations.viennaNorth });
    await editDialog.submit();
    return editDialog;
  });

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