import '@angular/compiler';
import { type ComponentRef } from '@angular/core';
import { type ComponentFixture, TestBed } from '@angular/core/testing';
import { TranslocoTestingModule } from '@jsverse/transloco';
import type { DataTableColumnDef, DataTableData } from '../data-table/types';
import { GridPickDialog } from './grid-pick-dialog.component';

interface Permission {
  id: string;
  name: string;
}

const PERMISSIONS_PAGE_1: Permission[] = [
  { id: 'p1', name: 'Read' },
  { id: 'p2', name: 'Write' },
  { id: 'p3', name: 'Delete' },
];

const PERMISSIONS_PAGE_2: Permission[] = [
  { id: 'p4', name: 'Admin' },
  { id: 'p5', name: 'Audit' },
];

const COLUMNS: DataTableColumnDef<Permission>[] = [
  { id: 'name', header: 'Name', accessorKey: 'name' },
];

const dataPage = (rows: Permission[]): DataTableData<Permission> => ({
  count: rows.length,
  total: rows.length + 7,
  rows,
});

describe('GridPickDialog', () => {
  let fixture: ComponentFixture<GridPickDialog<Permission>>;
  let component: GridPickDialog<Permission>;
  let componentRef: ComponentRef<GridPickDialog<Permission>>;

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [
        GridPickDialog,
        TranslocoTestingModule.forRoot({
          langs: { en: {} },
          translocoConfig: { defaultLang: 'en' },
        }),
      ],
    });
    fixture = TestBed.createComponent<GridPickDialog<Permission>>(GridPickDialog);
    component = fixture.componentInstance;
    componentRef = fixture.componentRef;
    componentRef.setInput('columns', COLUMNS);
    componentRef.setInput('data', dataPage(PERMISSIONS_PAGE_1));
    componentRef.setInput('open', true);
    componentRef.setInput('multi', true);
    fixture.detectChanges();
  });

  describe('multi=true Apply emits selected ids', () => {
    it('emits linkRequested with the selection at Apply time', () => {
      // biome-ignore lint/suspicious/noExplicitAny: read the protected binding.selection atom
      (component as any).binding.selection.setIds(['p1', 'p3']);

      const emitted: string[][] = [];
      component.linkRequested.subscribe((ids) => emitted.push(ids));

      const closeSpy = vi.fn();
      // biome-ignore lint/suspicious/noExplicitAny: invoke protected handler
      (component as any).onApply({ close: closeSpy });

      expect(emitted.length).toBe(1);
      expect(emitted[0].sort()).toEqual(['p1', 'p3']);
      expect(closeSpy).toHaveBeenCalledTimes(1);
    });
  });

  describe('disabledIds prevents single-pick selection', () => {
    it('blocks click on a disabled row in multi=false mode', () => {
      componentRef.setInput('multi', false);
      componentRef.setInput('disabledIds', new Set(['p1']));
      fixture.detectChanges();

      const emitted: string[][] = [];
      component.linkRequested.subscribe((ids) => emitted.push(ids));

      // biome-ignore lint/suspicious/noExplicitAny: invoke protected handler
      (component as any).onRowClick({ id: 'p1', name: 'Read' });

      expect(emitted).toEqual([]);
    });

    it('emits on click of non-disabled row in multi=false mode', () => {
      componentRef.setInput('multi', false);
      componentRef.setInput('disabledIds', new Set<string>());
      fixture.detectChanges();

      const emitted: string[][] = [];
      component.linkRequested.subscribe((ids) => emitted.push(ids));

      // biome-ignore lint/suspicious/noExplicitAny: invoke protected handler
      (component as any).onRowClick({ id: 'p2', name: 'Write' });

      expect(emitted).toEqual([['p2']]);
    });
  });

  describe('selection persists across data input changes', () => {
    it('keeps previously-selected id when data swaps to a different page', () => {
      // biome-ignore lint/suspicious/noExplicitAny: access protected binding
      (component as any).binding.selection.setIds(['p1']);

      componentRef.setInput('data', dataPage(PERMISSIONS_PAGE_2));
      fixture.detectChanges();

      // biome-ignore lint/suspicious/noExplicitAny: read protected atom
      const ids = (component as any).binding.selection.ids() as Set<string>;
      expect(ids.has('p1')).toBe(true);

      const emitted: string[][] = [];
      component.linkRequested.subscribe((received) => emitted.push(received));
      const closeSpy = vi.fn();
      // biome-ignore lint/suspicious/noExplicitAny: invoke protected handler
      (component as any).onApply({ close: closeSpy });

      expect(emitted).toEqual([['p1']]);
    });
  });

  describe('cancel emits closed without linkRequested', () => {
    it('does not emit linkRequested on cancel', () => {
      // biome-ignore lint/suspicious/noExplicitAny: access protected binding
      (component as any).binding.selection.setIds(['p1', 'p2']);

      const linkEmitted: string[][] = [];
      component.linkRequested.subscribe((ids) => linkEmitted.push(ids));

      const closeSpy = vi.fn();
      // biome-ignore lint/suspicious/noExplicitAny: invoke protected handler
      (component as any).onCancel({ close: closeSpy });

      expect(linkEmitted).toEqual([]);
      expect(closeSpy).toHaveBeenCalledTimes(1);
    });
  });

  describe('open=false resets selection', () => {
    it('clears selection when open transitions to false', () => {
      // biome-ignore lint/suspicious/noExplicitAny: access protected binding
      (component as any).binding.selection.setIds(['p1']);

      componentRef.setInput('open', false);
      fixture.detectChanges();

      // biome-ignore lint/suspicious/noExplicitAny: read protected atom
      const ids = (component as any).binding.selection.ids() as Set<string>;
      expect(ids.size).toBe(0);
    });
  });
});
