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

interface FilterTestData {
  createdAt: string;
  serviceType: string;
}

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

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

const CustomFilterComponent: React.FC<CustomFilterProps> = () => {
  return <div data-testid="custom-filter"></div>;
};

const customFilter: FilterType<{ custom: string }> = {
  label: 'Custom Filter',
  property: 'custom',
  type: FilterTypes.Custom,
  component: CustomFilterComponent,
};

const sampleText = 'sample text';
const spy = vi.fn();
const onFilterClickedSpy = vi.fn();

const defaultProps = {
  onFilterChange: spy,
  onFilterClicked: onFilterClickedSpy,
  isActive: false,
} as unknown as FilterProps<FilterTestData>;

describe('Filter', () => {
  afterEach(() => {
    vi.clearAllMocks();
  });

  it('FreeTextFilter is rendered only if click on filter title', () => {
    const { container } = render(
      <Filter {...defaultProps} options={freeTextFilter} />,
    );
    expect(
      container.querySelector('.free-text-filter-container'),
    ).not.toBeInTheDocument();

    const title = container.querySelector('.title') as HTMLElement;
    fireEvent.click(title);
    expect(
      container.querySelector('.free-text-filter-container'),
    ).toBeInTheDocument();
  });

  it('OptionsFilter is rendered only if click on filter title', () => {
    const { container } = render(
      <Filter {...defaultProps} options={optionFilter} />,
    );
    expect(
      container.querySelector('.options-filter-container'),
    ).not.toBeInTheDocument();

    const title = container.querySelector('.title') as HTMLElement;
    fireEvent.click(title);
    expect(
      container.querySelector('.options-filter-container'),
    ).toBeInTheDocument();
  });

  it.each([
    [true, true, true],
    [false, true, false],
    [true, false, false],
  ])('filter highlight', (isExpanded, isActive, expected) => {
    const { container } = render(
      <Filter {...defaultProps} options={freeTextFilter} isActive={isActive} />,
    );

    if (isExpanded) {
      const title = container.querySelector('.title') as HTMLElement;
      fireEvent.click(title);
    }

    const filterTitle = container.querySelector('.title');

    expect(filterTitle?.classList.contains('active')).toBe(expected);
  });

  it('passes active state to custom filter', () => {
    const { container, rerender } = render(
      <Filter
        onFilterChange={() => null}
        onFilterClicked={() => null}
        options={customFilter}
        isActive={false}
      />,
    );

    const title = container.querySelector('.title') as HTMLElement;
    fireEvent.click(title);

    expect(
      container.querySelector('[data-testid="custom-filter"]'),
    ).toBeInTheDocument();

    rerender(
      <Filter
        onFilterChange={() => null}
        onFilterClicked={() => null}
        options={customFilter}
        isActive={true}
      />,
    );

    // Custom filter should still be present with updated props
    expect(
      container.querySelector('[data-testid="custom-filter"]'),
    ).toBeInTheDocument();
  });

  it(`selected filter raises 'onFilterClicked' when clicked`, () => {
    const { container } = render(
      <Filter
        {...defaultProps}
        options={freeTextFilter}
        onFilterClicked={onFilterClickedSpy}
      />,
    );
    expect(
      container.querySelector('.free-text-filter-container'),
    ).not.toBeInTheDocument();

    const filter = container.querySelector('.filter-container') as HTMLElement;
    fireEvent.click(filter);

    expect(onFilterClickedSpy).toHaveBeenCalledTimes(1);
  });

  it('Selected filter value is displayed when a value is passed in', () => {
    const { container } = render(
      <Filter {...defaultProps} options={freeTextFilter} value={sampleText} />,
    );

    const selectedValue = container.querySelector('.selectedValue');
    expect(selectedValue).toBeInTheDocument();
    expect(selectedValue?.textContent).toEqual(sampleText);
  });

  it('Selected filter value is not displayed when no value is present', () => {
    const { container } = render(
      <Filter {...defaultProps} options={freeTextFilter} />,
    );

    expect(container.querySelector('.selectedValue')).not.toBeInTheDocument();
  });

  it(`'selectedValueRenderer' value is displayed when the prop is set`, () => {
    const renderedValue = `${sampleText} renderer`;
    const selectedValueRenderer: SelectedValueRenderer = (value) => {
      return `${value} renderer`;
    };

    const { container, rerender } = render(
      <Filter {...defaultProps} options={freeTextFilter} value={sampleText} />,
    );

    let selectedValue = container.querySelector('.selectedValue');
    expect(selectedValue?.textContent).toEqual(sampleText);

    rerender(
      <Filter
        {...defaultProps}
        options={freeTextFilter}
        value={sampleText}
        selectedValueRenderer={selectedValueRenderer}
      />,
    );

    selectedValue = container.querySelector('.selectedValue');
    expect(selectedValue?.textContent).toEqual(renderedValue);
  });

  it('Raises onFilterChange with prop name and new undefined value when filter is closed', () => {
    const mockValue = 'test-value';
    const { container } = render(
      <Filter {...defaultProps} options={freeTextFilter} value={mockValue} />,
    );
    const buttons = container.querySelectorAll('button');
    const closeBtn = buttons[buttons.length - 1];

    fireEvent.click(closeBtn);

    expect(spy).toHaveBeenCalledTimes(1);
    expect(spy).toHaveBeenLastCalledWith(
      freeTextFilter.property,
      undefined,
      -1,
    );
  });
});
