import { describe, it, expect } from 'vitest';
import {
  transformAvailabilitiesToDictionary,
  transformAvailabilitiesToDictionaryWithFieldFilter,
  transformDownloadAvailabilitiesToDictionary,
  transformStockToAvailability,
} from './availability-transformers';
import { Availability, AvailabilityType } from '../models';
import { StockInfo } from '@isa/remission/data-access';
import { Supplier } from '@isa/checkout/data-access';

// ============================================================================
// Mock Data Helpers
// ============================================================================

function mockAvailability(
  itemId: number,
  overrides: Partial<Availability> = {},
): Availability {
  return {
    itemId,
    status: AvailabilityType.Available,
    preferred: 1,
    qty: 5,
    supplierId: 1,
    at: '2025-10-15',
    altAt: '2025-10-20',
    requestStatusCode: '0',
    price: {
      value: { value: 19.99, currency: 'EUR', currencySymbol: '€' },
      vat: { value: 3.8, inPercent: 19, label: '19%', vatType: 1 },
    },
    priceMaintained: true,
    ssc: '1024',
    sscText: 'Available',
    ...overrides,
  };
}

function mockStockInfo(
  itemId: number,
  overrides: Partial<StockInfo> = {},
): StockInfo {
  return {
    itemId,
    inStock: 10,
    retailPrice: 19.99,
    ...overrides,
  };
}

function mockSupplier(overrides: Partial<Supplier> = {}): Supplier {
  return {
    id: 'F',
    name: 'Filiale',
    ...overrides,
  };
}

// ============================================================================
// Tests: transformAvailabilitiesToDictionary
// ============================================================================

describe('transformAvailabilitiesToDictionary', () => {
  it('should return empty dictionary for empty availabilities array', () => {
    const result = transformAvailabilitiesToDictionary(
      [],
      [{ itemId: 123 }],
    );

    expect(result).toEqual({});
  });

  it('should return empty dictionary for empty requested items', () => {
    const availabilities = [mockAvailability(123)];
    const result = transformAvailabilitiesToDictionary(availabilities, []);

    expect(result).toEqual({});
  });

  it('should skip items without itemId', () => {
    const availabilities = [
      mockAvailability(123),
      mockAvailability(456),
    ];
    const requestedItems = [
      { itemId: 123 },
      {}, // Missing itemId
      { itemId: 456 },
    ];

    const result = transformAvailabilitiesToDictionary(
      availabilities,
      requestedItems,
    );

    expect(result).toHaveProperty('123');
    expect(result).toHaveProperty('456');
    expect(Object.keys(result)).toHaveLength(2);
  });

  it('should transform single item correctly', () => {
    const availability = mockAvailability(123, { preferred: 1 });
    const result = transformAvailabilitiesToDictionary(
      [availability],
      [{ itemId: 123 }],
    );

    expect(result).toEqual({ '123': availability });
  });

  it('should transform multiple items correctly', () => {
    const availability1 = mockAvailability(123, { preferred: 1 });
    const availability2 = mockAvailability(456, { preferred: 1 });

    const result = transformAvailabilitiesToDictionary(
      [availability1, availability2],
      [{ itemId: 123 }, { itemId: 456 }],
    );

    expect(result).toEqual({
      '123': availability1,
      '456': availability2,
    });
  });

  it('should exclude item when no preferred availability found', () => {
    const availability = mockAvailability(123, { preferred: 0 });
    const result = transformAvailabilitiesToDictionary(
      [availability],
      [{ itemId: 123 }],
    );

    expect(result).toEqual({});
  });

  it('should match itemIds correctly with type coercion', () => {
    const availability = mockAvailability(123, { preferred: 1 });
    const result = transformAvailabilitiesToDictionary(
      [availability],
      [{ itemId: 123 }],
    );

    // Key should be string, but matches number itemId
    expect(result).toHaveProperty('123');
    expect(result['123'].itemId).toBe(123);
  });
});

// ============================================================================
// Tests: transformAvailabilitiesToDictionaryWithFieldFilter
// ============================================================================

