import { signal } from '@angular/core';
import { TestBed } from '@angular/core/testing';
import { provideRouter } from '@angular/router';
import { of, throwError } from 'rxjs';
import { MatSnackBar } from '@angular/material/snack-bar';
import { AuthStore } from '../../core/auth/auth-store';
import { CashboxExportApi } from '../../core/team/cashbox-export-api';
import { RecurringTransactionApi } from '../../core/team/recurring-transaction-api';
import { LogsApi } from '../../core/logs/logs-api';
import { Logs } from './logs';

describe('Logs', () => {
  const entries = [
    {
      id: 1,
      level: 'ERROR' as const,
      event: 'cashbox_export_subscription_run_fail',
      details: 'subscriptionId=1 teamId=5: smtp down',
      userId: -1,
      createdAt: '2026-08-04T04:00:00.000Z',
    },
  ];

  async function setup(isAdmin = true) {
    const loadLogs = vi.fn(() => of({ data: entries, page: 1, limit: 50, total: 1, hasNextPage: false }));
    const triggerCashboxRun = vi.fn(() => of(undefined));
    const triggerRecurringRun = vi.fn(() => of(undefined));
    const snackBarOpen = vi.fn();

    await TestBed.configureTestingModule({
      imports: [Logs],
      providers: [
        provideRouter([]),
        { provide: AuthStore, useValue: { isGlobalAdmin: signal(isAdmin) } },
        { provide: LogsApi, useValue: { loadLogs } },
        { provide: CashboxExportApi, useValue: { triggerRunNow: triggerCashboxRun } },
        { provide: RecurringTransactionApi, useValue: { triggerRunNow: triggerRecurringRun } },
        { provide: MatSnackBar, useValue: { open: snackBarOpen } },
      ],
    }).compileComponents();

    const fixture = TestBed.createComponent(Logs);
    fixture.detectChanges();
    return {
      fixture,
      component: fixture.componentInstance,
      loadLogs,
      triggerCashboxRun,
      triggerRecurringRun,
      snackBarOpen,
    };
  }

  // AG Grid's real component initialization (layout/ResizeObserver setup) can
  // run slower under the full suite's parallel load than in isolation, so this
  // gets a longer timeout rather than the vitest default 5s.
  it('shows the log grid and trigger buttons to a global admin', async () => {
    const { fixture } = await setup(true);

    expect(fixture.nativeElement.querySelector('ag-grid-angular')).not.toBeNull();
    expect(fixture.nativeElement.textContent).toContain('Cashbox-Export jetzt ausführen');
    expect(fixture.nativeElement.textContent).toContain('Wiederkehrende Buchungen jetzt prüfen');
  }, 15000);

  it('hides the grid and shows no access for a non-admin', async () => {
    const { fixture } = await setup(false);

    expect(fixture.nativeElement.querySelector('ag-grid-angular')).toBeNull();
    expect(fixture.nativeElement.textContent).toContain('Kein Zugriff');
  });

  it('builds a logs datasource sorted newest first with page/limit only when no filters are set', async () => {
    const { component, loadLogs } = await setup();
    const successCallback = vi.fn();

    const datasource = component['buildLogsDatasource']();
    datasource.getRows({
      startRow: 0,
      endRow: 50,
      sortModel: [],
      filterModel: {},
      successCallback,
      failCallback: vi.fn(),
    } as unknown as Parameters<typeof datasource.getRows>[0]);

    expect(loadLogs).toHaveBeenCalledWith({ page: 1, limit: 50 });
    expect(successCallback).toHaveBeenCalledWith(entries, 1);
  });

  it('applies level, event, date-range and search filters to the datasource query', async () => {
    const { component, loadLogs } = await setup();
    component['levelFilter'].set('ERROR');
    component['eventFilter'].set('cashbox_export_subscription_run_fail');
    component['fromFilter'].set('2026-01-01');
    component['toFilter'].set('2026-01-31');
    component['search'].set('teamId=5');

    const datasource = component['buildLogsDatasource']();
    datasource.getRows({
      startRow: 50,
      endRow: 100,
      sortModel: [],
      filterModel: {},
      successCallback: vi.fn(),
      failCallback: vi.fn(),
    } as unknown as Parameters<typeof datasource.getRows>[0]);

    expect(loadLogs).toHaveBeenCalledWith({
      page: 2,
      limit: 50,
      level: 'ERROR',
      event: 'cashbox_export_subscription_run_fail',
      from: '2026-01-01',
      to: '2026-01-31',
      search: 'teamId=5',
    });
  });

  it('triggers the cashbox export run and reloads the grid on success', async () => {
    const { component, triggerCashboxRun, snackBarOpen } = await setup();
    const reloadSpy = vi.spyOn(component as any, 'reloadLogs');

    component['triggerCashboxExportRun']();

    expect(triggerCashboxRun).toHaveBeenCalledTimes(1);
    expect(snackBarOpen).toHaveBeenCalled();
    expect(reloadSpy).toHaveBeenCalled();
  });

  it('shows an error message when the cashbox export trigger fails', async () => {
    const { component, snackBarOpen } = await setup();
    (component as any).cashboxExportApi.triggerRunNow = vi.fn(() =>
      throwError(() => new Error('boom')),
    );

    component['triggerCashboxExportRun']();

    expect(snackBarOpen).toHaveBeenCalled();
  });

  it('triggers the recurring-transactions run and reloads the grid on success', async () => {
    const { component, triggerRecurringRun, snackBarOpen } = await setup();
    const reloadSpy = vi.spyOn(component as any, 'reloadLogs');

    component['triggerRecurringTransactionsRun']();

    expect(triggerRecurringRun).toHaveBeenCalledTimes(1);
    expect(snackBarOpen).toHaveBeenCalled();
    expect(reloadSpy).toHaveBeenCalled();
  });
});
