/**
 * FedEx REST API Client
 * Handles OAuth authentication, shipment creation, cancellation, and tracking
 */

import { env } from "./env"
import { logger } from "./logger"
import type { WooOrder, WooAddress } from "./woo"
import type {
  FedExAuthResponse,
  FedExShipmentRequest,
  FedExShipmentResponse,
  FedExCancelRequest,
  FedExCancelResponse,
  FedExTrackRequest,
  FedExTrackResponse,
  FedExShipmentResult,
  FedExErrorResponse,
  FedExAddress,
  FedExContact,
  FedExParty,
} from "./types/fedex"

// Default item weight when WooCommerce doesn't provide it
const DEFAULT_ITEM_WEIGHT_LB = 0.5

// Default origin address (your warehouse/fulfillment center)
const DEFAULT_ORIGIN: FedExParty = {
  contact: {
    personName: "Factor Peptides",
    phoneNumber: "5550000000",
    companyName: "Factor Peptides",
    emailAddress: "shipping@factorpeptides.com",
  },
  address: {
    streetLines: ["123 Warehouse Ave"],
    city: "Los Angeles",
    stateOrProvinceCode: "CA",
    postalCode: "90001",
    countryCode: "US",
  },
}

// ─── OAuth Token Cache ───────────────────────────────────────────

let cachedToken: string | null = null
let tokenExpiresAt = 0

/**
 * FedEx API Client
 */
export class FedEx {
  /**
   * Get FedEx API base URL
   */
  private static getBaseUrl(): string {
    return env.FEDEX_API_URL || "https://apis-sandbox.fedex.com"
  }

  /**
   * Authenticate with FedEx OAuth2 and cache the token
   * Tokens expire after 60 minutes; we refresh at 55 minutes
   */
  static async getAccessToken(): Promise<string> {
    // Return cached token if still valid (with 5-minute buffer)
    if (cachedToken && Date.now() < tokenExpiresAt) {
      return cachedToken
    }

    const url = `${this.getBaseUrl()}/oauth/token`

    const body = new URLSearchParams({
      grant_type: "client_credentials",
      client_id: env.FEDEX_CLIENT_ID || "",
      client_secret: env.FEDEX_CLIENT_SECRET || "",
    })

    const response = await fetch(url, {
      method: "POST",
      headers: {
        "Content-Type": "application/x-www-form-urlencoded",
      },
      body: body.toString(),
    })

    if (!response.ok) {
      const errorText = await response.text()
      logger.error("fedex", "OAuth authentication failed", {
        status: response.status,
        error: errorText,
      })
      throw new Error(`FedEx OAuth failed: ${response.status} ${response.statusText}`)
    }

    const data: FedExAuthResponse = await response.json()

    cachedToken = data.access_token
    // Refresh 5 minutes before expiry
    tokenExpiresAt = Date.now() + (data.expires_in - 300) * 1000

    logger.info("fedex", "OAuth token obtained", {
      expiresIn: data.expires_in,
      scope: data.scope,
    })

    return cachedToken
  }

  /**
   * Make authenticated request to FedEx API
   */
  private static async request<T>(
    endpoint: string,
    options: RequestInit = {}
  ): Promise<{ success: boolean; data?: T; error?: string; errorDetails?: any }> {
    const url = `${this.getBaseUrl()}${endpoint}`

    let token: string
    try {
      token = await this.getAccessToken()
    } catch (error) {
      return {
        success: false,
        error: error instanceof Error ? error.message : "OAuth authentication failed",
        errorDetails: error,
      }
    }

    try {
      const response = await fetch(url, {
        ...options,
        headers: {
          "Content-Type": "application/json",
          Authorization: `Bearer ${token}`,
          ...options.headers,
        },
      })

      const responseText = await response.text()
      let data: T | FedExErrorResponse

      try {
        data = JSON.parse(responseText)
      } catch {
        if (!response.ok) {
          return {
            success: false,
            error: `FedEx API error: ${response.status} ${response.statusText}`,
            errorDetails: responseText,
          }
        }
        data = {} as T
      }

      if (!response.ok) {
        const errorResp = data as FedExErrorResponse
        const message =
          errorResp.errors?.[0]?.message || `HTTP ${response.status}`
        return {
          success: false,
          error: message,
          errorDetails: errorResp,
        }
      }

      return {
        success: true,
        data: data as T,
      }
    } catch (error) {
      return {
        success: false,
        error: error instanceof Error ? error.message : "Unknown error",
        errorDetails: error,
      }
    }
  }

