import { PriceDTO, Price } from '@generated/swagger/checkout-api';
import { Availability as AvaAvailability } from '@isa/availability/data-access';
import { ensureCurrencyDefaults } from '@isa/common/data-access';
import { Availability, AvailabilityType } from '../schemas';

/**
 * Availability data from catalogue-api (raw response)
 */
export interface CatalogueAvailabilityResponse {
  availabilityType: AvailabilityType;
  ssc: number;
  sscText: string;
  supplier: { id: number };
  isPrebooked: boolean;
  estimatedShippingDate: string;
  price: number;
  lastRequest: string;
  // Optional fields for shipping
  estimatedDelivery?: { start?: string; stop?: string };
  logistician?: { id: number };
  supplierProductNumber?: string;
  supplierInfo?: string;
  priceMaintained?: boolean;
  inStock?: number;
  orderDeadline?: string;
}

/**
 * Adapter for converting catalogue-api availability responses to checkout-api format.
 *
 * Handles:
 * - Structure mapping between different API representations
 * - Price object creation with VAT
 * - Supplier and logistician reference wrapping
 */
export class AvailabilityAdapter {
  private static readonly ADAPTER_NAME = 'AvailabilityAdapter';

  /**
   * Converts catalogue-api availability to checkout-api AvailabilityDTO.
   *
   * @param catalogueAvailability - Raw availability from catalogue service
   * @param originalPrice - Original price to preserve (if different from new price)
   * @returns AvailabilityDTO compatible with checkout-api
   */
  static toCheckoutFormat(
    catalogueAvailability: CatalogueAvailabilityResponse,
    originalPrice?: number,
  ): Availability {
    const availability: Availability = {
      availabilityType: catalogueAvailability.availabilityType,
      ssc: catalogueAvailability.ssc?.toString(),
      sscText: catalogueAvailability.sscText,
      supplier: {
        id: catalogueAvailability.supplier.id,
        data: {
          id: catalogueAvailability.supplier.id,
        },
        // Explicitly omit externalReference to avoid TypeScript errors
        // (generated DTOs require externalStatus when externalReference is present)
      },
      isPrebooked: catalogueAvailability.isPrebooked,
      estimatedShippingDate: catalogueAvailability.estimatedShippingDate,
      lastRequest: catalogueAvailability.lastRequest,
      // Use original price if provided, otherwise use new price
      price: {
        value: {
          value: originalPrice ?? catalogueAvailability.price,
          currency: 'EUR',
          currencySymbol: '€',
        },
      },
    };

    // Add optional fields if present
    if (catalogueAvailability.estimatedDelivery) {
      availability.estimatedDelivery = catalogueAvailability.estimatedDelivery;
    }

    if (catalogueAvailability.logistician) {
      availability.logistician = {
        id: catalogueAvailability.logistician.id,
        data: {
          id: catalogueAvailability.logistician.id,
        },
        // Explicitly omit externalReference to avoid TypeScript errors
        // (generated DTOs require externalStatus when externalReference is present)
      };
    }

    if (catalogueAvailability.supplierProductNumber) {
      availability.supplierProductNumber =
        catalogueAvailability.supplierProductNumber;
    }

    if (catalogueAvailability.supplierInfo) {
      availability.supplierInfo = catalogueAvailability.supplierInfo;
    }

    if (catalogueAvailability.inStock !== undefined) {
      availability.inStock = catalogueAvailability.inStock;
    }

    return availability;
  }

  /**
   * Converts availability-api Availability to checkout-api AvailabilityDTO.
   *
   * Handles mapping between different API representations:
   * - status → availabilityType
   * - qty → inStock (preserves quantity information)
   * - Simple IDs → Entity containers for logistician/supplier
   * - Preserves common fields (price, ssc, dates, etc.)
   *
   * @param availability - Availability from availability-api service
   * @returns AvailabilityDTO compatible with checkout-api
   */
  static fromAvailabilityApi(availability: AvaAvailability): Availability {
    const checkoutAvailability: Availability = {
      availabilityType: availability.status,
      ssc: availability.ssc,
      sscText: availability.sscText,
      isPrebooked: availability.isPrebooked,
      price: ensureCurrencyDefaults(availability.price),
      estimatedShippingDate: availability.at,
      lastRequest: availability.requested,
    };

    // Map qty to inStock (preserve quantity information)
    if (availability.qty !== undefined) {
      checkoutAvailability.inStock = availability.qty;
    }

    // Convert logistician ID to entity container
    if (availability.logisticianId) {
      checkoutAvailability.logistician = {
        id: availability.logisticianId,
        data: {
          id: availability.logisticianId,
        },
        // Explicitly omit externalReference to avoid TypeScript errors
        // (generated DTOs require externalStatus when externalReference is present)
      };
    }

    // Convert supplier ID to entity container
    if (availability.supplierId) {
      checkoutAvailability.supplier = {
        id: availability.supplierId,
        data: {
          id: availability.supplierId,
        },
        // Explicitly omit externalReference to avoid TypeScript errors
        // (generated DTOs require externalStatus when externalReference is present)
      };
    }

    // Map supplier string to supplierInfo (alternative to supplierId)
    if (availability.supplier) {
      checkoutAvailability.supplierInfo = availability.supplier;
    }

    // Optional fields
    if (availability.estimatedDelivery) {
      checkoutAvailability.estimatedDelivery = availability.estimatedDelivery;
    }

    if (availability.supplierProductNumber) {
      checkoutAvailability.supplierProductNumber =
        availability.supplierProductNumber;
    }

    if (availability.requestReference) {
      checkoutAvailability.requestReference = availability.requestReference;
    }

    return checkoutAvailability;
  }

  /**
   * Converts PriceDTO to Price format for shopping cart operations.
   *
   * PriceDTO format (nested):
   * - value: { value?: number, currency?: string, ... }
   * - vat: { value?: number, inPercent?: number, vatType?: VATType, ... }
   *
   * Price format (flat):
   * - value: number (required)
   * - vatInPercent?: number
   * - vatType: VATType (required)
   * - vatValue?: number
   * - currency?: string
   *
   * @param priceDTO - PriceDTO from shopping cart item
   * @returns Price in flat format, or undefined if input is invalid
   */
  static convertPriceDTOToPrice(priceDTO?: PriceDTO): Price | undefined {
    if (!priceDTO) {
      return undefined;
    }

    const value = priceDTO.value?.value;
    const vatType = priceDTO.vat?.vatType;

    // Both value and vatType are required for Price
    if (value === undefined || value === null || !vatType) {
      return undefined;
    }

    return {
      value,
      vatType,
      vatInPercent: priceDTO.vat?.inPercent,
      vatValue: priceDTO.vat?.value,
      currency: priceDTO.value?.currency,
      currencySymbol: priceDTO.value?.currencySymbol,
    };
  }

  /**
   * Type guard for catalogue availability response
   */
  static isValidCatalogueResponse(
    value: unknown,
  ): value is CatalogueAvailabilityResponse {
    if (typeof value !== 'object' || value === null) return false;

    return (
      'availabilityType' in value &&
      typeof value.availabilityType === 'number' &&
      'ssc' in value &&
      typeof value.ssc === 'number' &&
      'sscText' in value &&
      typeof value.sscText === 'string' &&
      'supplier' in value &&
      typeof value.supplier === 'object' &&
      value.supplier !== null &&
      'id' in value.supplier &&
      typeof value.supplier.id === 'number'
    );
  }
}
