import { AbstractFulfillmentProviderService } from "@medusajs/framework/utils"
import {
  CalculatedShippingOptionPrice,
  CalculateShippingOptionPriceContext,
  CreateFulfillmentResult,
  FulfillmentOption,
  ValidateFulfillmentDataContext,
} from "@medusajs/framework/types"
import { MedusaError } from "@medusajs/framework/utils"

type InjectedDependencies = {
  logger: any
}

type Options = {
  apiKey: string
  originCity: string
}

export const RAJAONGKIR_API_BASE = "https://rajaongkir.komerce.id/api/v1"

class RajaongkirFulfillmentService extends AbstractFulfillmentProviderService {
  static identifier = "rajaongkir"

  protected logger_: any
  protected options_: Options

  constructor({ logger }: InjectedDependencies, options: Options) {
    super()

    this.logger_ = logger
    this.options_ = options
  }

  async getFulfillmentOptions(): Promise<FulfillmentOption[]> {
    return [
      {
        id: "jne",
        name: "JNE",
        courier: "jne",
      },
      {
        id: "jnt",
        name: "J&T Express",
        courier: "jnt",
      },
      {
        id: "sicepat",
        name: "SiCepat",
        courier: "sicepat",
      },
      {
        id: "pos",
        name: "POS Indonesia",
        courier: "pos",
      },
      {
        id: "tiki",
        name: "TIKI",
        courier: "tiki",
      },
    ]
  }

  async validateFulfillmentData(
    optionData: Record<string, unknown>,
    data: Record<string, unknown>,
    context: ValidateFulfillmentDataContext
  ): Promise<any> {
    return data
  }

  async calculatePrice(
    optionData: Record<string, unknown>,
    data: Record<string, unknown>,
    context: CalculateShippingOptionPriceContext
  ): Promise<CalculatedShippingOptionPrice> {
    const courier = optionData.courier as string

    const actualWeight = context.items?.reduce(
      (sum: number, item: any) => sum + Number(item.quantity || 0) * Number(item.variant?.weight || 0),
      0
    ) || 0

    const VOLUMETRIC_FACTOR = 6000
    let volumetricWeight = 0

    for (const item of context.items || []) {
      const variant = item.variant as any
      if (variant?.length && variant?.width && variant?.height) {
        const itemVolume = (variant.length * variant.width * variant.height) / VOLUMETRIC_FACTOR
        volumetricWeight += itemVolume * Number(item.quantity || 1)
      }
    }

    const chargeableWeight = Math.max(actualWeight, volumetricWeight, 100)

    const destination = (context.shipping_address?.metadata?.rajaongkir_subdistrict_id as string) || ""
    const origin = this.options_.originCity

    if (!destination) {
      throw new MedusaError(
        MedusaError.Types.INVALID_DATA,
        `Subdistrict ID not set for courier ${courier}. Please complete address selection.`
      )
    }

    try {
      const response = await fetch(`${RAJAONGKIR_API_BASE}/calculate/domestic-cost`, {
        method: "POST",
        headers: {
          key: this.options_.apiKey,
          "Content-Type": "application/x-www-form-urlencoded",
        },
        body: new URLSearchParams({
          origin,
          destination,
          weight: String(Math.round(chargeableWeight)),
          courier,
          price: "lowest",
        }).toString(),
      })

      const result = await response.json()

      if (result.meta?.status !== "success" || !result.data?.length) {
        throw new MedusaError(
          MedusaError.Types.INVALID_DATA,
          `No shipping rates found for courier ${courier}`
        )
      }

      const firstService = result.data[0]
      const cost = firstService.cost

      return {
        calculated_amount: cost,
        is_calculated_price_tax_inclusive: false,
      }
    } catch (error) {
      this.logger_.error(
        `RajaOngkir calculatePrice error: ${(error as any)?.message || error}`
      )
      throw error
    }
  }

  async canCalculate(): Promise<boolean> {
    return true
  }

  async validateOption(data: Record<string, any>): Promise<boolean> {
    return true
  }

  async createFulfillment(): Promise<CreateFulfillmentResult> {
    return {
      data: {},
      labels: [],
    }
  }

  async cancelFulfillment(): Promise<any> {
    return {}
  }

  async createReturnFulfillment(): Promise<CreateFulfillmentResult> {
    return {
      data: {},
      labels: [],
    }
  }
}

export default RajaongkirFulfillmentService
