// app/routes/api.checkout.jsx
import { gql } from "@apollo/client";
import { createClient } from "../graphql/publicClient";
import { getCookieFromRequest } from "../../utils/cookies";
import { PLACE_ORDER_STRIPE } from "../graphql/queries/checkoutQueries";
import { GET_CHECKOUT, SET_PAYMENT_METHOD_BASIC, CREATE_PAYPAL_EXPRESS_TOKEN,SET_PAYPAL_ON_CART,PLACE_ORDER }  from "../graphql/queries/checkoutQueries";
/**
 * React Router 7 resource route
 * POST /api/checkout
 *
 * Body: JSON { intent: string, ...payload }
 * Response: JSON { ok: true, ... } or { ok: false, message }
 *
 * This file implements the common intents your CheckoutContext/UI typically needs:
 * - ensure-cart
 * - set-guest-email
 * - set-address / set-addresses
 * - set-shipping-method
 * - set-payment-basic
 * - stripe-set-pm
 * - paypal-create-token
 * - paypal-set-on-cart
 * - klarna-create-session
 * - klarna-set-on-cart
 * - apply-coupon
 * - remove-coupon
 * - place-order
 * - place-order-stripe
 *
 * Notes:
 * - Uses Response(JSON) directly (no Remix json helper).
 * - Reads customerToken + cartId from cookies.
 * - Sets/clears cartId cookie via Set-Cookie header.
 * - If logged in, ensures customerCart is used and merges guest cart if needed.
 */

// ----------------------
// GraphQL
// ----------------------

const GET_CUSTOMER_CART_ID = gql`
  query GetCustomerCartId {
    customerCart {
      id
    }
  }
`;

const CREATE_EMPTY_CART = gql`
  mutation CreateEmptyCart {
    createEmptyCart
  }
`;

// Most common schema. If your Magento uses different arg names, adjust here.
const MERGE_CARTS = gql`
  mutation MergeCarts($source: String!, $destination: String!) {
    mergeCarts(sourceCartId: $source, destinationCartId: $destination) {
      id
    }
  }
`;

const SET_GUEST_EMAIL_ON_CART = gql`
  mutation SetGuestEmail($cartId: String!, $email: String!) {
    setGuestEmailOnCart(input: { cart_id: $cartId, email: $email }) {
      cart {
        email
      }
    }
  }
`;

const SET_SHIPPING_ADDRESSES_ON_CART = gql`
  mutation SetShippingAddresses($cartId: String!, $addresses: [ShippingAddressInput!]!) {
    setShippingAddressesOnCart(input: { cart_id: $cartId, shipping_addresses: $addresses }) {
      cart {
        shipping_addresses {
          firstname
          lastname
          postcode
          country {
            code
          }
        }
      }
    }
  }
`;

const SET_BILLING_ADDRESS_ON_CART = gql`
  mutation SetBillingAddress($cartId: String!, $billing: BillingAddressInput!) {
    setBillingAddressOnCart(input: { cart_id: $cartId, billing_address: $billing }) {
      cart {
        billing_address {
          firstname
          lastname
          postcode
          country {
            code
          }
        }
      }
    }
  }
`;

const SET_SHIPPING_METHOD_ON_CART = gql`
  mutation SetShippingMethod($cartId: String!, $carrier: String!, $method: String!) {
    setShippingMethodsOnCart(
      input: { cart_id: $cartId, shipping_methods: [{ carrier_code: $carrier, method_code: $method }] }
    ) {
      cart {
        shipping_addresses {
          selected_shipping_method {
            carrier_code
            method_code
          }
        }
      }
    }
  }
`;

const SET_PAYMENT_METHOD_ON_CART = gql`
  mutation SetPaymentMethodOnCart($cartId: String!, $code: String!) {
    setPaymentMethodOnCart(input: { cart_id: $cartId, payment_method: { code: $code } }) {
      cart {
        selected_payment_method {
          code
          title
        }
      }
    }
  }
`;

// Adobe Commerce Stripe Payments module style (your earlier mutation)
const SET_STRIPE_PAYMENT_METHOD_ON_CART = gql`
  mutation SetStripePaymentMethodOnCart(
    $cartId: String!
    $paymentMethod: String!
    $save: Boolean!
    $usePaymentElement: Boolean!
  ) {
    setPaymentMethodOnCart(
      input: {
        cart_id: $cartId
        payment_method: {
          code: "stripe_payments"
          stripe_payments: {
            payment_element: $usePaymentElement
            payment_method: $paymentMethod
            save_payment_method: $save
          }
        }
      }
    ) {
      cart {
        selected_payment_method {
          code
          title
        }
      }
    }
  }
`;

