/**
 * Klaviyo API Client
 * Handles sending transactional email events via Klaviyo Events API
 *
 * Event property keys match the Klaviyo email template variables:
 *   {{ event.extra.OrderNumber }}
 *   {{ event.extra.Items.0.Name }}
 *   {{ event.extra.ShippingAddress.PostCode }}
 *   {{ event|lookup:'$currency_code' }}
 */

import { env } from "./env"
import { logger } from "./logger"
import type { WooOrder } from "./woo"

const KLAVIYO_API_BASE = "https://a.klaviyo.com/api"
const KLAVIYO_API_REVISION = "2024-10-15"

interface KlaviyoResult {
  success: boolean
  error?: string
}

export class Klaviyo {
  /**
   * Get authorization headers for Klaviyo API
   */
  private static getHeaders(): Record<string, string> {
    return {
      Authorization: `Klaviyo-API-Key ${env.KLAVIYO_API_KEY || ""}`,
      "Content-Type": "application/json",
      revision: KLAVIYO_API_REVISION,
    }
  }

  /**
   * Make a POST request to Klaviyo API
   */
  private static async request(
    endpoint: string,
    body: Record<string, unknown>
  ): Promise<KlaviyoResult> {
    const url = `${KLAVIYO_API_BASE}${endpoint}`

    try {
      const response = await fetch(url, {
        method: "POST",
        headers: this.getHeaders(),
        body: JSON.stringify(body),
      })

      if (!response.ok) {
        const errorBody = await response.text()
        return {
          success: false,
          error: `Klaviyo API error ${response.status}: ${errorBody}`,
        }
      }

      logger.debug("klaviyo", `API response ${response.status}`, { endpoint })
      return { success: true }
    } catch (error) {
      return {
        success: false,
        error: error instanceof Error ? error.message : "Unknown error",
      }
    }
  }

  /**
   * Build Klaviyo event payload
   */
  private static buildEventPayload(
    metricName: string,
    profileEmail: string,
    properties: Record<string, unknown>,
    uniqueId: string,
    profileFirstName?: string,
    profileLastName?: string
  ) {
    const profileAttributes: Record<string, string> = { email: profileEmail }
    if (profileFirstName) profileAttributes.first_name = profileFirstName
    if (profileLastName) profileAttributes.last_name = profileLastName

    return {
      data: {
        type: "event",
        attributes: {
          metric: {
            data: {
              type: "metric",
              attributes: { name: metricName },
            },
          },
          profile: {
            data: {
              type: "profile",
              attributes: profileAttributes,
            },
          },
          properties,
          unique_id: uniqueId,
        },
      },
    }
  }

  /**
   * Build order properties matching Klaviyo template variable structure
   *
   * Klaviyo templates use {{ event.extra.X }} to access the $extra property.
   * All order data is nested inside $extra so templates resolve correctly.
   *
   * Template access patterns:
   *   {{ event.extra.OrderNumber }}
   *   {{ event.extra.Items.0.Name }}
   *   {{ event.extra.Items.0.Price }}
   *   {{ event.extra.Items.0.Quantity }}
   *   {{ event.extra.Items.0|lookup:'$extra'|lookup:'Images'|lookup:'0'|lookup:'URL' }}
   *   {{ event.extra.ShippingAddress.Address1 }}
   *   {{ event.extra.ShippingAddress.PostCode }}
   *   {{ event.extra.ShippingAddress.Country }}
   *   {{ event.extra.BillingAddress.FirstName }}
   *   {{ event|lookup:'$value' }}
   *   {{ event|lookup:'$currency_code' }}
   */
  private static buildOrderProperties(
    order: WooOrder,
    chargeId: string
  ): Record<string, unknown> {
    const orderData: Record<string, unknown> = {
      OrderNumber: String(order.id),
      OrderTotal: order.total,
      Currency: order.currency,
      ShippingMethod: order.shipping_lines[0]?.method_title || "",
      ShippingTotal: order.shipping_lines[0]?.total || "0.00",
      StripeChargeId: chargeId,
      PaymentMethod: "Credit Card (Stripe)",

      Items: order.line_items.map((item) => ({
        Name: item.name,
        ProductId: item.product_id,
        Quantity: item.quantity,
        Price: item.total,
        ImageURL: item.image?.src || "",
        // WooCommerce-compatible image path for {{ Items.0|lookup:'$extra'|lookup:'Images'|... }}
        $extra: {
          Images: [{ URL: item.image?.src || "" }],
        },
      })),

      BillingAddress: {
        FirstName: order.billing.first_name,
        LastName: order.billing.last_name,
        Address1: order.billing.address_1,
        Address2: order.billing.address_2,
        City: order.billing.city,
        State: order.billing.state,
        PostCode: order.billing.postcode,
        Country: order.billing.country,
      },

      ShippingAddress: {
        FirstName: order.shipping.first_name,
        LastName: order.shipping.last_name,
        Address1: order.shipping.address_1,
        Address2: order.shipping.address_2,
        City: order.shipping.city,
        State: order.shipping.state,
        PostCode: order.shipping.postcode,
        Country: order.shipping.country,
      },
    }

    return {
      // Klaviyo special top-level keys (accessible via event|lookup)
      $value: parseFloat(order.total),
      $currency_code: order.currency,
      // Order data inside $extra (accessed as {{ event.extra.X }} in templates)
      $extra: orderData,
      // Also spread at top level for {{ event.X }} access
      ...orderData,
    }
  }

