import {
  internalAction,
  internalMutation,
  internalQuery,
} from "./_generated/server";
import { internal } from "./_generated/api";
import { v } from "convex/values";
import { recordOrderTimelineEvent } from "./model/orders";

// ─── Internal queries ─────────────────────────────────────────────────────────

export const getOrderByTracking = internalQuery({
  args: { trackingNumber: v.string(), carrier: v.string() },
  handler: async (ctx, { trackingNumber, carrier }) => {
    return await ctx.db
      .query("orders")
      .withIndex("by_tracking_number_and_carrier", (q) =>
        q.eq("trackingNumber", trackingNumber).eq("carrier", carrier),
      )
      .first();
  },
});

export const getOrderByReturnTracking = internalQuery({
  args: { returnTrackingNumber: v.string(), returnCarrier: v.string() },
  handler: async (ctx, { returnTrackingNumber, returnCarrier }) => {
    return await ctx.db
      .query("orders")
      .withIndex("by_return_tracking_number_and_carrier", (q) =>
        q.eq("returnTrackingNumber", returnTrackingNumber).eq("returnCarrier", returnCarrier),
      )
      .first();
  },
});

// ─── Internal mutation ────────────────────────────────────────────────────────

export const applyTrackingUpdate = internalMutation({
  args: {
    orderId: v.id("orders"),
    trackingStatus: v.string(),
    estimatedDelivery: v.optional(v.number()),
    isDelivered: v.boolean(),
    isReturnTracking: v.boolean(),
    payload: v.string(),
  },
  handler: async (ctx, args) => {
    const order = await ctx.db.get(args.orderId);
    if (!order) return;

    // Idempotency: skip if this exact status was already applied
    if (order.trackingStatus === args.trackingStatus) {
      console.log(
        `[shippoWebhook] Skipping duplicate tracking status "${args.trackingStatus}" for order ${args.orderId}`,
      );
      return;
    }

    const now = Date.now();

    // Return tracking updates never set status to "delivered" — only outbound does
    if (args.isDelivered && !args.isReturnTracking) {
      await ctx.db.patch(args.orderId, {
        trackingStatus: args.trackingStatus,
        ...(args.estimatedDelivery !== undefined
          ? { estimatedDelivery: args.estimatedDelivery }
          : {}),
        status: "delivered",
        actualDelivery: now,
        updatedAt: now,
      });
    } else {
      await ctx.db.patch(args.orderId, {
        trackingStatus: args.trackingStatus,
        ...(args.estimatedDelivery !== undefined
          ? { estimatedDelivery: args.estimatedDelivery }
          : {}),
        updatedAt: now,
      });
    }

    await recordOrderTimelineEvent(ctx, {
      orderId: args.orderId,
      eventType: args.isReturnTracking ? "return_tracking_update" : "tracking_update",
      source: "shippo_webhook",
      ...(args.isDelivered && !args.isReturnTracking
        ? { fromStatus: order.status, toStatus: "delivered" }
        : {}),
      payload: args.payload,
    });

    if (args.isDelivered && !args.isReturnTracking) {
      const customer = await ctx.db.get(order.userId);
      if (customer) {
        await ctx.scheduler.runAfter(0, internal.emails.sendDeliveryConfirmation, {
          to: customer.email,
          firstName: customer.firstName ?? customer.name.split(" ")[0] ?? "there",
          orderNumber: order.orderNumber,
        });
      }
    }
  },
});

// ─── Internal action ──────────────────────────────────────────────────────────

type ShippoTrackUpdatedPayload = {
  event?: string;
  data?: {
    carrier?: string;
    tracking_number?: string;
    eta?: string | null;
    tracking_status?: {
      status?: string;
      status_details?: string;
      status_date?: string;
    };
  };
};

export const handleTrackUpdated = internalAction({
  args: { body: v.string() },
  handler: async (ctx, { body }) => {
    let payload: ShippoTrackUpdatedPayload;
    try {
      payload = JSON.parse(body) as ShippoTrackUpdatedPayload;
    } catch {
      console.error("[shippoWebhook] Failed to parse JSON body");
      return;
    }

    if (payload.event !== "track_updated") {
      console.log("[shippoWebhook] Ignoring event:", payload.event);
      return;
    }

    const { data } = payload;
    const trackingNumber = data?.tracking_number;
    const carrier = data?.carrier;

    if (!trackingNumber || !carrier) {
      console.error(
        "[shippoWebhook] Missing tracking_number or carrier in payload",
      );
      return;
    }

    let order = await ctx.runQuery(
      internal.shippoWebhook.getOrderByTracking,
      { trackingNumber, carrier },
    );
    let isReturnTracking = false;

    if (!order) {
      order = await ctx.runQuery(
        internal.shippoWebhook.getOrderByReturnTracking,
        { returnTrackingNumber: trackingNumber, returnCarrier: carrier },
      );
      isReturnTracking = !!order;
    }

    if (!order) {
      console.log(
        `[shippoWebhook] No order found for tracking ${trackingNumber} / ${carrier}`,
      );
      return;
    }

    const trackingStatus = data?.tracking_status?.status ?? "UNKNOWN";
    const isDelivered = trackingStatus === "DELIVERED";

    const eta = data?.eta;
    const estimatedDelivery =
      eta && !isNaN(new Date(eta).getTime())
        ? new Date(eta).getTime()
        : undefined;

    await ctx.runMutation(internal.shippoWebhook.applyTrackingUpdate, {
      orderId: order._id,
      trackingStatus,
      estimatedDelivery,
      isDelivered,
      isReturnTracking,
      payload: body,
    });
  },
});