const APPLY_COUPON = gql`
  mutation ApplyCoupon($cartId: String!, $coupon: String!) {
    applyCouponToCart(input: { cart_id: $cartId, coupon_code: $coupon }) {
      cart {
        applied_coupons {
          code
        }
      }
    }
  }
`;

const REMOVE_COUPON = gql`
  mutation RemoveCoupon($cartId: String!) {
    removeCouponFromCart(input: { cart_id: $cartId }) {
      cart {
        applied_coupons {
          code
        }
      }
    }
  }
`;





// Klarna – schema varies by module. These are placeholders that match many setups.
// If yours differs, adjust to your module mutations.
const KLARNA_CREATE_SESSION = gql`
  mutation KlarnaCreateSession($cartId: String!, $code: String!) {
    klarnaCreateSession(input: { cart_id: $cartId, payment_method: $code }) {
      client_token
      session_id
    }
  }
`;

const KLARNA_SET_ON_CART = gql`
  mutation KlarnaSetOnCart($cartId: String!, $code: String!, $authorizationToken: String!) {
    setPaymentMethodOnCart(
      input: {
        cart_id: $cartId
        payment_method: { code: $code, klarna: { authorization_token: $authorizationToken } }
      }
    ) {
      cart {
        selected_payment_method {
          code
          title
        }
      }
    }
  }
`;



// ----------------------
// Helpers
// ----------------------

function jsonResponse(data, init = {}) {
  const headers = new Headers(init.headers || {});
  if (!headers.has("Content-Type")) headers.set("Content-Type", "application/json");
  return new Response(JSON.stringify(data), { status: init.status || 200, headers });
}

function badRequest(message, init = {}) {
  const { headers, status, ...rest } = init || {};
  return jsonResponse(
    { ok: false, message, ...rest },
    { status: status || 400, headers }
  );
}

function readAuthFromRequest(request) {
  const customerToken = getCookieFromRequest(request, "customerToken") || "";
  const cartId = getCookieFromRequest(request, "cartId") || "";
  return { customerToken, cartId };
}

function setCartCookie(headers, cartId) {
  headers.append(
    "Set-Cookie",
    `cartId=${encodeURIComponent(cartId)}; Path=/; Max-Age=${60 * 60 * 24 * 30}; SameSite=Lax`
  );
}

function clearCartCookie(headers) {
  headers.append("Set-Cookie", "cartId=; Path=/; Max-Age=0; SameSite=Lax");
}

function parseMaybeJson(v) {
  if (v == null) return null;
  if (typeof v === "object") return v;
  if (typeof v === "string") {
    try {
      return JSON.parse(v);
    } catch {
      return null;
    }
  }
  return null;
}

function buildRegion(address) {
  if (!address) return undefined;
  // Accept several shapes; Magento usually accepts one of: region_id, region_code, region
  if (address?.region_id) return { region_id: Number(address.region_id) };
  if (address?.regionId) return { region_id: Number(address.regionId) };
  if (address?.region_code) return { region_code: String(address.region_code) };
  if (address?.regionCode) return { region_code: String(address.regionCode) };
  if (address?.region?.code) return { region_code: String(address.region.code) };
  if (address?.region?.region_code) return { region_code: String(address.region.region_code) };
  if (address?.region) return { region: String(address.region) };
  return undefined;
}

function normalizeStreet(street) {
  if (Array.isArray(street)) return street.map((s) => String(s));
  if (typeof street === "string") return [street];
  return [];
}

function toShippingAddressInput(address) {
  const countryCode = address?.country_code || address?.countryCode || address?.country?.code || address?.country;
  return {
    address: {
      firstname: String(address.firstname || ""),
      lastname: String(address.lastname || ""),
      company: address.company ? String(address.company) : undefined,
      street: normalizeStreet(address.street),
      city: String(address.city || ""),
      region: buildRegion(address),
      postcode: String(address.postcode || ""),
      country_code: String(countryCode || ""),
      telephone: String(address.telephone || ""),
      save_in_address_book: Boolean(address.save_in_address_book ?? false),
    },
  };
}

