import { fireEvent, render } from '@testing-library/react';
import React from 'react';
import { describe, expect, it, vi } from 'vitest';
import { Filters } from './Filters';
import { FilterType, FilterTypes, FilterValues } from './Filters.model';

interface TestFilterData {
  createdAt: string;
  serviceType: string;
  limits: number;
}

const freeTextFilter: FilterType<TestFilterData> = {
  label: 'Created At',
  property: 'createdAt',
  type: FilterTypes.FreeText,
};

const optionFilter: FilterType<TestFilterData> = {
  label: 'Service Type',
  property: 'serviceType',
  type: FilterTypes.Options,
  options: [
    {
      label: 'CREW',
      value: 'CREW',
    },
  ],
};

const lowerLimitFilter: FilterType<TestFilterData> = {
  label: 'Lower Limit',
  property: 'limits',
  type: FilterTypes.Numeric,
};

const upperLimitFilter: FilterType<TestFilterData> = {
  label: 'Upper Limit',
  property: 'limits',
  type: FilterTypes.Numeric,
};

const excludeLimitFilter: FilterType<TestFilterData> = {
  label: 'Exclude Limit',
  property: 'limits',
  type: FilterTypes.Numeric,
};

const spy = vi.fn();

describe('Filters', () => {
  it('Filter is rendered and FreeTextFilter is not rendered', () => {
    const { container } = render(
      <Filters options={[freeTextFilter]} onFiltersChange={spy} />,
    );
    expect(container.querySelector('.filter-container')).toBeInTheDocument();
    expect(
      container.querySelector('.free-text-filter'),
    ).not.toBeInTheDocument();
  });

  it('Filter is rendered and OptionsFilter is not rendered', () => {
    const { container } = render(
      <Filters options={[optionFilter]} onFiltersChange={spy} />,
    );
    expect(container.querySelector('.filter-container')).toBeInTheDocument();
    expect(container.querySelector('.options-filter')).not.toBeInTheDocument();
  });

  it(`renders the Filter container's styles when there are filters`, () => {
    const { container } = render(
      <Filters options={[optionFilter]} onFiltersChange={spy} />,
    );
    expect(container.querySelector('.container')).toBeInTheDocument();
  });

  it(`does not render the Filter container's styles when there are no filters`, () => {
    const { container } = render(<Filters />);
    expect(container.querySelector('.container')).not.toBeInTheDocument();
  });

  it(`renders active filters when the component is loaded`, () => {
    const mockActiveFilters: FilterValues<TestFilterData> = {
      serviceType: 'CREW',
      createdAt: 'entered-value',
    };
    const { container } = render(
      <Filters
        options={[optionFilter, freeTextFilter]}
        onFiltersChange={spy}
        defaultValues={mockActiveFilters}
      />,
    );
    const activeFilters = container.querySelectorAll('.selectedValue>span');

    activeFilters.forEach((filterLabel, index) => {
      expect(filterLabel.textContent).toBe(
        Object.values(mockActiveFilters)[index],
      );
    });
  });

  it('returns an array of values if two or more filters share the same property name', () => {
    const localSpy = vi.fn();
    const { container } = render(
      <Filters<TestFilterData>
        options={[lowerLimitFilter, upperLimitFilter, excludeLimitFilter]}
        onFiltersChange={localSpy}
      />,
    );

    const filters = container.querySelectorAll(
      '[data-test-id="filter:limits"]',
    );
    expect(filters).toHaveLength(3);

    const values = [10, 20, 30];
    filters.forEach((filterElement, index) => {
      const title = filterElement.querySelector('.title') as HTMLElement;
      fireEvent.click(title);

      const input = filterElement.querySelector(
        '.inputValue',
      ) as HTMLInputElement;
      fireEvent.change(input, { target: { value: String(values[index]) } });
      fireEvent.keyDown(input, { key: 'Enter' });
    });

    expect(localSpy).toHaveBeenCalled();
    const lastCall = localSpy.mock.calls[localSpy.mock.calls.length - 1];
    expect(lastCall[0].limits).toEqual(values);
  });

  it('only the clicked filter should be active', () => {
    const { container } = render(
      <Filters
        options={[freeTextFilter, optionFilter]}
        onFiltersChange={spy}
      />,
    );

    const firstFilter = container.querySelector(
      '[data-test-id="filter:createdAt"]',
    ) as HTMLElement;
    const secondFilter = container.querySelector(
      '[data-test-id="filter:serviceType"]',
    ) as HTMLElement;

    // Click on the first filter
    fireEvent.click(firstFilter);

    // Click on the second filter
    fireEvent.click(secondFilter);

    // Both filters should be present
    const filters = container.querySelectorAll('[data-test-id^="filter:"]');
    expect(filters).toHaveLength(2);
  });
});