describe('transformAvailabilitiesToDictionaryWithFieldFilter', () => {
  it('should exclude supplierId field from result', () => {
    const availability = mockAvailability(123, {
      preferred: 1,
      supplierId: 5,
    });

    const result = transformAvailabilitiesToDictionaryWithFieldFilter(
      [availability],
      [{ itemId: 123 }],
    );

    expect(result['123']).toBeDefined();
    expect(result['123']).not.toHaveProperty('supplierId');
  });

  it('should exclude all supplier and logistician fields', () => {
    const availability = mockAvailability(123, {
      preferred: 1,
      supplierId: 5,
      supplier: { id: 5, name: 'Test Supplier' },
      logisticianId: 10,
      logistician: { id: 10, name: 'Test Logistician' },
    });

    const result = transformAvailabilitiesToDictionaryWithFieldFilter(
      [availability],
      [{ itemId: 123 }],
    );

    const transformed = result['123'];
    expect(transformed).toBeDefined();
    expect(transformed).not.toHaveProperty('supplierId');
    expect(transformed).not.toHaveProperty('supplier');
    expect(transformed).not.toHaveProperty('logisticianId');
    expect(transformed).not.toHaveProperty('logistician');
  });

  it('should preserve all other availability fields', () => {
    const availability = mockAvailability(123, {
      preferred: 1,
      supplierId: 5,
      qty: 10,
      status: AvailabilityType.Available,
      price: {
        value: { value: 29.99, currency: 'EUR', currencySymbol: '€' },
        vat: { value: 5.7, inPercent: 19, label: '19%', vatType: 1 },
      },
    });

    const result = transformAvailabilitiesToDictionaryWithFieldFilter(
      [availability],
      [{ itemId: 123 }],
    );

    const transformed = result['123'];
    expect(transformed.itemId).toBe(123);
    expect(transformed.qty).toBe(10);
    expect(transformed.status).toBe(AvailabilityType.Available);
    expect(transformed.price?.value?.value).toBe(29.99);
    expect(transformed.at).toBe('2025-10-15');
  });

  it('should return empty dictionary for empty availabilities', () => {
    const result = transformAvailabilitiesToDictionaryWithFieldFilter(
      [],
      [{ itemId: 123 }],
    );

    expect(result).toEqual({});
  });

  it('should skip items without itemId', () => {
    const availabilities = [mockAvailability(123)];
    const requestedItems = [{ itemId: 123 }, {}];

    const result = transformAvailabilitiesToDictionaryWithFieldFilter(
      availabilities,
      requestedItems,
    );

    expect(Object.keys(result)).toHaveLength(1);
    expect(result).toHaveProperty('123');
  });

  it('should exclude item when no preferred availability', () => {
    const availability = mockAvailability(123, { preferred: 0 });
    const result = transformAvailabilitiesToDictionaryWithFieldFilter(
      [availability],
      [{ itemId: 123 }],
    );

    expect(result).toEqual({});
  });

  it('should filter fields for multiple items', () => {
    const availability1 = mockAvailability(123, {
      preferred: 1,
      supplierId: 5,
    });
    const availability2 = mockAvailability(456, {
      preferred: 1,
      supplierId: 8,
    });

    const result = transformAvailabilitiesToDictionaryWithFieldFilter(
      [availability1, availability2],
      [{ itemId: 123 }, { itemId: 456 }],
    );

    expect(result['123']).not.toHaveProperty('supplierId');
    expect(result['456']).not.toHaveProperty('supplierId');
  });

  it('should handle availabilities without supplier fields gracefully', () => {
    const availability = mockAvailability(123, {
      preferred: 1,
      supplierId: undefined,
      supplier: undefined,
      logisticianId: undefined,
      logistician: undefined,
    });

    const result = transformAvailabilitiesToDictionaryWithFieldFilter(
      [availability],
      [{ itemId: 123 }],
    );

    expect(result['123']).toBeDefined();
    expect(result['123'].itemId).toBe(123);
  });
});

// ============================================================================
// Tests: transformDownloadAvailabilitiesToDictionary
// ============================================================================

