import { expect } from '@/core/fixtures';
import { AbstractDropdownLikeInputWidget } from './AbstractFormInputWidget';
import { Locator } from 'playwright';
import { widgetStep } from '@/core/utils';
import { getLocator } from '@/core/locators';

class SelectLikeInput extends AbstractDropdownLikeInputWidget {
  get searchInputLocator() {
    return getLocator('input.select.searchInput', this.menuLocator);
  }
  get optionsList() {
    return getLocator('input.select.optionsList', this.menuLocator);
  }

  async baseSelect(label?: string, value?: string) {
    await this.getOptionLocator(label, value).click();
  }

  getOptionLocator(label?: string, value?: string) {
    const selectorArr = ['[role="option"]'];
    if (value) selectorArr.push(`[data-testid="option-${value}"]`);

    return this.optionsList
      .locator(selectorArr.join(''))
      .filter({ hasText: label })
      .first();
  }

  @widgetStep
  async clear(force?: boolean) {
    const locator = getLocator('input.select.clear', this.l);

    if (force) {
      const count = await locator.count();
      if (count > 0) {
        await locator.click();
      }
    } else {
      await locator.click();
    }

    return this;
  }
}

export class SelectInput extends SelectLikeInput {
  type = 'formSelectInput';

  @widgetStep
  async select(label?: string, id?: string) {
    if (!id && !label)
      throw new Error("id and label can't be empty both at the same time");
    await this.toggle();

    await this.getOptionLocator(label, id).click();

    return this;
  }

  @widgetStep
  async selectFirstOption() {
    await this.toggle();
    await this.getOptionLocator().click();
    await this.page.waitForTimeout(50);
    return this;
  }
}

class MultiSelectLikeInput extends SelectLikeInput {
  getOptionLocatorInValue(label?: string, value?: string) {
    return getMultiOptionLocatorInValue(this.l, label, value);
  }

  @widgetStep
  async unselect(label?: string, id?: string) {
    await this.getOptionLocatorInValue(label, id).click();
  }

  @widgetStep
  async hasOptionSelected(label?: string, id?: string) {
    await expect(this.getOptionLocatorInValue(label, id)).toBeVisible();
  }

  @widgetStep
  async hasFewOptionSelected(options: { label?: string; value?: string }[]) {
    for (const { label, value } of options) {
      await this.hasOptionSelected(label, value);
    }
  }

  @widgetStep
  async hasNotOptionSelected(label?: string, id?: string) {
    await expect(this.getOptionLocatorInValue(label, id)).not.toBeVisible();
  }

  @widgetStep
  async isEmpty() {
    await expect(this.getOptionLocatorInValue()).not.toBeVisible();
  }
}

export class MultiSelectInput extends MultiSelectLikeInput {
  type = 'formMultiSelectInput';

  @widgetStep
  async select(label?: string, value?: string) {
    if (!value && !label)
      throw new Error("value and label can't be empty both at the same time");
    await this.toggle();
    await this.baseSelect(label, value);
    await this.close();
    return this;
  }

  @widgetStep
  async selectFew(options: { label?: string; value?: string }[]) {
    await this.toggle();
    for (const { label, value } of options) {
      await this.baseSelect(label, value);
      await this.page.waitForTimeout(20);
    }
    await this.close();
  }

  @widgetStep
  async selectFirstOption() {
    await this.toggle();
    await this.menuLocator.locator('[role="option"] > div').first().click();
    await this.close();
    return this;
  }
}

export class SearchInput extends SelectLikeInput {
  type = 'formSearchInput';

  @widgetStep
  async selectByLabel(label: string) {
    if (!label) throw new Error("Label can't be empty");
    await this.toggle();

    const selectorArr = ['[role="option"]'];

    await this.searchInputLocator.fill(label);
    let locator = this.menuLocator
      .locator(selectorArr.join(''))
      .filter({ hasText: label });

    await locator.click();

    return this;
  }

  @widgetStep
  async selectFirstBySearch(search: string) {
    if (!search) throw new Error("Search can't be empty");
    await this.toggle();

    await this.searchInputLocator.fill(search);
    await this.getOptionLocator().click();
    return this;
  }
}

export class MultiSearchInput extends MultiSelectLikeInput {
  type = 'formMultiSearchInput';

  @widgetStep
  async selectByLabel(label: string) {
    if (!label) throw new Error("Label can't be empty");
    await this.toggle();

    await this.searchInputLocator.fill(label);
    await this.optionsList
      .locator('div', { hasNotText: 'Vybrat vše' })
      .filter({ hasText: label })
      .click();

    return this;
  }

  @widgetStep
  async selectFew(options: string[]) {
    await this.toggle();
    for (const label of options) {
      await this.page.waitForTimeout(20);
      await this.searchInputLocator.fill(label);
      await this.baseSelect(label);
    }
    await this.close();
  }

  @widgetStep
  async selectFirstBySearch(search: string) {
    if (!search) throw new Error("Search can't be empty");
    await this.toggle();

    await this.searchInputLocator.fill(search);
    await this.getOptionLocator().click();
    await this.page.waitForTimeout(200);

    await this.close();
    return this;
  }
}

function getMultiOptionLocatorInValue(
  locator: Locator,
  label?: string,
  id?: string
) {
  let optionLocator = '[data-role="option"]';
  if (label) optionLocator += `[title="${label}"]`;
  if (id) optionLocator += `[data-value="${id}"]`;
  return locator.locator(`${optionLocator} svg[data-icon="xmark"]`);
}
