import { TestBed } from '@angular/core/testing';
import { describe, it, expect, beforeEach, vi } from 'vitest';
import { runInInjectionContext, Injector } from '@angular/core';
import { MockProvider } from 'ng-mocks';
import { createSupplierResource } from './supplier.resource';
import { RemissionSupplierService, Supplier } from '@isa/remission/data-access';

describe('createSupplierResource', () => {
  let mockService: any;
  let mockSuppliers: Supplier[];

  beforeEach(() => {
    mockSuppliers = [
      {
        id: 123,
        name: 'Test Supplier GmbH',
        address: 'Test Street 1',
        contactPerson: 'John Doe',
        email: 'john@testsupplier.com',
        phone: '+49123456789',
        active: true,
      } as Supplier,
      {
        id: 456,
        name: 'Another Supplier Ltd',
        address: 'Another Street 2',
        contactPerson: 'Jane Smith',
        email: 'jane@anothersupplier.com',
        phone: '+49987654321',
        active: true,
      } as Supplier,
      {
        id: 789,
        name: 'Inactive Supplier Corp',
        address: 'Inactive Street 3',
        contactPerson: 'Bob Wilson',
        email: 'bob@inactivesupplier.com',
        phone: '+49555666777',
        active: false,
      } as Supplier,
    ];

    mockService = {
      fetchSuppliers: vi.fn().mockResolvedValue(mockSuppliers),
    };

    TestBed.configureTestingModule({
      providers: [
        MockProvider(RemissionSupplierService, mockService),
      ],
    });
  });

  describe('Resource Creation', () => {
    it('should create resource successfully', () => {
      const resource = runInInjectionContext(TestBed.inject(Injector), () =>
        createSupplierResource()
      );

      expect(resource).toBeDefined();
      expect(resource.value).toBeDefined();
      expect(resource.isLoading).toBeDefined();
      expect(resource.error).toBeDefined();
    });

    it('should inject RemissionSupplierService', () => {
      const resource = runInInjectionContext(TestBed.inject(Injector), () =>
        createSupplierResource()
      );

      expect(resource).toBeDefined();
      expect(mockService).toBeDefined();
    });
  });

  describe('Resource State Management', () => {
    it('should provide loading state', () => {
      const resource = runInInjectionContext(TestBed.inject(Injector), () =>
        createSupplierResource()
      );

      expect(resource.isLoading).toBeDefined();
      expect(typeof resource.isLoading).toBe('function');
    });

    it('should provide error state', () => {
      const resource = runInInjectionContext(TestBed.inject(Injector), () =>
        createSupplierResource()
      );

      expect(resource.error).toBeDefined();
      expect(typeof resource.error).toBe('function');
    });

    it('should provide value state', () => {
      const resource = runInInjectionContext(TestBed.inject(Injector), () =>
        createSupplierResource()
      );

      expect(resource.value).toBeDefined();
      expect(typeof resource.value).toBe('function');
    });
  });

  describe('Resource Loader', () => {
    it('should call service fetchSuppliers method', () => {
      const resource = runInInjectionContext(TestBed.inject(Injector), () =>
        createSupplierResource()
      );

      expect(resource).toBeDefined();
      expect(mockService.fetchSuppliers).toBeDefined();
    });

    it('should handle successful service response', () => {
      mockService.fetchSuppliers.mockResolvedValue(mockSuppliers);

      const resource = runInInjectionContext(TestBed.inject(Injector), () =>
        createSupplierResource()
      );

      expect(resource).toBeDefined();
      expect(resource.value).toBeDefined();
    });

    it('should handle service returning empty array', () => {
      mockService.fetchSuppliers.mockResolvedValue([]);

      const resource = runInInjectionContext(TestBed.inject(Injector), () =>
        createSupplierResource()
      );

      expect(resource).toBeDefined();
      expect(resource.value).toBeDefined();
    });

    it('should handle service errors', () => {
      const error = new Error('Service error');
      mockService.fetchSuppliers.mockRejectedValue(error);

      const resource = runInInjectionContext(TestBed.inject(Injector), () =>
        createSupplierResource()
      );

      expect(resource).toBeDefined();
      expect(resource.error).toBeDefined();
    });
  });

  describe('AbortSignal handling', () => {
    it('should pass abortSignal to service', () => {
      const resource = runInInjectionContext(TestBed.inject(Injector), () =>
        createSupplierResource()
      );

      expect(resource).toBeDefined();
      expect(mockService.fetchSuppliers).toBeDefined();
    });

    it('should handle aborted requests', () => {
      const abortError = new Error('Request aborted');
      mockService.fetchSuppliers.mockRejectedValue(abortError);

      const resource = runInInjectionContext(TestBed.inject(Injector), () =>
        createSupplierResource()
      );

      expect(resource).toBeDefined();
      expect(resource.error).toBeDefined();
    });
  });

  describe('Resource without parameters', () => {
    it('should create resource without requiring parameters', () => {
      const resource = runInInjectionContext(TestBed.inject(Injector), () =>
        createSupplierResource()
      );

      expect(resource).toBeDefined();
      expect(resource.value).toBeDefined();
      expect(resource.isLoading).toBeDefined();
      expect(resource.error).toBeDefined();
    });

    it('should automatically load suppliers on creation', () => {
      const resource = runInInjectionContext(TestBed.inject(Injector), () =>
        createSupplierResource()
      );

      expect(resource).toBeDefined();
      expect(mockService.fetchSuppliers).toBeDefined();
    });
  });

  describe('Resource Function', () => {
    it('should create resource function correctly', () => {
      const createResourceFn = () => createSupplierResource();

      const resource = runInInjectionContext(TestBed.inject(Injector), createResourceFn);

      expect(resource).toBeDefined();
      expect(typeof resource.value).toBe('function');
      expect(typeof resource.isLoading).toBe('function');
      expect(typeof resource.error).toBe('function');
    });

    it('should handle multiple resource instances', () => {
      const resource1 = runInInjectionContext(TestBed.inject(Injector), () =>
        createSupplierResource()
      );

      const resource2 = runInInjectionContext(TestBed.inject(Injector), () =>
        createSupplierResource()
      );

      expect(resource1).toBeDefined();
      expect(resource2).toBeDefined();
      expect(resource1).not.toBe(resource2);
    });
  });

  describe('Service Integration', () => {
    it('should integrate correctly with RemissionSupplierService', () => {
      const resource = runInInjectionContext(TestBed.inject(Injector), () =>
        createSupplierResource()
      );

      expect(resource).toBeDefined();
      expect(mockService.fetchSuppliers).toBeDefined();
    });

    it('should handle different supplier data types', () => {
      const differentSuppliers = [
        { id: 1, name: 'Supplier One', active: true },
        { id: 2, name: 'Supplier Two', active: false },
      ] as Supplier[];

      mockService.fetchSuppliers.mockResolvedValue(differentSuppliers);

      const resource = runInInjectionContext(TestBed.inject(Injector), () =>
        createSupplierResource()
      );

      expect(resource).toBeDefined();
      expect(resource.value).toBeDefined();
    });
  });
});