import {
  ShippingProviderContract,
  ShipmentResult,
  TrackingResult,
  RateResult,
} from './shipping_interface.js'
import Order from '#models/order'
import shippingConfig from '#config/shipping'
import logger from '@adonisjs/core/services/logger'
import { parseFedExRates, resolveBahrainPostalCode, splitStreetLines } from '../../Helper/Helper.js'
import InternationalOrder from '#models/international_order'
import { DateTime } from 'luxon'

export default class FedExService implements ShippingProviderContract {
  private config: any = shippingConfig.fedex
  private token: string | null = null
  private tokenExpiry: number = 0

  private async getAuthToken() {
    if (this.token && Date.now() < this.tokenExpiry) {
      return this.token
    }

    const params = new URLSearchParams()
    params.append('grant_type', 'client_credentials')
    params.append('client_id', this.config.apiKey)
    params.append('client_secret', this.config.secretKey)

    const response = await fetch(`${this.config.baseUrl}/oauth/token`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
      },
      body: params,
    })

    if (!response.ok) {
      const errorBody = await response.text()
      logger.error({ errorBody }, 'FedEx Auth Failed')
      throw new Error(`FedEx Auth Error: ${response.statusText}`)
    }

    const data: any = await response.json()
    this.token = data.access_token
    this.tokenExpiry = Date.now() + (data.expires_in - 60) * 1000 // Buffer 60s
    return this.token
  }

  async createShipment(
    order: Order,
    internationalOrder?: InternationalOrder
  ): Promise<ShipmentResult> {
    try {
      const token = await this.getAuthToken()

      if (!internationalOrder) {
        throw new Error('International Order details are required for FedEx shipment')
      }

      const payload = this.prepareShipmentPayload(order, internationalOrder)

      const response = await fetch(`${this.config.baseUrl}/ship/v1/shipments`, {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${token}`,
          'Content-Type': 'application/json',
        },
        body: JSON.stringify(payload),
      })
      if (!response.ok) {
        const errorBody = await response.text()
        logger.error({ errorBody }, 'FedEx Create Shipment Failed')
        throw new Error(`FedEx API Error: ${response.statusText} - ${errorBody}`)
      }

      const data: any = await response.json()
      const transactionShipment = data.output?.transactionShipments?.[0]
      const trackingNumber = transactionShipment?.masterTrackingNumber
      const labelUrl = transactionShipment?.pieceResponses?.[0]?.packageDocuments?.[0]?.url // Or encodedLabel

      // Check for invoice in shipmentDocuments
      let invoiceUrl = transactionShipment?.shipmentDocuments?.find(
        (doc: any) =>
          doc.docType === 'COMMERCIAL_INVOICE' || doc.contentType === 'COMMERCIAL_INVOICE'
      )?.url

      // If not found, check packageDocuments of the first package
      if (!invoiceUrl) {
        const packageDocs = transactionShipment?.pieceResponses?.[0]?.packageDocuments || []
        const invoiceDoc = packageDocs.find(
          (doc: any) =>
            doc.docType === 'COMMERCIAL_INVOICE' || doc.contentType === 'COMMERCIAL_INVOICE'
        )
        invoiceUrl = invoiceDoc?.url
      }

      return {
        trackingNumber: trackingNumber,
        labelUrl: labelUrl, // Or labelData if base64
        invoiceUrl: invoiceUrl,
        provider: 'FedEx',
        apiResponse: data,
      }
    } catch (error) {
      logger.error(error, 'FedEx Create Shipment Error')
      throw error
    }
  }

  async trackShipment(trackingNumber: string): Promise<TrackingResult> {
    try {
      const token = await this.getAuthToken()
      const payload = {
        trackingInfo: [
          {
            trackingNumberInfo: {
              trackingNumber: trackingNumber,
            },
          },
        ],
        includeDetailedScans: true,
      }

      const response = await fetch(`${this.config.baseUrl}/track/v1/trackingdocuments`, {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${token}`,
          'Content-Type': 'application/json',
        },
        body: JSON.stringify(payload),
      })

      if (!response.ok) {
        throw new Error(`FedEx Tracking Failed: ${response.statusText}`)
      }

      const data: any = await response.json()
      const trackResult = data.output?.completeTrackResults?.[0]?.trackResults?.[0]
      const latestStatus = trackResult?.latestStatusDetail?.statusByLocale
      const scans = trackResult?.scanEvents || []

      return {
        trackingNumber: trackingNumber,
        status: latestStatus || 'UNKNOWN',
        history: scans.map((event: any) => ({
          status: event.eventDescription,
          location: `${event.scanLocation?.city}, ${event.scanLocation?.countryCode}`,
          timestamp: event.date,
          description: event.eventDescription,
        })),
      }
    } catch (error) {
      logger.error(error, 'FedEx Track Shipment Error')
      throw error
    }
  }

  async cancelShipment(_trackingNumber: string): Promise<boolean> {
    // Implement if needed
    return false
  }

  async generateLabel(_trackingNumber: string): Promise<string> {
    // Usually part of create shipment response.
    return ''
  }

  async calculateRate(order: Order): Promise<RateResult> {
    try {
      const token = await this.getAuthToken()
      const payload = this.prepareRatePayload(order)

      const response = await fetch(`${this.config.baseUrl}/rate/v1/rates/quotes`, {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${token}`,
          'Content-Type': 'application/json',
        },
        body: JSON.stringify(payload),
      })

      if (!response.ok) {
        throw new Error(`FedEx Rate Failed: ${response.statusText}`)
      }

      const data: any = await response.json()
      const rateReply = data.output?.rateReplyDetails?.[0]

      if (!rateReply) {
        throw new Error('No rates found')
      }

      // Use ratedShipmentDetails to get total net charge
      const ratedShipment = rateReply.ratedShipmentDetails?.[0]
      const totalNetCharge = ratedShipment?.totalNetCharge

      return {
        amount: totalNetCharge?.amount,
        currency: totalNetCharge?.currency,
        provider: 'FedEx',
        serviceName: rateReply.serviceType,
        estimatedDeliveryDate: rateReply.commit?.dateDetail?.dayFormat,
      }
    } catch (error) {
      logger.error(error, 'FedEx Calculate Rate Error')
      throw error
    }
  }

  private prepareRatePayload(order: Order) {
    return {
      accountNumber: {
        value: this.config.accountNumber,
      },
      requestedShipment: {
        shipper: {
          address: {
            postalCode: '12345',
            countryCode: 'BH',
          },
        },
        recipient: {
          address: {
            postalCode: '54321',
            countryCode: 'SA',
          },
        },
        pickupType: 'USE_SCHEDULED_PICKUP',
        rateRequestType: ['LIST', 'ACCOUNT'],
        requestedPackageLineItems: [
          {
            weight: {
              units: 'KG',
              value: order.total_weight,
            },
          },
        ],
      },
    }
  }

  async calculateInternationalRate(details: {
    originCity: string
    originCountry: string
    originPostalCode?: string
    destinationCity: string
    destinationCountry: string
    destinationPostalCode?: string
    numberOfPieces: number
    productGroup?: string
    productType?: string
    weight: number
    length: number
    width: number
    height: number
  }): Promise<RateResult> {
    try {
      const token = await this.getAuthToken()
      const originCityDhl = 'AL HIDD'
      const originPostalCode = resolveBahrainPostalCode(originCityDhl)
      const payload = {
        accountNumber: {
          value: this.config.accountNumber,
        },
        requestedShipment: {
          shipper: {
            address: {
              city: originCityDhl ?? details.originCity,
              countryCode: details.originCountry,
              postalCode: originPostalCode ?? (details.originPostalCode || ''),
            },
          },
          recipient: {
            address: {
              city: details.destinationCity,
              countryCode: details.destinationCountry,
              postalCode: details.destinationPostalCode || '695001',
            },
          },
          pickupType: 'DROPOFF_AT_FEDEX_LOCATION',
          rateRequestType: ['LIST', 'ACCOUNT'],
          packagingType: 'YOUR_PACKAGING',
          totalPackageCount: details?.numberOfPieces ?? 1,
          requestedPackageLineItems: [
            {
              weight: { units: 'KG', value: details?.weight ?? 0 },
              dimensions: {
                length: details?.length ?? 0,
                width: details?.width ?? 0,
                height: details?.height ?? 0,
                units: 'CM',
              },
            },
          ],
        },
      }
      const response = await fetch(`${this.config.baseUrl}/rate/v1/rates/quotes`, {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${token}`,
          'Content-Type': 'application/json',
        },
        body: JSON.stringify(payload),
      })

      if (!response.ok) {
        const errorData: any = await response.json()
        const message =
          errorData.errors?.[0]?.message ||
          errorData.output?.errors?.[0]?.message ||
          `FedEx Rate Error: ${response.statusText}`
        logger.error({ errorData }, 'FedEx Calculate International Rate Failed')
        throw new Error(message)
      }

      const data: any = await response.json()
      const result = await parseFedExRates(data)
      return result
    } catch (error) {
      logger.error(error, 'FedEx Calculate International Rate Error')
      throw error
    }
  }

  private prepareShipmentPayload(order: Order, internationalOrder: InternationalOrder) {
    const commodities = [
      {
        description: internationalOrder.description || 'Merchandise',
        countryOfManufacture: internationalOrder.shipper_country_details?.short_code || 'BH',
        quantity: 1,
        quantityUnits: 'PCS',
        unitPrice: {
          amount: internationalOrder.customs_value || 1,
          currency: internationalOrder.currency || 'BHD',
        },
        customsValue: {
          amount: internationalOrder.customs_value || 1,
          currency: internationalOrder.currency || 'BHD',
        },
        weight: {
          units: 'KG',
          value: order.total_weight,
        },
        harmonizedCode: '610910',
      },
    ]

    return {
      labelResponseOptions: 'URL_ONLY',
      accountNumber: {
        value: this.config.accountNumber,
      },
      requestedShipment: {
        shipDatestamp: DateTime.now().toFormat('yyyy-MM-dd'),
        pickupType: 'DROPOFF_AT_FEDEX_LOCATION',
        serviceType: 'FEDEX_INTERNATIONAL_PRIORITY',
        packagingType: 'FEDEX_BOX',

        preferredCurrency: internationalOrder.currency || 'BHD',

        totalDeclaredValue: {
          amount: internationalOrder.customs_value || 1,
          currency: internationalOrder.currency || 'BHD',
        },

        shipper: {
          contact: {
            personName: internationalOrder.shipper_name,
            phoneNumber: internationalOrder.shipper_phone ?? '+97333333333',
            companyName: internationalOrder.shipper_company_name || internationalOrder.shipper_name,
          },
          address: {
            streetLines: splitStreetLines(internationalOrder.shipper_address || 'Address'),
            city: internationalOrder.shipper_city || 'Manama',
            postalCode: internationalOrder.shipper_postal_code || '00000',
            countryCode: internationalOrder.shipper_country_details?.short_code || 'BH',
          },
        },

        recipients: [
          {
            contact: {
              personName: internationalOrder.recipient_name,
              phoneNumber:
                internationalOrder.recipient_phone ||
                internationalOrder.recipient_mobile ||
                '+97333333333',
            },
            address: {
              streetLines: splitStreetLines(internationalOrder.recipient_address || 'Address'),
              city: internationalOrder.recipient_city || 'City',
              postalCode: internationalOrder.recipient_postal_code || '00000',
              countryCode: internationalOrder.recipient_country_details?.short_code || 'BH',
            },
          },
        ],

        shippingChargesPayment: {
          paymentType: 'SENDER',
          payor: {
            responsibleParty: {
              accountNumber: {
                value: this.config.accountNumber,
              },
            },
          },
        },

        customsClearanceDetail: {
          dutiesPayment: {
            paymentType: 'SENDER',
          },
          commodities: commodities,
        },

        requestedPackageLineItems:
          order.order_package_list?.length > 0
            ? order.order_package_list.map((pkg) => ({
                weight: {
                  units: 'KG',
                  value: pkg.weight,
                },
              }))
            : [
                {
                  weight: {
                    units: 'KG',
                    value: order.total_weight,
                  },
                },
              ],

        labelSpecification: {
          labelFormatType: 'COMMON2D',
          imageType: 'PDF',
          labelStockType: 'PAPER_4X6',
        },
        shippingDocumentSpecification: {
          shippingDocumentTypes: ['COMMERCIAL_INVOICE'],
          commercialInvoiceDetail: {
            documentFormat: {
              docType: 'PDF',
              stockType: 'PAPER_LETTER',
            },
          },
        },
      },
    }
  }
  async validatePostalCode(
    postalCode: string,
    countryCode: string,
    city?: string
  ): Promise<{ status: boolean; message?: string }> {
    try {
      const shipDate = new Date().toISOString().split('T')[0]
      const inputCity = city
        ?.normalize('NFD') // remove accents
        .replace(/[\u0300-\u036f]/g, '')
        .toUpperCase()

      const token = await this.getAuthToken()
      const response = await fetch(`${this.config.baseUrl}/country/v1/postal/validate`, {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${token}`,
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          carrierCode: 'FDXE',
          shipDate: shipDate,
          postalCode: postalCode,
          countryCode: countryCode,
          city: inputCity,
        }),
      })

      if (!response.ok) {
        const errorData: any = await response.json()
        const message = errorData.errors?.[0]?.message || 'FedEx Postal Validation Failed'
        logger.error({ errorData }, 'FedEx Postal Validation Failed')
        return { status: false, message }
      }

      const data: any = await response.json()
      if (data.errors && data.errors.length > 0) {
        return { status: false, message: data.errors[0].message }
      }

      if (data.output) {
        if (data.output.postalCodeExists === false) {
          return {
            status: false,
            message: `Postal code ${postalCode} does not exist in ${countryCode}`,
          }
        }
        if (data.output.cityExists === false && inputCity) {
          return {
            status: false,
            message: `City ${inputCity} does not match postal code ${postalCode}`,
          }
        }
      }

      return { status: true }
    } catch (error) {
      logger.error(error, 'FedEx Postal Validation Error')
      return { status: false, message: error.message }
    }
  }
}
