import ShippingFactory from '#services/shipping/shipping_factory'
import { ShippingCarrier, CommissionType, IntlCouriers } from '../../types/enum.js'
import AramexService from '#services/shipping/aramex_service'
import logger from '@adonisjs/core/services/logger'
import { RateResult } from '#services/shipping/shipping_interface'
import { intlCourierRateCalculatorHelper } from './IntlCourierRateCalculatorHelper.js'
/**
 * Interface for international shipping rate calculation parameters
 */
export interface InternationalRateParams {
  originCity: string
  originCountry: string
  originPostalCode?: string
  originLat?: string
  originLng?: string
  destinationCity: string
  destinationCountry: string
  destinationPostalCode?: string
  destinationLat?: string
  destinationLng?: string
  weight: number
  numberOfPieces: number
  productGroup?: string
  productType?: string
  length?: number
  width?: number
  height?: number
}

/**
 * Calculate shipping charge by carrier
 * @param carrier - Shipping carrier name (Aramex, DHL, FedEx, SMSA)
 * @param params - International rate calculation parameters
 * @returns RateResult with base shipping charge from provider
 */
export async function calculateShippingChargeByCarrier(
  carrier: string,
  params: InternationalRateParams
): Promise<RateResult | RateResult[]> {
  try {
    const fetchPostalCode = async (lat: string, lng: string) => {
      try {
        const response = await fetch(
          `https://nominatim.openstreetmap.org/reverse?format=json&lat=${lat}&lon=${lng}`,
          {
            headers: {
              'User-Agent': 'Delybell/1.0',
            },
          }
        )
        if (response.ok) {
          const data: any = await response.json()
          return data.address?.postcode
        }
      } catch (e) {
        logger.error(e, 'Failed to fetch postal code from Nominatim')
        return {
          message: 'Failed to fetch postal code from Nominatim',
        }
      }
      return null
    }
    if (!params.originPostalCode && params.originLat && params.originLng) {
      const postalCode = await fetchPostalCode(params.originLat, params.originLng)
      if (postalCode) params.originPostalCode = postalCode
    }

    if (!params.destinationPostalCode && params.destinationLat && params.destinationLng) {
      const postalCode = await fetchPostalCode(params.destinationLat, params.destinationLng)
      if (postalCode) {
        params.destinationPostalCode = postalCode
        logger.info({ postalCode }, 'Resolved destination postal code from lat/lng')
      }
    }

    const shippingProvider = ShippingFactory.getProvider(carrier)

    if (carrier === ShippingCarrier.Aramex) {
      const aramexService = shippingProvider as AramexService
      return await aramexService.calculateInternationalRate({
        originCity: params.originCity,
        originCountry: params.originCountry,
        originPostalCode: params.originPostalCode,
        destinationCity: params.destinationCity,
        destinationCountry: params.destinationCountry,
        destinationPostalCode: params.destinationPostalCode,
        weight: params.weight,
        numberOfPieces: params.numberOfPieces,
        productGroup: params.productGroup,
        productType: params.productType,
        width: undefined,
        length: undefined,
      })
    }
    {
      if ('calculateInternationalRate' in shippingProvider) {
        const providerWithIntlRate = shippingProvider as any
        return await providerWithIntlRate.calculateInternationalRate({
          originCity: params?.originCity,
          originCountry: params?.originCountry,
          originPostalCode: params?.originPostalCode,
          destinationCity: params?.destinationCity,
          destinationCountry: params?.destinationCountry,
          destinationPostalCode: params?.destinationPostalCode,
          numberOfPieces: params?.numberOfPieces,
          productGroup: params?.productGroup,
          productType: params?.productType,
          weight: params?.weight,
          length: params?.length,
          width: params?.width,
          height: params?.height,
        })
      }
    }
    throw new Error(`International rate calculation not yet implemented for carrier: ${carrier}`)
  } catch (error) {
    logger.error(error, `Failed to calculate shipping charge for carrier: ${carrier}`)
    throw error
  }
}

