"use node";

import Stripe from "stripe";
import { action } from "./_generated/server";
import { internal } from "./_generated/api";
import { v } from "convex/values";
import { getShipmentRateObjectId } from "./model/shippo";

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);

const SHIPPO_TRANSACTIONS_URL = "https://api.goshippo.com/transactions/";

type AcceptReturnResult =
  | { success: true; returnTrackingNumber: string }
  | { success: false; code: string; message: string };

export const acceptReturn = action({
  args: { orderId: v.id("orders") },
  handler: async (ctx, { orderId }): Promise<AcceptReturnResult> => {
    // 1. Auth — must be admin
    const userId = await ctx.runQuery(internal.checkout.getCurrentUserId);
    const user = await ctx.runQuery(internal.users.getById, { userId });
    if (user.role !== "admin" && user.role !== "super_admin") {
      throw new Error("Unauthorized: admin access required");
    }

    // 2. Load order and validate
    const order = await ctx.runQuery(internal.orders.getOrderForRefund, {
      id: orderId,
    });
    if (!order) throw new Error("Order not found");
    if (order.status !== "delivered") {
      return {
        success: false,
        code: "INVALID_STATUS",
        message: "Only delivered orders can have a return accepted.",
      };
    }
    if (!order.returnRequestedAt) {
      return {
        success: false,
        code: "NO_RETURN_REQUEST",
        message: "No return has been requested for this order.",
      };
    }
    if (order.returnTrackingNumber) {
      return {
        success: false,
        code: "DUPLICATE_RETURN_LABEL",
        message: "A return label has already been created for this order.",
      };
    }
    if (!order.shippoShipmentId) {
      return {
        success: false,
        code: "NO_SHIPMENT",
        message: "Order has no Shippo shipment ID.",
      };
    }

    // 3. Resolve rate object ID from the stored shipment
    let rateObjectId: string;
    try {
      rateObjectId = await getShipmentRateObjectId(
        order.shippoShipmentId,
        order.shippingServiceCode,
        order.carrier,
      );
    } catch (err) {
      const message =
        err instanceof Error ? err.message : "Failed to resolve shipping rate.";
      return { success: false, code: "RATE_ERROR", message };
    }

    // 4. Purchase return label via Shippo POST /transactions (is_return: true)
    const apiKey = process.env.SHIPPO_API_KEY;
    if (!apiKey) {
      return {
        success: false,
        code: "CONFIG_ERROR",
        message: "Shippo API key not configured.",
      };
    }

    let txResponse: Response;
    try {
      txResponse = await fetch(SHIPPO_TRANSACTIONS_URL, {
        method: "POST",
        headers: {
          Authorization: `ShippoToken ${apiKey}`,
          "Content-Type": "application/json",
        },
        body: JSON.stringify({ rate: rateObjectId, async: false, is_return: true }),
      });
    } catch {
      return {
        success: false,
        code: "SHIPPO_UNREACHABLE",
        message: "Could not reach Shippo to create the return label.",
      };
    }

    if (!txResponse.ok) {
      let detail = "";
      try {
        detail = JSON.stringify(await txResponse.json());
      } catch {}
      console.error("Shippo /transactions/ return error:", txResponse.status, detail);
      return {
        success: false,
        code: "SHIPPO_ERROR",
        message: `Shippo returned status ${txResponse.status}.`,
      };
    }

    let tx: {
      object_id: string;
      status: string;
      tracking_number?: string;
      tracking_url_provider?: string;
      label_url?: string;
      messages?: Array<{ source: string; text: string; code: string }>;
    };
    try {
      tx = await txResponse.json();
    } catch {
      return {
        success: false,
        code: "PARSE_ERROR",
        message: "Shippo response could not be parsed.",
      };
    }

    if (tx.status !== "SUCCESS") {
      const errMsg =
        tx.messages?.map((m) => m.text).join("; ") ?? "Unknown Shippo error";
      console.error("Shippo return transaction failed:", tx.status, errMsg);
      return {
        success: false,
        code: "SHIPPO_ERROR",
        message: errMsg,
      };
    }

    if (!tx.tracking_number || !tx.label_url) {
      return {
        success: false,
        code: "NO_TRACKING",
        message: "Shippo returned success but no tracking number or label URL.",
      };
    }

    // 5. Persist return label data
    await ctx.runMutation(internal.orders.applyReturnAccepted, {
      orderId,
      adminUserId: userId,
      returnLabelUrl: tx.label_url,
      returnTrackingNumber: tx.tracking_number,
      returnCarrier: order.carrier,
    });

    // 6. Send return label email to customer
    const customer = await ctx.runQuery(internal.users.getById, {
      userId: order.userId,
    });
    await ctx.runMutation(internal.emails.sendReturnLabelEmail, {
      to: customer.email,
      firstName: customer.firstName ?? customer.name.split(" ")[0] ?? "there",
      orderNumber: order.orderNumber,
      returnLabelUrl: tx.label_url,
    });

    return { success: true, returnTrackingNumber: tx.tracking_number };
  },
});

export const issueRefund = action({
  args: { orderId: v.id("orders") },
  handler: async (ctx, { orderId }): Promise<{ success: boolean }> => {
    // 1. Auth — must be an admin
    const userId = await ctx.runQuery(internal.checkout.getCurrentUserId);
    const user = await ctx.runQuery(internal.users.getById, { userId });
    if (user.role !== "admin" && user.role !== "super_admin") {
      throw new Error("Unauthorized: admin access required");
    }

    // 2. Load order
    const order = await ctx.runQuery(internal.orders.getOrderForRefund, {
      id: orderId,
    });
    if (!order) throw new Error("Order not found");
    if (!order.stripePaymentIntentId)
      throw new Error("Order has no Stripe payment intent to refund");
    if (order.paymentStatus === "refunded")
      throw new Error("Order has already been refunded");

    // 3. Create Stripe refund — idempotent: skip if one already exists
    const existingRefunds = await stripe.refunds.list({
      payment_intent: order.stripePaymentIntentId,
      limit: 10,
    });
    const hasRefund = existingRefunds.data.some(
      (r) => r.status === "succeeded" || r.status === "pending",
    );
    if (!hasRefund) {
      await stripe.refunds.create({
        payment_intent: order.stripePaymentIntentId,
      });
    }

    // 4. Mark order refunded and restore stock — applyRefund is also idempotent
    await ctx.runMutation(internal.orders.applyRefund, {
      id: orderId,
      adminUserId: userId,
    });

    return { success: true };
  },
});