  /**
   * Send "Placed Order" event to customer profile
   */
  static async sendOrderReceipt(
    order: WooOrder,
    chargeId: string
  ): Promise<KlaviyoResult> {
    const customerEmail = order.billing.email || order.billing_email || ""

    if (!customerEmail) {
      logger.warn("klaviyo", "No customer email found, skipping receipt", {
        orderId: order.id,
      })
      return { success: false, error: "No customer email" }
    }

    const properties = this.buildOrderProperties(order, chargeId)
    const payload = this.buildEventPayload(
      "Placed Order",
      customerEmail,
      properties,
      `order_${order.id}_receipt_${Date.now()}`,
      order.billing.first_name,
      order.billing.last_name
    )

    const result = await this.request("/events", payload)

    if (result.success) {
      logger.info("klaviyo", "Placed Order event sent to customer", {
        orderId: order.id,
        email: customerEmail,
      })
    } else {
      logger.error("klaviyo", "Failed to send Placed Order event", {
        orderId: order.id,
        email: customerEmail,
        error: result.error,
      })
    }

    return result
  }

  /**
   * Send "Admin Order Notification" event to admin profile
   */
  static async sendAdminNotification(
    order: WooOrder,
    chargeId: string
  ): Promise<KlaviyoResult> {
    const adminEmail = env.KLAVIYO_ADMIN_EMAIL || ""
    const customerEmail = order.billing.email || order.billing_email || ""

    const properties = {
      ...this.buildOrderProperties(order, chargeId),
      CustomerEmail: customerEmail,
      CustomerName: `${order.billing.first_name} ${order.billing.last_name}`.trim(),
    }

    const payload = this.buildEventPayload(
      "Admin Order Notification",
      adminEmail,
      properties,
      `order_${order.id}_admin_${Date.now()}`
    )

    const result = await this.request("/events", payload)

    if (result.success) {
      logger.info("klaviyo", "Admin notification event sent", {
        orderId: order.id,
        adminEmail,
      })
    } else {
      logger.error("klaviyo", "Failed to send admin notification event", {
        orderId: order.id,
        adminEmail,
        error: result.error,
      })
    }

    return result
  }

  /**
   * Fire-and-forget: send both customer receipt and admin notification
   * Errors are logged but never thrown
   */
  static sendOrderEmails(order: WooOrder, chargeId: string): void {
    this.sendOrderReceipt(order, chargeId).catch((error) => {
      logger.error("klaviyo", "Unexpected error sending order receipt", {
        orderId: order.id,
        error: error instanceof Error ? error.message : "Unknown error",
      })
    })

    this.sendAdminNotification(order, chargeId).catch((error) => {
      logger.error("klaviyo", "Unexpected error sending admin notification", {
        orderId: order.id,
        error: error instanceof Error ? error.message : "Unknown error",
      })
    })
  }
}
