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'

export default class SMSAService implements ShippingProviderContract {
  private config = shippingConfig.smsa

  private async sendSoapRequest(action: string, body: string) {
    const envelope = `<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    ${body}
  </soap:Body>
</soap:Envelope>`

    if (!this.config.soapPasskey) {
      throw new Error('SMSA_SOAP_PASSKEY is not configured in .env')
    }

    logger.debug({ envelope, action }, 'Sending SMSA SOAP Request')
    const response = await fetch(this.config.baseUrl, {
      method: 'POST',
      headers: {
        'Content-Type': 'text/xml; charset=utf-8',
        'SOAPAction': `"http://track.smsaexpress.com/secom/${action}"`,
      },
      body: envelope,
    })

    const responseText = await response.text()

    if (!response.ok) {
      logger.error({ responseText, envelope, action }, 'SMSA SOAP Request Failed')
      throw new Error(`SMSA API Error: ${response.statusText}`)
    }

    if (responseText.includes('Invalid Passkey')) {
      throw new Error(
        'SMSA Error: Invalid Passkey. Please ensure you are using a SOAP Passkey, not a REST ApiKey.'
      )
    }

    return responseText
  }

  private async sendRestRequest(method: string, endpoint: string, body?: any) {
    if (!this.config.passkey) {
      throw new Error('SMSA_PASSKEY is not configured in .env')
    }

    const url = `${this.config.restBaseUrl}${endpoint}`
    logger.debug({ url, method, body }, 'Sending SMSA REST Request')

    const response = await fetch(url, {
      method,
      headers: {
        'Content-Type': 'application/json',
        'ApiKey': this.config.passkey,
      },
      body: body ? JSON.stringify(body) : undefined,
    })

    const responseText = await response.text()
    let responseData
    try {
      responseData = JSON.parse(responseText)
    } catch (e) {
      responseData = responseText
    }

    if (!response.ok) {
      logger.error({ responseData, url, method }, 'SMSA REST Request Failed')
      throw new Error(`SMSA REST API Error: ${response.statusText}`)
    }

    return responseData
  }

  private parseXmlValue(xml: string, tag: string): string {
    const match = xml.match(new RegExp(`<${tag}>(.*?)<\/${tag}>`))
    return match ? match[1] : ''
  }

  async createShipment(order: Order): Promise<ShipmentResult> {
    try {
      const body = {
        refNo: `ORDER-${order.id}`,
        sentDate: new Date().toISOString(),
        idNo: `ORDER-${order.id}`,
        cName: order.destination_customer_name,
        cntry: 'SA',
        cCity: 'Riyadh',
        cZip: '12345',
        cMobile: order.destination_mobile_number,
        cAddr1: order.destination_road_id || 'Address',
        shipType: 'DLV',
        pcs: order.total_no_of_packages,
        weight: order.total_weight,
        itemDesc: 'General Goods',
      }

      const response = await this.sendRestRequest('POST', '/api/shipment/b2c/new', body)

      // The response might be the tracking number directly or an object
      const trackingNumber = typeof response === 'string' ? response : response.awbNo || response

      if (
        !trackingNumber ||
        (typeof trackingNumber === 'string' && trackingNumber.startsWith('Failed'))
      ) {
        throw new Error(`SMSA Error: ${JSON.stringify(response)}`)
      }

      return {
        trackingNumber: trackingNumber.toString(),
        provider: 'SMSA',
      }
    } catch (error) {
      logger.error(error, 'SMSA Create Shipment Error')
      throw error
    }
  }

  async trackShipment(trackingNumber: string): Promise<TrackingResult> {
    try {
      const response = await this.sendRestRequest(
        'GET',
        `/api/shipment/b2c/query/${trackingNumber}`
      )

      return {
        trackingNumber: trackingNumber,
        status: response.status || 'UNKNOWN',
        history: response.history || [],
      }
    } catch (error) {
      logger.error(error, 'SMSA Track Shipment Error')
      throw error
    }
  }

  async cancelShipment(trackingNumber: string): Promise<boolean> {
    try {
      const body = `<cancelShipment xmlns="http://track.smsaexpress.com/secom/">
        <passKey>${this.config.soapPasskey}</passKey>
        <awbNo>${trackingNumber}</awbNo>
        <reas>Cancelled by user</reas>
      </cancelShipment>`

      const responseXml = await this.sendSoapRequest('cancelShipment', body)
      const result = this.parseXmlValue(responseXml, 'cancelShipmentResult')

      return result === 'Success'
    } catch (error) {
      return false
    }
  }

  async generateLabel(trackingNumber: string): Promise<string> {
    try {
      const response = await this.sendRestRequest(
        'GET',
        `/api/shipment/b2c/label/${trackingNumber}`
      )

      // The response might be a base64 string or an object with a base64 field
      return typeof response === 'string' ? response : response.label || response.pdf || ''
    } catch (error) {
      logger.error(error, 'SMSA Generate Label Error')
      throw error
    }
  }

  async calculateRate(order: Order): Promise<RateResult> {
    try {
      // getShipCharges method
      const body = `<getShipCharges xmlns="http://track.smsaexpress.com/secom/">
      <passKey>${this.config.soapPasskey}</passKey>
      <shipCity>Dubai</shipCity>
      <shipCntry>AE</shipCntry>
      <destCity>Riyadh</destCity>
      <destCntry>SA</destCntry>
      <shipType>DLV</shipType>
      <codAmt>0</codAmt>
      <weight>${order.total_weight}</weight>
      </getShipCharges>`

      const responseXml = await this.sendSoapRequest('getShipCharges', body)
      const rate = this.parseXmlValue(responseXml, 'ShipCharges')

      // Rate might be just a number string
      return {
        amount: Number.parseFloat(rate) || 0,
        currency: this.parseXmlValue(responseXml, 'ShipChargesCurr') || 'SAR',
        provider: 'SMSA',
      }
    } catch (error) {
      logger.error(error, 'SMSA Calculate Rate Error')
      throw error
    }
  }

  async calculateInternationalRate(details: {
    originCity: string
    originCountry: string
    originPostalCode?: string
    destinationCity: string
    destinationCountry: string
    destinationPostalCode?: string
    weight: number
    numberOfPieces: number
    productGroup?: string
    productType?: string
  }): Promise<RateResult> {
    try {
      // getShipCharges method for international
      const body = `<getShipCharges xmlns="http://track.smsaexpress.com/secom/">
      <passKey>${this.config.soapPasskey}</passKey>
      <shipCity>${details.originCity}</shipCity>
      <shipCntry>${details.originCountry}</shipCntry>
      <destCity>${details.destinationCity}</destCity>
      <destCntry>${details.destinationCountry}</destCntry>
      <shipType>DLV</shipType>
      <codAmt>0</codAmt>
      <weight>${details.weight}</weight>
      </getShipCharges>`

      const responseXml = await this.sendSoapRequest('getShipCharges', body)
      const rate = this.parseXmlValue(responseXml, 'ShipCharges')
      console.log(
        responseXml,
        'raterateraterateraterateraterateraterate>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>'
      )

      return {
        amount: Number.parseFloat(rate) || 0,
        currency: this.parseXmlValue(responseXml, 'ShipChargesCurr') || 'SAR',
        provider: 'SMSA',
      }
    } catch (error) {
      logger.error(error, 'SMSA Calculate International Rate Error')
      throw error
    }
  }
}