  /**
   * Map WooCommerce address to FedEx address format
   */
  private static mapAddress(wooAddress: WooAddress): FedExAddress {
    const streetLines = [wooAddress.address_1]
    if (wooAddress.address_2) {
      streetLines.push(wooAddress.address_2)
    }

    return {
      streetLines,
      city: wooAddress.city,
      stateOrProvinceCode: wooAddress.state,
      postalCode: wooAddress.postcode,
      countryCode: wooAddress.country,
      residential: true,
    }
  }

  /**
   * Map WooCommerce address to FedEx party (contact + address)
   */
  private static mapRecipient(wooAddress: WooAddress, email?: string): FedExParty {
    return {
      contact: {
        personName: `${wooAddress.first_name} ${wooAddress.last_name}`.trim(),
        phoneNumber: "0000000000", // Placeholder — WooCommerce address doesn't store phone
        emailAddress: email || wooAddress.email || undefined,
      },
      address: this.mapAddress(wooAddress),
    }
  }

  /**
   * Check if order already synced to FedEx
   * Returns tracking number if already synced, null otherwise
   */
  private static getExistingTrackingNumber(order: WooOrder): string | null {
    const meta = order.meta_data.find(
      (m) => m.key === "_fedex_tracking_number"
    )
    return meta?.value ? String(meta.value) : null
  }

  /**
   * Create shipment in FedEx
   * Implements idempotency — checks if tracking number already exists before creating
   */
  static async createShipment(order: WooOrder): Promise<FedExShipmentResult> {
    try {
      // Idempotency: check for existing tracking number
      const existingTracking = this.getExistingTrackingNumber(order)
      if (existingTracking) {
        logger.info("fedex", "Shipment already created in FedEx", {
          wooOrderId: order.id,
          trackingNumber: existingTracking,
        })
        return {
          success: true,
          trackingNumber: existingTracking,
        }
      }

      // Determine email from billing address
      const customerEmail = order.billing.email || order.billing_email || ""

      // Use shipping address if available, fall back to billing
      const shippingAddress =
        order.shipping.address_1 ? order.shipping : order.billing

      const recipient = this.mapRecipient(shippingAddress, customerEmail)

      // Calculate total weight from all items
      const totalWeight = order.line_items.reduce(
        (sum, item) => sum + DEFAULT_ITEM_WEIGHT_LB * item.quantity,
        0
      )

      // Build FedEx shipment request
      const shipmentRequest: FedExShipmentRequest = {
        labelResponseOptions: "URL_ONLY",
        accountNumber: {
          value: env.FEDEX_ACCOUNT_NUMBER || "",
        },
        requestedShipment: {
          shipper: DEFAULT_ORIGIN,
          recipients: [recipient],
          pickupType: "DROPOFF_AT_FEDEX_LOCATION",
          serviceType: "GROUND_HOME_DELIVERY",
          packagingType: "YOUR_PACKAGING",
          shippingChargesPayment: {
            paymentType: "SENDER",
            payor: {
              responsibleParty: {
                accountNumber: {
                  value: env.FEDEX_ACCOUNT_NUMBER || "",
                },
              },
            },
          },
          labelSpecification: {
            labelFormatType: "COMMON2D",
            imageType: "PDF",
          },
          requestedPackageLineItems: [
            {
              weight: {
                units: "LB",
                value: Math.max(totalWeight, 0.1), // FedEx requires > 0
              },
              customerReferences: [
                {
                  customerReferenceType: "CUSTOMER_REFERENCE",
                  value: `WC_ORDER_${order.id}`,
                },
              ],
              itemDescription: "Instruction Booklet",
            },
          ],
        },
      }

      logger.info("fedex", "Creating shipment in FedEx", {
        wooOrderId: order.id,
        serviceType: "GROUND_HOME_DELIVERY",
        totalWeight,
        destination: `${shippingAddress.city}, ${shippingAddress.state} ${shippingAddress.country}`,
      })

      // Make API request
      const result = await this.request<FedExShipmentResponse>("/ship/v1/shipments", {
        method: "POST",
        body: JSON.stringify(shipmentRequest),
      })

      if (!result.success || !result.data) {
        logger.error("fedex", "Failed to create shipment in FedEx", {
          wooOrderId: order.id,
          error: result.error,
          errorDetails: result.errorDetails,
        })
        return {
          success: false,
          error: result.error || "Failed to create shipment",
          errorDetails: result.errorDetails,
        }
      }

      const shipment = result.data.output.transactionShipments[0]

      if (!shipment) {
        logger.error("fedex", "No transaction shipment in FedEx response", {
          wooOrderId: order.id,
          response: result.data,
        })
        return {
          success: false,
          error: "No shipment data in FedEx response",
        }
      }

      logger.info("fedex", "Successfully created shipment in FedEx", {
        wooOrderId: order.id,
        trackingNumber: shipment.masterTrackingNumber,
        serviceType: shipment.serviceType,
        shipDate: shipment.shipDatestamp,
      })

      return {
        success: true,
        trackingNumber: shipment.masterTrackingNumber,
        shipDate: shipment.shipDatestamp,
      }
    } catch (error) {
      logger.error("fedex", "Unexpected error creating FedEx shipment", {
        wooOrderId: order.id,
        error: error instanceof Error ? error.message : "Unknown error",
        stack: error instanceof Error ? error.stack : undefined,
      })
      return {
        success: false,
        error: error instanceof Error ? error.message : "Unexpected error",
        errorDetails: error,
      }
    }
  }

