"use node";

import { action } from "./_generated/server";
import { ConvexError, v } from "convex/values";
import { internal } from "./_generated/api";
import {
  validateAddressWithShippo,
  computeParcel,
  getShippingRatesFromShippo,
  selectBestRate,
  MAX_PARCEL_WEIGHT_G,
} from "./model/shippo";
import type { ShippingRateResult } from "./model/checkout";

export const validateAddress = action({
  args: {
    addressLine1: v.string(),
    additionalInformation: v.optional(v.string()),
    city: v.string(),
    postalCode: v.string(),
    country: v.string(),
    name: v.optional(v.string()),
  },
  handler: async (ctx, args) => {
    const identity = await ctx.auth.getUserIdentity();
    if (!identity) {
      throw new Error("You must be signed in to validate an address.");
    }

    return await validateAddressWithShippo({
      addressLine1: args.addressLine1,
      additionalInformation: args.additionalInformation,
      city: args.city,
      postalCode: args.postalCode,
      country: args.country,
      name: args.name,
    });
  },
});

export const getShippingRate = action({
  args: {
    addressId: v.id("addresses"),
    sessionId: v.optional(v.string()),
  },
  handler: async (ctx, args): Promise<ShippingRateResult> => {
    const identity = await ctx.auth.getUserIdentity();
    if (!identity) {
      throw new Error("You must be signed in to get shipping rates.");
    }

    const address = await ctx.runQuery(internal.checkout.getAddressById, {
      addressId: args.addressId,
    });

    if (address.isValidated !== true) {
      console.warn(
        `Shipping rate requested for unvalidated address ${args.addressId}. ` +
          "The shipping address step should validate before proceeding.",
      );
    }

    const userId = await ctx.runQuery(internal.checkout.getCurrentUserId);

    const cartResult = await ctx.runQuery(
      internal.checkout.validateCartInternal,
      { userId, sessionId: args.sessionId },
    );

    if (!cartResult) {
      throw new ConvexError("Your cart is empty.");
    }
    if (!cartResult.valid) {
      throw new ConvexError(
        "Your cart has issues that need to be resolved before checkout.",
      );
    }

    const parcel = computeParcel(cartResult.items);

    const weightG = parseFloat(parcel.weight);
    if (weightG > MAX_PARCEL_WEIGHT_G) {
      const actualKg = (weightG / 1000).toFixed(1);
      const maxKg = MAX_PARCEL_WEIGHT_G / 1000;
      throw new ConvexError(
        `Your order weighs ${actualKg}kg, which exceeds our maximum shipping weight of ${maxKg}kg. ` +
          "Please remove some items or reduce quantities to proceed.",
      );
    }

    const shippoAddress = {
      name: address.fullName,
      street1: address.addressLine1,
      street2: address.additionalInformation,
      city: address.city,
      zip: address.postalCode,
      country: address.country,
      phone: address.phone,
    };

    const sourceAddressId = process.env.SHIPPO_SOURCE_ADDRESS_ID;
    if (!sourceAddressId) {
      throw new ConvexError(
        "Shipping configuration is incomplete (missing source address).",
      );
    }

    const { shipmentObjectId, rates } = await getShippingRatesFromShippo({
      sourceAddressId,
      destinationAddress: shippoAddress,
      parcels: [parcel],
    });

    const { selected, alternatives } = selectBestRate(rates);

    const mapRate = (r: typeof selected) => ({
      provider: r.provider,
      serviceName: r.servicelevelName,
      serviceToken: r.servicelevelToken,
      amount: parseFloat(r.amount),
      currency: r.currency,
      estimatedDays: r.estimatedDays,
      durationTerms: r.durationTerms,
      carrierAccount: r.carrierAccount,
    });

    return {
      shipmentObjectId,
      selectedRate: mapRate(selected),
      alternativeRates: alternatives.map(mapRate),
      cartSubtotal: cartResult.subtotal,
      shippingTotal: parseFloat(selected.amount),
      orderTotal: cartResult.subtotal + parseFloat(selected.amount),
    };
  },
});
