import { ComponentFixture, TestBed } from '@angular/core/testing';
import { describe, it, expect, beforeEach, vi } from 'vitest';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { ReturnReceiptListItemComponent } from './return-receipt-list-item.component';
import {
  Return,
  ReceiptCompleteStatus,
  getReceiptNumberFromReturn,
  getReceiptItemQuantityFromReturn,
  getReceiptStatusFromReturn,
} from '@isa/remission/data-access';
import { MockComponent } from 'ng-mocks';
import { RemissionReturnReceiptActionsComponent } from '@isa/remission/shared/return-receipt-actions';

// Mock the helper functions
vi.mock('@isa/remission/data-access', async () => {
  const actual = await vi.importActual('@isa/remission/data-access');
  return {
    ...actual,
    getReceiptNumberFromReturn: vi.fn(),
    getReceiptItemQuantityFromReturn: vi.fn(),
    getReceiptStatusFromReturn: vi.fn(),
  };
});

describe('ReturnReceiptListItemComponent', () => {
  let component: ReturnReceiptListItemComponent;
  let fixture: ComponentFixture<ReturnReceiptListItemComponent>;

  const createMockReturn = (overrides: Partial<Return> = {}): Return =>
    ({
      id: 1,
      receipts: [
        { id: 101, items: [] },
        { id: 102, items: [] },
      ],
      ...overrides,
    }) as Return;

  beforeEach(async () => {
    // Reset all mocks before each test
    vi.clearAllMocks();

    await TestBed.configureTestingModule({
      imports: [
        HttpClientTestingModule,
        ReturnReceiptListItemComponent,
        MockComponent(RemissionReturnReceiptActionsComponent),
      ],
    }).compileComponents();

    fixture = TestBed.createComponent(ReturnReceiptListItemComponent);
    component = fixture.componentInstance;
  });

  describe('Component Setup', () => {
    it('should create', () => {
      const mockReturn = createMockReturn();
      fixture.componentRef.setInput('remissionReturn', mockReturn);

      expect(component).toBeTruthy();
    });

    it('should have remissionReturn as required input', () => {
      const mockReturn = createMockReturn();
      fixture.componentRef.setInput('remissionReturn', mockReturn);
      fixture.detectChanges();

      expect(component.remissionReturn()).toBeDefined();
      expect(component.remissionReturn()).toEqual(mockReturn);
    });

    it('should have correct host class applied', () => {
      const mockReturn = createMockReturn();
      fixture.componentRef.setInput('remissionReturn', mockReturn);
      fixture.detectChanges();

      const hostElement = fixture.debugElement.nativeElement;
      expect(
        hostElement.classList.contains('remi-return-receipt-list-item'),
      ).toBe(true);
    });
  });

  describe('Computed signals', () => {
    describe('receiptNumber', () => {
      it('should return receipt number from helper function', () => {
        const mockReturn = createMockReturn();
        const expectedReceiptNumber = '24-001';

        vi.mocked(getReceiptNumberFromReturn).mockReturnValue(
          expectedReceiptNumber,
        );

        fixture.componentRef.setInput('remissionReturn', mockReturn);
        fixture.detectChanges();

        expect(component.receiptNumber()).toBe(expectedReceiptNumber);
        expect(getReceiptNumberFromReturn).toHaveBeenCalledWith(mockReturn);
      });
    });

    describe('itemQuantity', () => {
      it('should return item quantity from helper function', () => {
        const mockReturn = createMockReturn();
        const expectedQuantity = 5;

        vi.mocked(getReceiptItemQuantityFromReturn).mockReturnValue(
          expectedQuantity,
        );

        fixture.componentRef.setInput('remissionReturn', mockReturn);
        fixture.detectChanges();

        expect(component.itemQuantity()).toBe(expectedQuantity);
        expect(getReceiptItemQuantityFromReturn).toHaveBeenCalledWith(
          mockReturn,
        );
      });
    });

    describe('status', () => {
      it('should return status from helper function using linkedSignal', () => {
        const mockReturn = createMockReturn();
        const expectedStatus = ReceiptCompleteStatus.Abgeschlossen;

        vi.mocked(getReceiptStatusFromReturn).mockReturnValue(expectedStatus);

        fixture.componentRef.setInput('remissionReturn', mockReturn);
        fixture.detectChanges();

        expect(component.status()).toBe(expectedStatus);
        expect(getReceiptStatusFromReturn).toHaveBeenCalledWith(mockReturn);
      });

      it('should update when input changes', () => {
        const mockReturn1 = createMockReturn({ id: 1 });
        const mockReturn2 = createMockReturn({ id: 2 });

        // Setup first status
        vi.mocked(getReceiptStatusFromReturn).mockReturnValue(
          ReceiptCompleteStatus.Offen,
        );
        fixture.componentRef.setInput('remissionReturn', mockReturn1);
        fixture.detectChanges();
        expect(component.status()).toBe(ReceiptCompleteStatus.Offen);

        // Change status for second return
        vi.mocked(getReceiptStatusFromReturn).mockReturnValue(
          ReceiptCompleteStatus.Abgeschlossen,
        );
        fixture.componentRef.setInput('remissionReturn', mockReturn2);
        fixture.detectChanges();
        expect(component.status()).toBe(ReceiptCompleteStatus.Abgeschlossen);
      });
    });
  });

  describe('Template rendering', () => {
    beforeEach(() => {
      // Setup default mock values for template rendering tests
      vi.mocked(getReceiptNumberFromReturn).mockReturnValue('24-001');
      vi.mocked(getReceiptItemQuantityFromReturn).mockReturnValue(3);
      vi.mocked(getReceiptStatusFromReturn).mockReturnValue(
        ReceiptCompleteStatus.Offen,
      );
    });

    it('should display receipt information correctly', () => {
      const mockReturn = createMockReturn();
      fixture.componentRef.setInput('remissionReturn', mockReturn);
      fixture.detectChanges();

      const compiled = fixture.nativeElement;

      // Check receipt number display
      expect(compiled.textContent).toContain('#24-001');

      // Check item quantity display
      expect(compiled.textContent).toContain('3');

      // Check status display
      expect(compiled.textContent).toContain('Offen');
    });

    it('should apply correct background class for "Offen" status', () => {
      const mockReturn = createMockReturn();
      vi.mocked(getReceiptStatusFromReturn).mockReturnValue(
        ReceiptCompleteStatus.Offen,
      );

      fixture.componentRef.setInput('remissionReturn', mockReturn);
      fixture.detectChanges();

      const containerDiv = fixture.nativeElement.querySelector(
        '.flex.flex-col.gap-6',
      );
      expect(containerDiv.classList.contains('bg-isa-white')).toBe(true);
      expect(containerDiv.classList.contains('bg-isa-neutral-400')).toBe(false);
    });

    it('should apply correct background class for "Abgeschlossen" status', () => {
      const mockReturn = createMockReturn();
      vi.mocked(getReceiptStatusFromReturn).mockReturnValue(
        ReceiptCompleteStatus.Abgeschlossen,
      );

      fixture.componentRef.setInput('remissionReturn', mockReturn);
      fixture.detectChanges();

      const containerDiv = fixture.nativeElement.querySelector(
        '.flex.flex-col.gap-6',
      );
      expect(containerDiv.classList.contains('bg-isa-neutral-400')).toBe(true);
      expect(containerDiv.classList.contains('bg-isa-white')).toBe(false);
    });

    it('should show action component for "Offen" status', () => {
      const mockReturn = createMockReturn();
      vi.mocked(getReceiptStatusFromReturn).mockReturnValue(
        ReceiptCompleteStatus.Offen,
      );

      fixture.componentRef.setInput('remissionReturn', mockReturn);
      fixture.detectChanges();

      const actionsComponent = fixture.nativeElement.querySelector(
        'lib-remission-return-receipt-actions',
      );
      expect(actionsComponent).toBeTruthy();
    });

    it('should hide action component for "Abgeschlossen" status', () => {
      const mockReturn = createMockReturn();
      vi.mocked(getReceiptStatusFromReturn).mockReturnValue(
        ReceiptCompleteStatus.Abgeschlossen,
      );

      fixture.componentRef.setInput('remissionReturn', mockReturn);
      fixture.detectChanges();

      const actionsComponent = fixture.nativeElement.querySelector(
        'lib-remission-return-receipt-actions',
      );
      expect(actionsComponent).toBeFalsy();
    });

    it('should pass correct inputs to action component', () => {
      const mockReturn = createMockReturn();
      vi.mocked(getReceiptStatusFromReturn).mockReturnValue(
        ReceiptCompleteStatus.Offen,
      );

      fixture.componentRef.setInput('remissionReturn', mockReturn);
      fixture.detectChanges();

      const actionsComponent = fixture.nativeElement.querySelector(
        'lib-remission-return-receipt-actions',
      );
      expect(actionsComponent).toBeTruthy();

      // Since we're using MockComponent, we can verify the component is present
      // and that it should receive the remissionReturn input based on the template
      // The actual input binding testing would require a more complex setup
      expect(actionsComponent.tagName.toLowerCase()).toBe(
        'lib-remission-return-receipt-actions',
      );
    });
  });

  describe('Output events', () => {
    it('should emit reloadList when action component emits reloadData', () => {
      const mockReturn = createMockReturn();
      vi.mocked(getReceiptStatusFromReturn).mockReturnValue(
        ReceiptCompleteStatus.Offen,
      );

      fixture.componentRef.setInput('remissionReturn', mockReturn);
      fixture.detectChanges();

      const reloadListSpy = vi.fn();
      component.reloadList.subscribe(reloadListSpy);

      // Simulate the action component emitting reloadData
      const actionsComponent = fixture.nativeElement.querySelector(
        'lib-remission-return-receipt-actions',
      );

      // Trigger the reloadData event from the actions component
      actionsComponent.dispatchEvent(new CustomEvent('reloadData'));
      fixture.detectChanges();

      expect(reloadListSpy).toHaveBeenCalled();
    });
  });

  describe('Component reactivity', () => {
    it('should update computed signals when input changes', () => {
      const initialReturn = createMockReturn({ id: 1 });
      const updatedReturn = createMockReturn({ id: 2 });

      // Mock return values for initial state
      vi.mocked(getReceiptNumberFromReturn).mockReturnValue('24-001');
      vi.mocked(getReceiptItemQuantityFromReturn).mockReturnValue(3);
      vi.mocked(getReceiptStatusFromReturn).mockReturnValue(
        ReceiptCompleteStatus.Offen,
      );

      fixture.componentRef.setInput('remissionReturn', initialReturn);
      fixture.detectChanges();

      expect(component.receiptNumber()).toBe('24-001');
      expect(component.itemQuantity()).toBe(3);
      expect(component.status()).toBe(ReceiptCompleteStatus.Offen);

      // Mock return values for updated state
      vi.mocked(getReceiptNumberFromReturn).mockReturnValue('24-002, 24-003');
      vi.mocked(getReceiptItemQuantityFromReturn).mockReturnValue(7);
      vi.mocked(getReceiptStatusFromReturn).mockReturnValue(
        ReceiptCompleteStatus.Abgeschlossen,
      );

      fixture.componentRef.setInput('remissionReturn', updatedReturn);
      fixture.detectChanges();

      expect(component.receiptNumber()).toBe('24-002, 24-003');
      expect(component.itemQuantity()).toBe(7);
      expect(component.status()).toBe(ReceiptCompleteStatus.Abgeschlossen);
    });

    it('should trigger helper functions with correct parameters when input changes', () => {
      const mockReturn1 = createMockReturn({ id: 1 });
      const mockReturn2 = createMockReturn({ id: 2 });

      // First input
      fixture.componentRef.setInput('remissionReturn', mockReturn1);
      fixture.detectChanges();

      expect(getReceiptNumberFromReturn).toHaveBeenCalledWith(mockReturn1);
      expect(getReceiptItemQuantityFromReturn).toHaveBeenCalledWith(
        mockReturn1,
      );
      expect(getReceiptStatusFromReturn).toHaveBeenCalledWith(mockReturn1);

      // Clear mock calls
      vi.clearAllMocks();

      // Second input
      fixture.componentRef.setInput('remissionReturn', mockReturn2);
      fixture.detectChanges();

      expect(getReceiptNumberFromReturn).toHaveBeenCalledWith(mockReturn2);
      expect(getReceiptItemQuantityFromReturn).toHaveBeenCalledWith(
        mockReturn2,
      );
      expect(getReceiptStatusFromReturn).toHaveBeenCalledWith(mockReturn2);
    });
  });

  describe('Constants', () => {
    it('should expose ReceiptCompleteStatus constant', () => {
      expect(component.ReceiptCompleteStatus).toBe(ReceiptCompleteStatus);
    });
  });
});