describe('transformDownloadAvailabilitiesToDictionary', () => {
  it('should include item with valid download availability', () => {
    const availability = mockAvailability(123, {
      preferred: 1,
      supplierId: 1,
      qty: 5,
      status: AvailabilityType.Available, // 1024
    });

    const result = transformDownloadAvailabilitiesToDictionary(
      [availability],
      [{ itemId: 123 }],
    );

    expect(result).toEqual({ '123': availability });
  });

  it('should exclude supplier 16 with zero stock', () => {
    const availability = mockAvailability(123, {
      preferred: 1,
      supplierId: 16,
      qty: 0,
      status: AvailabilityType.Available,
    });

    const result = transformDownloadAvailabilitiesToDictionary(
      [availability],
      [{ itemId: 123 }],
    );

    expect(result).toEqual({});
  });

  it('should include supplier 16 with positive stock', () => {
    const availability = mockAvailability(123, {
      preferred: 1,
      supplierId: 16,
      qty: 5,
      status: AvailabilityType.Available,
    });

    const result = transformDownloadAvailabilitiesToDictionary(
      [availability],
      [{ itemId: 123 }],
    );

    expect(result).toEqual({ '123': availability });
  });

  it('should exclude item with invalid status code', () => {
    const availability = mockAvailability(123, {
      preferred: 1,
      status: AvailabilityType.NotAvailable, // 1 (invalid for downloads)
    });

    const result = transformDownloadAvailabilitiesToDictionary(
      [availability],
      [{ itemId: 123 }],
    );

    expect(result).toEqual({});
  });

  it('should accept all valid download status codes', () => {
    const validStatusCodes = [
      AvailabilityType.PrebookAtBuyer, // 2
      AvailabilityType.PrebookAtRetailer, // 32
      AvailabilityType.PrebookAtSupplier, // 256
      AvailabilityType.Available, // 1024
      AvailabilityType.OnDemand, // 2048
      AvailabilityType.AtProductionDate, // 4096
    ];

    validStatusCodes.forEach((status, index) => {
      const availability = mockAvailability(100 + index, {
        preferred: 1,
        status,
        supplierId: 1,
        qty: 5,
      });

      const result = transformDownloadAvailabilitiesToDictionary(
        [availability],
        [{ itemId: 100 + index }],
      );

      expect(result).toHaveProperty(String(100 + index));
    });
  });

  it('should return empty dictionary for empty availabilities', () => {
    const result = transformDownloadAvailabilitiesToDictionary(
      [],
      [{ itemId: 123 }],
    );

    expect(result).toEqual({});
  });

  it('should include only valid downloads in mixed scenario', () => {
    const validAvailability = mockAvailability(123, {
      preferred: 1,
      status: AvailabilityType.Available,
      supplierId: 1,
    });

    const invalidAvailability = mockAvailability(456, {
      preferred: 1,
      status: AvailabilityType.Available,
      supplierId: 16,
      qty: 0, // Invalid: supplier 16 with zero stock
    });

    const result = transformDownloadAvailabilitiesToDictionary(
      [validAvailability, invalidAvailability],
      [{ itemId: 123 }, { itemId: 456 }],
    );

    expect(result).toHaveProperty('123');
    expect(result).not.toHaveProperty('456');
  });
});

// ============================================================================
// Tests: transformStockToAvailability
// ============================================================================

describe('transformStockToAvailability', () => {
  it('should transform item with stock to available', () => {
    const stock = mockStockInfo(123, {
      inStock: 5,
      retailPrice: 19.99,
    });
    const supplier = mockSupplier({ id: 'F', name: 'Filiale' });

    const result = transformStockToAvailability([stock], [123], supplier);

    expect(result['123']).toEqual({
      status: AvailabilityType.Available,
      itemId: 123,
      qty: 5,
      ssc: '999',
      sscText: 'Filialentnahme',
      supplierId: 'F',
      price: 19.99,
    });
  });

  it('should transform item without stock to not available', () => {
    const stock = mockStockInfo(123, {
      inStock: 0,
      retailPrice: 19.99,
    });
    const supplier = mockSupplier();

    const result = transformStockToAvailability([stock], [123], supplier);

    expect(result['123']).toEqual({
      status: AvailabilityType.NotAvailable,
      itemId: 123,
      qty: 0,
      ssc: '',
      sscText: '',
      supplierId: 'F',
      price: 19.99,
    });
  });

  it('should treat undefined inStock as zero', () => {
    const stock = mockStockInfo(123, {
      inStock: undefined,
      retailPrice: 19.99,
    });
    const supplier = mockSupplier();

    const result = transformStockToAvailability([stock], [123], supplier);

    expect(result['123'].qty).toBe(0);
    expect(result['123'].status).toBe(AvailabilityType.NotAvailable);
  });

  it('should exclude item when stock info not found', () => {
    const stock = mockStockInfo(123, { inStock: 5 });
    const supplier = mockSupplier();

    const result = transformStockToAvailability([stock], [456], supplier);

    expect(result).toEqual({});
  });

  it('should transform multiple items correctly', () => {
    const stocks = [
      mockStockInfo(123, { inStock: 5, retailPrice: 19.99 }),
      mockStockInfo(456, { inStock: 0, retailPrice: 29.99 }),
    ];
    const supplier = mockSupplier();

    const result = transformStockToAvailability(
      stocks,
      [123, 456],
      supplier,
    );

    expect(result['123'].status).toBe(AvailabilityType.Available);
    expect(result['123'].qty).toBe(5);
    expect(result['456'].status).toBe(AvailabilityType.NotAvailable);
    expect(result['456'].qty).toBe(0);
  });

  it('should map supplier id correctly', () => {
    const stock = mockStockInfo(123, { inStock: 5 });
    const supplier = mockSupplier({ id: 'TEST_ID', name: 'Test' });

    const result = transformStockToAvailability([stock], [123], supplier);

    expect(result['123'].supplierId).toBe('TEST_ID');
  });
});