function toBillingAddressInput(address, sameAsShipping = false) {
  if (sameAsShipping) {
    return { same_as_shipping: true };
  }

  const countryCode = address?.country_code || address?.countryCode || address?.country?.code || address?.country;

  return {
    address: {
      firstname: String(address.firstname || ""),
      lastname: String(address.lastname || ""),
      company: address.company ? String(address.company) : undefined,
      street: normalizeStreet(address.street),
      city: String(address.city || ""),
      region: buildRegion(address),
      postcode: String(address.postcode || ""),
      country_code: String(countryCode || ""),
      telephone: String(address.telephone || ""),
      save_in_address_book: Boolean(address.save_in_address_book ?? false),
    },
  };
}

async function ensureCart({ apollo, customerToken, cartId }) {
  if (customerToken) {
    const { data } = await apollo.query({ query: GET_CUSTOMER_CART_ID, fetchPolicy: "no-cache" });
    const customerCartId = data?.customerCart?.id;
    if (!customerCartId) throw new Error("Unable to load customer cart");

    if (cartId && cartId !== customerCartId) {
      const merged = await apollo.mutate({
        mutation: MERGE_CARTS,
        variables: { source: cartId, destination: customerCartId },
        fetchPolicy: "no-cache",
      });
      return merged?.data?.mergeCarts?.id || customerCartId;
    }
    return customerCartId;
  }

  if (cartId) return cartId;

  const created = await apollo.mutate({ mutation: CREATE_EMPTY_CART, fetchPolicy: "no-cache" });
  const newId = created?.data?.createEmptyCart;
  if (!newId) throw new Error("Failed to create guest cart");
  return newId;
}

function extractOrderNo(res) {
  return (
    res?.data?.placeOrder?.order?.order_number ||
    res?.data?.placeOrder?.order_number ||
    res?.data?.placeOrder?.order?.increment_id ||
    res?.data?.placeOrder?.increment_id ||
    ""
  );
}

function extractClientSecret(res) {
  return res?.data?.placeOrder?.order?.client_secret || res?.data?.placeOrder?.client_secret || "";
}

// Try to pull a PayPal start URL from multiple possible shapes
function extractPaypalStartUrl(res) {
  return (
    res?.data?.createPaypalExpressToken?.paypal_urls?.start ||
    res?.data?.createPaypalExpressToken?.paypal_urls?.[0]?.start ||
    res?.data?.createPaypalExpressToken?.paypal_urls?.[0]?.url ||
    res?.data?.createPaypalExpressToken?.paypal_urls?.start_url ||
    ""
  );
}

// ----------------------
// Route handler
// ----------------------