/**
 * Apply Delybell markup to base shipping charge
 * @param baseAmount - Base shipping charge from provider
 * @param commissionValue - Commission value to apply (percentage or flat amount)
 * @param commissionType - Type of commission (PERCENTAGE or FLAT_AMOUNT)
 * @returns Final amount with markup applied
 */
export function applyDelybellMarkup(
  baseAmount: number,
  commissionValue: number = 0,
  commissionType: CommissionType = CommissionType.FLAT_AMOUNT
): number {
  console.log({ baseAmount, commissionValue, commissionType })
  if (baseAmount < 0) {
    throw new Error('Base amount cannot be negative')
  }

  if (commissionValue < 0) {
    throw new Error('Commission value cannot be negative')
  }

  let finalAmount: number
  if (commissionType === CommissionType.PERCENTAGE) {
    // Formula: finalAmount = baseAmount * (1 + commissionValue / 100)
    finalAmount = baseAmount * (1 + commissionValue / 100)
  } else {
    // Formula: finalAmount = baseAmount + commissionValue
    finalAmount = Number(baseAmount || 0) + Number(commissionValue || 0)
  }

  // Round to 2 decimal places
  return Math.round(finalAmount * 100) / 100
}

/**
 * Calculate shipping charge with Delybell markup
 * @param carrier - Shipping carrier name
 * @param params - International rate calculation parameters
 * @param createdForId - Customer ID to fetch commission details from
 * @returns RateResult with markup applied
 */
// export async function calculateShippingChargeWithMarkup(
//   carrier: string,
//   params: InternationalRateParams,
//   createdForId?: number
// ): Promise<RateResult | RateResult[]> {
//   const baseRate = await calculateShippingChargeByCarrier(carrier, params)

//   let commissionValue = 0
//   let commissionType = CommissionType.FLAT_AMOUNT

//   if (createdForId) {
//     const customer = await Customer.find(createdForId)
//     if (customer) {
//       commissionValue = customer.commission_value ?? 0
//       commissionType = customer.commission_type ?? CommissionType.FLAT_AMOUNT
//     }
//   }

//   if (Array.isArray(baseRate)) {
//     return baseRate.map((rate) => ({
//       ...rate,
//       amount: applyDelybellMarkup(rate.amount, commissionValue, commissionType),
//     }))
//   }

//   const finalAmount = applyDelybellMarkup(baseRate.amount, commissionValue, commissionType)
//   return {
//     ...baseRate,
//     amount: finalAmount,
//   }
// }

export async function calculateShippingChargeWithMarkup(
  carrier: string,
  params: InternationalRateParams,
  createdForId?: number
): Promise<RateResult | RateResult[]> {
  const baseRate = await calculateShippingChargeByCarrier(carrier, params)

  // Determine courier enum
  const courier =
    carrier === ShippingCarrier.Aramex
      ? IntlCouriers.Aramex
      : carrier === ShippingCarrier.DHL
        ? IntlCouriers.DHL
        : carrier === ShippingCarrier.FedEx
          ? IntlCouriers.FedEx
          : carrier === ShippingCarrier.SMSA
            ? IntlCouriers.SMSA
            : null

  if (!courier) throw new Error(`Unsupported carrier: ${carrier}`)

  if (Array.isArray(baseRate)) {
    return await Promise.all(
      baseRate.map(async (rate) => {
        const { courierRate, margin, finalRate } = await intlCourierRateCalculatorHelper({
          rate: rate.amount as number,
          courier: courier as IntlCouriers,
          customer_id: createdForId as number,
          weight: params?.weight || 0,
        })
        return {
          ...rate,
          amount: finalRate,
          courierRate,
          margin,
        }
      })
    )
  } else {
    const { courierRate, margin, finalRate } = await intlCourierRateCalculatorHelper({
      rate: baseRate.amount,
      courier: courier as IntlCouriers,
      customer_id: createdForId || 0,
      weight: params?.weight || 0,
    })
    return {
      ...baseRate,
      amount: finalRate,
      courierRate,
      margin,
    }
  }
}