  /**
   * Cancel a shipment in FedEx
   */
  static async cancelShipment(trackingNumber: string): Promise<FedExShipmentResult> {
    try {
      logger.info("fedex", "Cancelling shipment in FedEx", { trackingNumber })

      const cancelRequest: FedExCancelRequest = {
        accountNumber: {
          value: env.FEDEX_ACCOUNT_NUMBER || "",
        },
        trackingNumber,
        senderCountryCode: "US",
        deletionControl: "DELETE_ALL_PACKAGES",
      }

      const result = await this.request<FedExCancelResponse>(
        "/ship/v1/shipments/cancel",
        {
          method: "PUT",
          body: JSON.stringify(cancelRequest),
        }
      )

      if (!result.success) {
        logger.error("fedex", "Failed to cancel shipment in FedEx", {
          trackingNumber,
          error: result.error,
          errorDetails: result.errorDetails,
        })
        return {
          success: false,
          error: result.error || "Failed to cancel shipment",
          errorDetails: result.errorDetails,
        }
      }

      logger.info("fedex", "Successfully cancelled shipment in FedEx", {
        trackingNumber,
      })

      return { success: true, trackingNumber }
    } catch (error) {
      logger.error("fedex", "Unexpected error cancelling FedEx shipment", {
        trackingNumber,
        error: error instanceof Error ? error.message : "Unknown error",
        stack: error instanceof Error ? error.stack : undefined,
      })
      return {
        success: false,
        error: error instanceof Error ? error.message : "Unexpected error",
        errorDetails: error,
      }
    }
  }

  /**
   * Track a shipment in FedEx
   */
  static async trackShipment(trackingNumber: string): Promise<{
    success: boolean
    status?: string
    statusDescription?: string
    events?: Array<{ date: string; description: string; location?: string }>
    error?: string
  }> {
    try {
      const trackRequest: FedExTrackRequest = {
        trackingInfo: [
          {
            trackingNumberInfo: {
              trackingNumber,
            },
          },
        ],
        includeDetailedScans: true,
      }

      const result = await this.request<FedExTrackResponse>(
        "/track/v1/trackingnumbers",
        {
          method: "POST",
          body: JSON.stringify(trackRequest),
        }
      )

      if (!result.success || !result.data) {
        return {
          success: false,
          error: result.error || "Failed to track shipment",
        }
      }

      const trackResult =
        result.data.output.completeTrackResults?.[0]?.trackResults?.[0]

      if (!trackResult) {
        return {
          success: false,
          error: "No tracking results found",
        }
      }

      const events = trackResult.scanEvents?.map((event) => ({
        date: event.date,
        description: event.eventDescription,
        location: [event.city, event.stateOrProvinceCode, event.countryCode]
          .filter(Boolean)
          .join(", "),
      }))

      return {
        success: true,
        status: trackResult.latestStatusDetail?.code,
        statusDescription: trackResult.latestStatusDetail?.statusByLocale,
        events,
      }
    } catch (error) {
      return {
        success: false,
        error: error instanceof Error ? error.message : "Unexpected error",
      }
    }
  }
}