export async function action({ request }) {
  if (request.method !== "POST") {
    return jsonResponse({ ok: false, message: "Method not allowed" }, { status: 405 });
  }

  let payload = null;
  try {
    payload = await request.json();
  } catch {
    return badRequest("Invalid JSON body");
  }

  const intent = payload?.intent;
  if (!intent) return badRequest("Missing intent");

  const { customerToken, cartId: cartIdFromCookie } = readAuthFromRequest(request);

  const apollo = createClient({
    ssrMode: true,
    request,
    token: customerToken || undefined,
  });

  const headers = new Headers();

  try {
    const effectiveCartId = await ensureCart({
      apollo,
      customerToken,
      cartId: cartIdFromCookie,
    });

    if (effectiveCartId && effectiveCartId !== cartIdFromCookie) {
      setCartCookie(headers, effectiveCartId);
    }

    // ----------------------
    // INTENTS
    // ----------------------

    if (intent === "ensure-cart") {
      return jsonResponse({ ok: true, cartId: effectiveCartId }, { headers });
    }

    if (intent === "set-guest-email") {
      if (customerToken) return jsonResponse({ ok: true }, { headers }); // no-op when logged in
      const email = String(payload?.email || "").trim();
      if (!email) return badRequest("Missing email", { headers });

      await apollo.mutate({
        mutation: SET_GUEST_EMAIL_ON_CART,
        variables: { cartId: effectiveCartId, email },
        fetchPolicy: "no-cache",
      });

      return jsonResponse({ ok: true }, { headers });
    }

    // Accept both "set-address" and "set-addresses"
    if (intent === "set-address" || intent === "set-addresses") {
      const address = parseMaybeJson(payload?.address);
      if (!address) return badRequest("Missing address", { headers });

      // If guest checkout, Magento requires email on cart
      if (!customerToken) {
        const email = String(payload?.email || address?.email || "").trim();
        if (!email) return badRequest("Missing email for guest checkout", { headers });

        await apollo.mutate({
          mutation: SET_GUEST_EMAIL_ON_CART,
          variables: { cartId: effectiveCartId, email },
          fetchPolicy: "no-cache",
        });
      }

      await apollo.mutate({
        mutation: SET_SHIPPING_ADDRESSES_ON_CART,
        variables: { cartId: effectiveCartId, addresses: [toShippingAddressInput(address)] },
        fetchPolicy: "no-cache",
      });

      // Billing: default to same as shipping unless explicitly disabled
      const setBilling = payload?.setBilling === false ? false : true;
      if (setBilling) {
        await apollo.mutate({
            mutation: SET_BILLING_ADDRESS_ON_CART,
            variables: {
                cartId: effectiveCartId,
                billing: toBillingAddressInput(address, false), // or true if you want same_as_shipping
            },
            fetchPolicy: "no-cache",
            });
      }

      return jsonResponse({ ok: true }, { headers });
    }

    if (intent === "set-shipping-method") {
      const carrier = String(payload?.carrier || "").trim();
      const method = String(payload?.method || "").trim();
      if (!carrier || !method) return badRequest("Missing carrier/method", { headers });

      await apollo.mutate({
        mutation: SET_SHIPPING_METHOD_ON_CART,
        variables: { cartId: effectiveCartId, carrier, method },
        fetchPolicy: "no-cache",
      });

      return jsonResponse({ ok: true }, { headers });
    }

    if (intent === "set-payment-basic") {
      const code = String(payload?.code || "").trim();
      if (!code) return badRequest("Missing payment code", { headers });

      await apollo.mutate({
        mutation: SET_PAYMENT_METHOD_ON_CART,
        variables: { cartId: effectiveCartId, code },
        fetchPolicy: "no-cache",
      });

      return jsonResponse({ ok: true }, { headers });
    }

    if (intent === "stripe-set-pm") {
      const paymentMethod = String(payload?.paymentMethod || "");
      const save = Boolean(payload?.save);
      const usePaymentElement = Boolean(payload?.usePaymentElement);

      if (!paymentMethod) return badRequest("Missing paymentMethod", { headers });

      await apollo.mutate({
        mutation: SET_STRIPE_PAYMENT_METHOD_ON_CART,
        variables: { cartId: effectiveCartId, paymentMethod, save, usePaymentElement },
        fetchPolicy: "no-cache",
      });

      return jsonResponse({ ok: true }, { headers });
    }

    // PayPal token creation (start URL)
    if (intent === "paypal-create-token") {
      const returnUrl = String(payload?.returnUrl || "").trim();
      const cancelUrl = String(payload?.cancelUrl || "").trim();
      if (!returnUrl || !cancelUrl) return badRequest("Missing returnUrl/cancelUrl", { headers });

      // 1) Read checkout state to validate PayPal eligibility
      const snap = await apollo.query({
        query: GET_CHECKOUT,
        variables: { cartId: effectiveCartId },
        fetchPolicy: "no-cache",
      });

      const cart = snap?.data?.cart;
      const available = cart?.available_payment_methods?.map((m) => m.code) || [];
      const selectedShip = cart?.shipping_addresses?.[0]?.selected_shipping_method;

      const missing = [];
      if (!cart?.total_quantity) missing.push("cart has no items");
      if (!cart?.email && !customerToken) missing.push("guest email not set");
      if (!selectedShip?.carrier_code || !selectedShip?.method_code) missing.push("shipping method not set");
      if (!available.includes("paypal_express")) missing.push("paypal_express not available for this cart");

      if (missing.length) {
        return badRequest(`PayPal cannot start: ${missing.join(", ")}`, {
          headers,
          debug: { cartId: effectiveCartId, available_payment_methods: available },
        });
      }

      // 2) Ensure payment method set (some setups require this even though token mutation includes code)
      await apollo.mutate({
        mutation: SET_PAYMENT_METHOD_BASIC,
        variables: { cartId: effectiveCartId, code: "paypal_express" },
        fetchPolicy: "no-cache",
      });

      // 3) Create token
      const res = await apollo.mutate({
        mutation: CREATE_PAYPAL_EXPRESS_TOKEN,
        variables: { cartId: effectiveCartId, returnUrl, cancelUrl },
        fetchPolicy: "no-cache",
        errorPolicy: "all",
      });

      const token = res?.data?.createPaypalExpressToken?.token || "";
      const startUrl = res?.data?.createPaypalExpressToken?.paypal_urls?.start || "";

      if (!startUrl) {
        const gqlErrors = (res?.errors || []).map((e) => e?.message).filter(Boolean);
        return badRequest("PayPal start URL missing", {
          headers,
          debug: { errors: gqlErrors, data: res?.data, cartId: effectiveCartId },
        });
      }

      return jsonResponse({ ok: true, startUrl, token }, { headers });
    }

    // PayPal apply auth params onto cart
   if (intent === "paypal-set-on-cart") {
      const token = String(payload?.token || payload?.TOKEN || "").trim();
      const payerId = String(payload?.payerId || payload?.PayerID || "").trim();
      if (!token || !payerId) return badRequest("Missing token/payerId", { headers });

      const res = await apollo.mutate({
        mutation: SET_PAYPAL_ON_CART,
        variables: { cartId: effectiveCartId, payerId, token },
        fetchPolicy: "no-cache",
        errorPolicy: "all",
      });

      const gqlErrors = (res?.errors || []).map((e) => e?.message).filter(Boolean);
      if (gqlErrors.length) {
        return badRequest("Failed to apply PayPal payment to cart", {
          headers,
          debug: { cartId: effectiveCartId, errors: gqlErrors, data: res?.data ?? null },
        });
      }

      return jsonResponse({ ok: true }, { headers });
    }

    // Klarna: create session for widget
    if (intent === "klarna-create-session") {
      const code = String(payload?.code || payload?.paymentMethod || payload?.method || "klarna_payments").trim();
      const res = await apollo.mutate({
        mutation: KLARNA_CREATE_SESSION,
        variables: { cartId: effectiveCartId, code },
        fetchPolicy: "no-cache",
      });

      const clientToken = res?.data?.klarnaCreateSession?.client_token || "";
      const sessionId = res?.data?.klarnaCreateSession?.session_id || "";

      // Not all modules return these; still return ok to allow UI to handle
      return jsonResponse({ ok: true, clientToken, sessionId }, { headers });
    }

    // Klarna: set authorized token onto cart
    if (intent === "klarna-set-on-cart") {
      const code = String(payload?.code || "").trim();
      const authorizationToken = String(payload?.authorizationToken || "").trim();
      if (!code || !authorizationToken) return badRequest("Missing code/authorizationToken", { headers });

      await apollo.mutate({
        mutation: KLARNA_SET_ON_CART,
        variables: { cartId: effectiveCartId, code, authorizationToken },
        fetchPolicy: "no-cache",
      });

      return jsonResponse({ ok: true }, { headers });
    }

    if (intent === "apply-coupon") {
      const coupon = String(payload?.coupon || "").trim();
      if (!coupon) return badRequest("Missing coupon", { headers });

      await apollo.mutate({
        mutation: APPLY_COUPON,
        variables: { cartId: effectiveCartId, coupon },
        fetchPolicy: "no-cache",
      });

      return jsonResponse({ ok: true }, { headers });
    }

    if (intent === "remove-coupon") {
      await apollo.mutate({
        mutation: REMOVE_COUPON,
        variables: { cartId: effectiveCartId },
        fetchPolicy: "no-cache",
      });

      return jsonResponse({ ok: true }, { headers });
    }

    if (intent === "place-order-stripe") {
      const res = await apollo.mutate({
        mutation: PLACE_ORDER_STRIPE,
        variables: { cartId: effectiveCartId },
        fetchPolicy: "no-cache",
        errorPolicy: "all", // ✅ IMPORTANT: allows res.errors + partial data
      });

      const orderNo = res?.data?.placeOrder?.order?.order_number || "";
      const clientSecret = res?.data?.placeOrder?.order?.client_secret || "";

      if (!orderNo) {
        // ✅ Return the real reason (Magento usually tells you exactly what's missing)
        const gqlErrors =
          (res?.errors || []).map((e) => e?.message).filter(Boolean);

        return badRequest("Order placement did not return an order number", {
          headers,
          // TEMP debugging payload (remove later)
          debug: {
            cartId: effectiveCartId,
            data: res?.data ?? null,
            errors: gqlErrors,
          },
        });
      }

      clearCartCookie(headers);

      return jsonResponse({ ok: true, orderNo, clientSecret }, { headers });
    }

    if (intent === "place-order") {
      const res = await apollo.mutate({
        mutation: PLACE_ORDER,
        variables: { cartId: effectiveCartId },
        fetchPolicy: "no-cache",
      });

      const orderNo = extractOrderNo(res);
      if (!orderNo) return badRequest("Order placement did not return an order number", { headers });

      clearCartCookie(headers);

      return jsonResponse({ ok: true, orderNo }, { headers });
    }

    return badRequest(`Unknown intent: ${intent}`, { headers });
  } catch (e) {
    const msg = e?.message || "Checkout API error";
    return badRequest(msg, { headers });
  }
}
