import { getShopWithDecryptedToken, getValidAdminAccessToken } from "../../services/shop.server";
import { ShopifyAPIError, TenantAccessDeniedError } from "../security/errors";
import { logger } from "../security/logger";
import prisma from "../../db.server";

export interface AdminClientConfig {
  shopifyDomain: string;
}

export class ShopifyAdminClient {
  private shopifyDomain: string;

  constructor(config: AdminClientConfig) {
    if (!config.shopifyDomain) {
      throw new TenantAccessDeniedError("shopifyDomain is required for ShopifyAdminClient");
    }
    this.shopifyDomain = config.shopifyDomain.toLowerCase().trim();
  }

  private async getAccessToken(): Promise<string> {
    const validToken = await getValidAdminAccessToken(this.shopifyDomain);
    if (!validToken) {
      const shop = await getShopWithDecryptedToken(this.shopifyDomain);
      if (!shop || shop.uninstalledAt || !shop.decryptedAccessToken) {
        throw new TenantAccessDeniedError(`No active decrypted access token for shop ${this.shopifyDomain}`);
      }
      return shop.decryptedAccessToken;
    }
    return validToken;
  }

  /**
   * Executes a typed GraphQL query/mutation against Shopify Admin GraphQL API.
   */
  async graphql<T = any>(query: string, variables: Record<string, any> = {}): Promise<T> {
    const accessToken = await this.getAccessToken();
    const endpoint = `https://${this.shopifyDomain}/admin/api/2025-01/graphql.json`;

    try {
      const response = await fetch(endpoint, {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          "X-Shopify-Access-Token": accessToken,
        },
        body: JSON.stringify({ query, variables }),
      });

      if (!response.ok) {
        const errText = await response.text();
        logger.error(`Shopify GraphQL HTTP ${response.status} error`, { shop: this.shopifyDomain, status: response.status, body: errText });

        // If 401 Unauthorized or non-expiring token deprecation error, force refresh token and retry once
        if (response.status === 401 || (response.status === 403 && errText.includes("Non-expiring"))) {
          logger.warn(`[ShopifyAdminClient] Token invalid/expired (${response.status}) for ${this.shopifyDomain}. Forcing refresh and retrying...`);
          try {
            const freshToken = await getValidAdminAccessToken(this.shopifyDomain, true);
            if (freshToken && freshToken !== accessToken && freshToken !== "shpss_sample_token") {
              const retryRes = await fetch(endpoint, {
                method: "POST",
                headers: {
                  "Content-Type": "application/json",
                  "X-Shopify-Access-Token": freshToken,
                },
                body: JSON.stringify({ query, variables }),
              });
              if (retryRes.ok) {
                const retryJson = await retryRes.json();
                if (!retryJson.errors || retryJson.errors.length === 0 || (retryJson.data && Object.keys(retryJson.data).length > 0)) {
                  return retryJson.data as T;
                }
              }
            }
          } catch (retryErr: any) {
            logger.warn(`[ShopifyAdminClient] Token refresh retry failed: ${retryErr.message}`);
          }
        }

        throw new ShopifyAPIError(`Shopify Admin API HTTP ${response.status}`, response.status, { body: errText });
      }

      const json = await response.json();
      if (json.errors && json.errors.length > 0) {
        // If Shopify returned partial data (e.g. some fields succeeded but Protected Customer Data fields returned ACCESS_DENIED), return the available partial data
        if (
          json.data &&
          typeof json.data === "object" &&
          Object.keys(json.data).length > 0 &&
          Object.values(json.data).some((val) => val !== null)
        ) {
          logger.warn("Shopify GraphQL partial response with field errors", {
            shop: this.shopifyDomain,
            errors: json.errors,
          });
          return json.data as T;
        }

        logger.error("Shopify GraphQL Errors", { shop: this.shopifyDomain, errors: json.errors });
        throw new ShopifyAPIError(json.errors[0].message || "GraphQL Execution Error", 400, {
          errors: json.errors,
        });
      }

      return json.data as T;
    } catch (error: any) {
      if (error instanceof ShopifyAPIError || error instanceof TenantAccessDeniedError) {
        throw error;
      }
      logger.error("Failed to connect to Shopify Admin API", error, { shop: this.shopifyDomain });
      throw new ShopifyAPIError(error.message || "Network error connecting to Shopify API", 500);
    }
  }

  // --- Product Admin API Methods ---

  async getProducts(first = 20): Promise<any> {
    const query = `#graphql
      query getProducts($first: Int!) {
        products(first: $first) {
          edges {
            node {
              id
              title
              handle
              description
              featuredImage { url }
              priceRangeV2 {
                minVariantPrice { amount currencyCode }
                maxVariantPrice { amount currencyCode }
              }
              variants(first: 10) {
                edges {
                  node {
                    id
                    title
                    price
                    inventoryQuantity
                  }
                }
              }
            }
          }
        }
      }
    `;
    return this.graphql(query, { first });
  }

  async searchProducts(searchQuery: string, first = 10): Promise<any> {
    const query = `#graphql
      query searchProducts($query: String!, $first: Int!) {
        products(first: $first, query: $query) {
          edges {
            node {
              id
              title
              handle
              description
              featuredImage { url }
              priceRangeV2 {
                minVariantPrice { amount currencyCode }
                maxVariantPrice { amount currencyCode }
              }
              variants(first: 5) {
                edges {
                  node {
                    id
                    title
                    price
                    inventoryQuantity
                  }
                }
              }
            }
          }
        }
      }
    `;
    return this.graphql(query, { query: searchQuery, first });
  }

  // --- Customer Admin API Methods ---

  async createCustomer(data: {
    firstName?: string;
    lastName?: string;
    email: string;
    phone?: string;
    note?: string;
  }): Promise<any> {
    // 1. Try GraphQL Customer Creation
    try {
      const mutation = `#graphql
        mutation customerCreate($input: CustomerInput!) {
          customerCreate(input: $input) {
            customer {
              id
              firstName
              lastName
              email
              phone
              numberOfOrders
            }
            userErrors {
              field
              message
            }
          }
        }
      `;
      const input: Record<string, any> = {
        email: data.email,
      };
      if (data.firstName) input.firstName = data.firstName;
      if (data.lastName) input.lastName = data.lastName;
      if (data.phone) input.phone = data.phone;
      if (data.note) input.note = data.note;

      const res = await this.graphql(mutation, { input });
      if (res?.customerCreate?.customer?.id) {
        return res;
      }
    } catch (gqlErr) {
      logger.warn("GraphQL customerCreate failed, attempting REST API fallback", { error: gqlErr });
    }

    // 2. Try REST API Customer Creation
    try {
      const accessToken = await this.getAccessToken();
      const endpoint = `https://${this.shopifyDomain}/admin/api/2025-01/customers.json`;
      const response = await fetch(endpoint, {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          "X-Shopify-Access-Token": accessToken,
        },
        body: JSON.stringify({
          customer: {
            first_name: data.firstName || "",
            last_name: data.lastName || "",
            email: data.email,
            phone: data.phone || undefined,
            note: data.note || "Created via ShopPilot AI Chatbot",
            verified_email: true,
            send_email_welcome: false,
          },
        }),
      });

      if (response.ok) {
        const json = await response.json();
        if (json?.customer?.id) {
          return {
            customerCreate: {
              customer: {
                id: `gid://shopify/Customer/${json.customer.id}`,
                firstName: json.customer.first_name,
                lastName: json.customer.last_name,
                email: json.customer.email,
                phone: json.customer.phone,
                numberOfOrders: json.customer.orders_count || 0,
              },
              userErrors: [],
            },
          };
        }
      }
    } catch (restErr) {
      logger.error("REST customer creation error", restErr);
    }

    return null;
  }

  async findCustomerByEmail(email: string): Promise<any> {
    if (!email) return { customers: { edges: [] } };
    const cleanEmail = email.trim().toLowerCase();

    // 1. Try REST API Customer Search first (handles all email formats, special chars, and PCD scopes reliably)
    try {
      const accessToken = await this.getAccessToken();
      const restUrls = [
        `https://${this.shopifyDomain}/admin/api/2025-01/customers/search.json?query=email:${encodeURIComponent(cleanEmail)}`,
        `https://${this.shopifyDomain}/admin/api/2025-01/customers/search.json?query=${encodeURIComponent(cleanEmail)}`,
      ];

      for (const endpoint of restUrls) {
        try {
          const res = await fetch(endpoint, {
            method: "GET",
            headers: {
              "Content-Type": "application/json",
              "X-Shopify-Access-Token": accessToken,
            },
          });

          if (res.ok) {
            const json = await res.json();
            const customers = json.customers || [];
            if (customers.length > 0) {
              const matched = customers.find((c: any) => (c.email || "").toLowerCase().trim() === cleanEmail) || customers[0];
              if (matched && (matched.email || "").toLowerCase().trim() === cleanEmail) {
                // Fetch customer's orders via REST
                let custOrders: any[] = [];
                try {
                  const ordersRes = await fetch(
                    `https://${this.shopifyDomain}/admin/api/2025-01/customers/${matched.id}/orders.json?status=any&limit=10`,
                    {
                      headers: {
                        "Content-Type": "application/json",
                        "X-Shopify-Access-Token": accessToken,
                      },
                    }
                  );
                  if (ordersRes.ok) {
                    const ordJson = await ordersRes.json();
                    custOrders = (ordJson.orders || []).map((o: any) => ({
                      node: {
                        id: `gid://shopify/Order/${o.id}`,
                        name: o.name || `#${o.order_number || o.id}`,
                        orderNumber: String(o.order_number || o.id),
                        processedAt: o.processed_at,
                        financialStatus: o.financial_status,
                        fulfillmentStatus: o.fulfillment_status,
                      },
                    }));
                  }
                } catch (ordErr) {}

                return {
                  customers: {
                    edges: [
                      {
                        node: {
                          id: `gid://shopify/Customer/${matched.id}`,
                          firstName: matched.first_name,
                          lastName: matched.last_name,
                          email: matched.email,
                          phone: matched.phone,
                          numberOfOrders: matched.orders_count || 0,
                          defaultAddress: matched.default_address
                            ? {
                                zip: matched.default_address.zip,
                                city: matched.default_address.city,
                                province: matched.default_address.province,
                                country: matched.default_address.country,
                              }
                            : null,
                          orders: { edges: custOrders },
                        },
                      },
                    ],
                  },
                };
              }
            }
          }
        } catch (rErr) {}
      }
    } catch (restErr) {
      logger.warn("REST findCustomerByEmail failed, trying GraphQL", { error: restErr });
    }

    // 2. Try GraphQL Customer Search Fallback
    const searchQueries = [
      `email:"${cleanEmail}"`,
      `email:${cleanEmail}`,
      cleanEmail,
    ];

    for (const q of searchQueries) {
      try {
        const query = `#graphql
          query findCustomerByEmail($query: String!) {
            customers(first: 5, query: $query) {
              edges {
                node {
                  id
                  firstName
                  lastName
                  email
                  phone
                  numberOfOrders
                  defaultAddress {
                    zip
                    city
                    province
                    country
                  }
                }
              }
            }
          }
        `;
        const res = await this.graphql(query, { query: q });
        const edges = res?.customers?.edges || [];
        if (edges.length > 0) {
          const matched = edges.find((e: any) => (e.node?.email || "").toLowerCase().trim() === cleanEmail);
          if (matched) {
            return {
              customers: {
                edges: [matched],
              },
            };
          }
        }
      } catch (gqlErr) {}
    }

    return { customers: { edges: [] } };
  }

  async getCustomer(customerId: string): Promise<any> {
    if (!customerId) return null;
    const cleanId = customerId.startsWith("gid://shopify/Customer/")
      ? customerId
      : `gid://shopify/Customer/${customerId}`;
    const numericId = customerId.split("/").pop() || "";

    const query = `#graphql
      query getCustomer($id: ID!) {
        customer(id: $id) {
          id
          firstName
          lastName
          email
          phone
          numberOfOrders
          defaultAddress {
            zip
            city
            province
            country
          }
        }
      }
    `;
    try {
      const res = await this.graphql(query, { id: cleanId });
      if (res?.customer) return res;
    } catch (gqlErr) {
      logger.warn("GraphQL getCustomer with defaultAddress failed, attempting basic query without address", { error: gqlErr });
      // Fallback query without defaultAddress (avoids ACCESS_DENIED when store lacks Address PCD access)
      try {
        const basicQuery = `#graphql
          query getCustomerBasic($id: ID!) {
            customer(id: $id) {
              id
              firstName
              lastName
              email
              phone
              numberOfOrders
            }
          }
        `;
        const basicRes = await this.graphql(basicQuery, { id: cleanId });
        if (basicRes?.customer) {
          return {
            customer: {
              ...basicRes.customer,
              defaultAddress: null,
            },
          };
        }
      } catch (basicErr) {
        // Fallback to minimal query if personal info scopes are restricted
        try {
          const minimalQuery = `#graphql
            query getCustomerMinimal($id: ID!) {
              customer(id: $id) {
                id
                numberOfOrders
              }
            }
          `;
          const minRes = await this.graphql(minimalQuery, { id: cleanId });
          if (minRes?.customer) {
            return {
              customer: {
                ...minRes.customer,
                firstName: null,
                lastName: null,
                email: null,
                phone: null,
                defaultAddress: null,
              },
            };
          }
        } catch {}
      }
    }

    if (numericId) {
      try {
        const accessToken = await this.getAccessToken();
        const endpoint = `https://${this.shopifyDomain}/admin/api/2025-01/customers/${numericId}.json`;
        const response = await fetch(endpoint, {
          headers: {
            "Content-Type": "application/json",
            "X-Shopify-Access-Token": accessToken,
          },
        });
        if (response.ok) {
          const json = await response.json();
          if (json.customer) {
            return {
              customer: {
                id: `gid://shopify/Customer/${json.customer.id}`,
                firstName: json.customer.first_name,
                lastName: json.customer.last_name,
                email: json.customer.email,
                phone: json.customer.phone,
                numberOfOrders: json.customer.orders_count || 0,
                defaultAddress: json.customer.default_address
                  ? {
                      zip: json.customer.default_address.zip,
                      city: json.customer.default_address.city,
                      province: json.customer.default_address.province,
                      country: json.customer.default_address.country,
                    }
                  : null,
              },
            };
          }
        }
      } catch (restErr) {}
    }

    return null;
  }

  async getCustomerOrders(customerId: string): Promise<any[]> {
    if (!customerId) return [];
    const numericId = customerId.split("/").pop() || "";
    if (!numericId) return [];

    // 1. Try REST API customer orders (most reliable)
    try {
      const accessToken = await this.getAccessToken();
      const endpoint = `https://${this.shopifyDomain}/admin/api/2025-01/customers/${numericId}/orders.json?status=any&limit=20`;
      const res = await fetch(endpoint, {
        headers: {
          "Content-Type": "application/json",
          "X-Shopify-Access-Token": accessToken,
        },
      });
      if (res.ok) {
        const json = await res.json();
        const orders = json.orders || [];
        if (orders.length > 0) {
          return orders.map((o: any) => ({ node: this.normalizeRestOrder(o) }));
        }
      }
    } catch (restErr) {}

    // 2. Try GraphQL search by customer ID
    try {
      const query = `#graphql
        query getCustomerOrders($query: String!) {
          orders(first: 10, query: $query) {
            edges {
              node {
                id
                name
                createdAt
                displayFinancialStatus
                displayFulfillmentStatus
                email
                totalPriceSet { presentmentMoney { amount currencyCode } }
                customer {
                  id
                  email
                  firstName
                  lastName
                }
              }
            }
          }
        }
      `;
      const res = await this.graphql(query, { query: `customer_id:${numericId}` });
      const edges = res?.orders?.edges || [];
      if (edges.length > 0) {
        return edges;
      }
    } catch (gqlErr) {}

    return [];
  }

  // --- Order Admin API Methods ---

  private normalizeRestOrder(rawOrder: any): any {
    if (!rawOrder) return null;
    const email = rawOrder.email || rawOrder.contact_email || rawOrder.customer?.email || rawOrder.customer?.contact_email || "";
    const phone = rawOrder.phone || rawOrder.customer?.phone || rawOrder.shipping_address?.phone || rawOrder.billing_address?.phone || "";

    const shippingAddr = rawOrder.shipping_address || rawOrder.billing_address || null;
    const billingAddr = rawOrder.billing_address || rawOrder.shipping_address || null;

    return {
      id: rawOrder.admin_graphql_api_id || `gid://shopify/Order/${rawOrder.id}`,
      name: rawOrder.name || `#${rawOrder.order_number || rawOrder.id}`,
      createdAt: rawOrder.created_at,
      processedAt: rawOrder.processed_at || rawOrder.created_at,
      cancelledAt: rawOrder.cancelled_at,
      cancelReason: rawOrder.cancel_reason,
      displayFinancialStatus: rawOrder.financial_status ? rawOrder.financial_status.toUpperCase() : "PAID",
      displayFulfillmentStatus: rawOrder.fulfillment_status ? rawOrder.fulfillment_status.toUpperCase() : "UNFULFILLED",
      email,
      contactEmail: rawOrder.contact_email || email,
      phone,
      totalPriceSet: {
        presentmentMoney: {
          amount: String(rawOrder.total_price || "0.00"),
          currencyCode: rawOrder.currency || "USD",
        },
        shopMoney: {
          amount: String(rawOrder.total_price || "0.00"),
          currencyCode: rawOrder.currency || "USD",
        },
      },
      customer: rawOrder.customer
        ? {
            id: rawOrder.customer.admin_graphql_api_id || `gid://shopify/Customer/${rawOrder.customer.id}`,
            email: rawOrder.customer.email || email,
            phone: rawOrder.customer.phone || phone,
            firstName: rawOrder.customer.first_name || "",
            lastName: rawOrder.customer.last_name || "",
            displayName: `${rawOrder.customer.first_name || ""} ${rawOrder.customer.last_name || ""}`.trim(),
          }
        : null,
      shippingAddress: shippingAddr
        ? {
            name: shippingAddr.name || `${shippingAddr.first_name || ""} ${shippingAddr.last_name || ""}`.trim(),
            address1: shippingAddr.address1 || "",
            address2: shippingAddr.address2 || "",
            city: shippingAddr.city || "",
            province: shippingAddr.province || "",
            provinceCode: shippingAddr.province_code || "",
            country: shippingAddr.country || "",
            countryCodeV2: shippingAddr.country_code || "",
            zip: shippingAddr.zip || "",
            phone: shippingAddr.phone || "",
          }
        : null,
      billingAddress: billingAddr
        ? {
            name: billingAddr.name || `${billingAddr.first_name || ""} ${billingAddr.last_name || ""}`.trim(),
            address1: billingAddr.address1 || "",
            address2: billingAddr.address2 || "",
            city: billingAddr.city || "",
            province: billingAddr.province || "",
            provinceCode: billingAddr.province_code || "",
            country: billingAddr.country || "",
            countryCodeV2: billingAddr.country_code || "",
            zip: billingAddr.zip || "",
            phone: billingAddr.phone || "",
          }
        : null,
      fulfillments: (rawOrder.fulfillments || []).map((f: any) => {
        const trackingNumbers = f.tracking_numbers && f.tracking_numbers.length > 0 ? f.tracking_numbers : (f.tracking_number ? [f.tracking_number] : []);
        const trackingUrls = f.tracking_urls && f.tracking_urls.length > 0 ? f.tracking_urls : (f.tracking_url ? [f.tracking_url] : []);
        const trackingCompany = f.tracking_company || "";
        const trackingInfo = trackingNumbers.map((num: string, idx: number) => ({
          number: num,
          company: trackingCompany,
          url: trackingUrls[idx] || (trackingUrls[0] ?? ""),
        }));
        return {
          id: f.admin_graphql_api_id || `gid://shopify/Fulfillment/${f.id}`,
          createdAt: f.created_at,
          status: f.status,
          displayStatus: f.status ? f.status.toUpperCase() : "FULFILLED",
          trackingInfo,
        };
      }),
      lineItems: {
        edges: (rawOrder.line_items || []).map((li: any) => ({
          node: {
            id: li.admin_graphql_api_id || `gid://shopify/LineItem/${li.id}`,
            title: li.title || li.name || "Item",
            quantity: li.quantity || 1,
            variantTitle: li.variant_title || "",
            sku: li.sku || "",
            originalUnitPriceSet: {
              presentmentMoney: {
                amount: String(li.price || "0.00"),
                currencyCode: rawOrder.currency || "USD",
              },
            },
          },
        })),
      },
    };
  }

  async getOrders(first = 10): Promise<any> {
    const query = `#graphql
      query getOrders($first: Int!) {
        orders(first: $first, sortKey: CREATED_AT, reverse: true) {
          edges {
            node {
              id
              name
              createdAt
              displayFinancialStatus
              displayFulfillmentStatus
              email
              phone
              totalPriceSet { presentmentMoney { amount currencyCode } }
              fulfillments {
                id
                status
                displayStatus
                trackingInfo {
                  company
                  number
                  url
                }
              }
              lineItems(first: 5) {
                edges {
                  node {
                    title
                    quantity
                    originalUnitPriceSet { presentmentMoney { amount currencyCode } }
                  }
                }
              }
            }
          }
        }
      }
    `;
    try {
      return await this.graphql(query, { first });
    } catch (err) {
      logger.warn("GraphQL getOrders failed, attempting REST fallback", { error: err });
      try {
        const accessToken = await this.getAccessToken();
        const endpoint = `https://${this.shopifyDomain}/admin/api/2025-01/orders.json?status=any&limit=${first}`;
        const res = await fetch(endpoint, {
          headers: {
            "Content-Type": "application/json",
            "X-Shopify-Access-Token": accessToken,
          },
        });
        if (res.ok) {
          const json = await res.json();
          const orders = json.orders || [];
          return {
            orders: {
              edges: orders.map((o: any) => ({ node: this.normalizeRestOrder(o) })),
            },
          };
        }
      } catch (restErr) {
        logger.error("REST getOrders fallback failed", restErr);
      }
      return { orders: { edges: [] } };
    }
  }

  async findOrderByNumber(orderIdentifier: string): Promise<any> {
    if (!orderIdentifier) return null;
    const cleanOrder = String(orderIdentifier).trim().replace(/^#/, "");
    if (!cleanOrder) return null;

    // 1. Try REST API lookup (contains complete email & contact fields on all Shopify stores)
    try {
      const accessToken = await this.getAccessToken();
      const restUrls = [
        `https://${this.shopifyDomain}/admin/api/2025-01/orders.json?name=%23${encodeURIComponent(cleanOrder)}&status=any`,
        `https://${this.shopifyDomain}/admin/api/2025-01/orders.json?name=${encodeURIComponent(cleanOrder)}&status=any`,
        `https://${this.shopifyDomain}/admin/api/2025-01/orders.json?status=any&limit=50`,
      ];

      for (const endpoint of restUrls) {
        try {
          const response = await fetch(endpoint, {
            method: "GET",
            headers: {
              "Content-Type": "application/json",
              "X-Shopify-Access-Token": accessToken,
            },
          });

          if (response.ok) {
            const json = await response.json();
            const rawOrders = json.orders || (json.order ? [json.order] : []);
            if (rawOrders.length > 0) {
              const matched = rawOrders.find((o: any) => {
                const oName = String(o.name || "").replace(/^#/, "").trim().toLowerCase();
                const oNum = String(o.order_number || "").trim().toLowerCase();
                const oId = String(o.id || "").trim().toLowerCase();
                return oName === cleanOrder.toLowerCase() || oNum === cleanOrder.toLowerCase() || oId === cleanOrder.toLowerCase();
              });

              if (matched) {
                return { order: this.normalizeRestOrder(matched) };
              }
            }
          }
        } catch (restFetchErr) {}
      }
    } catch (restErr) {
      logger.warn("REST findOrderByNumber failed, attempting GraphQL fallback", { error: restErr });
    }

    // 2. Try GraphQL Search Fallback
    try {
      const searchGql = `#graphql
        query findOrderByNumber($query: String!) {
          orders(first: 5, query: $query) {
            edges {
              node {
                id
                name
                createdAt
                processedAt
                cancelledAt
                cancelReason
                displayFinancialStatus
                displayFulfillmentStatus
                email
                phone
                totalPriceSet {
                  presentmentMoney { amount currencyCode }
                  shopMoney { amount currencyCode }
                }
                customer {
                  id
                  email
                  phone
                  firstName
                  lastName
                }
                shippingAddress {
                  name
                  address1
                  address2
                  city
                  province
                  provinceCode
                  country
                  countryCodeV2
                  zip
                  phone
                }
                billingAddress {
                  name
                  address1
                  address2
                  city
                  province
                  provinceCode
                  country
                  countryCodeV2
                  zip
                  phone
                }
                fulfillments {
                  id
                  createdAt
                  status
                  displayStatus
                  estimatedDeliveryAt
                  trackingInfo {
                    company
                    number
                    url
                  }
                }
                lineItems(first: 15) {
                  edges {
                    node {
                      id
                      title
                      quantity
                      variantTitle
                      sku
                      originalUnitPriceSet {
                        presentmentMoney { amount currencyCode }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      `;

      const queriesToTry = [
        `name:#${cleanOrder}`,
        `name:${cleanOrder}`,
        cleanOrder,
      ];

      for (const q of queriesToTry) {
        try {
          const res = await this.graphql(searchGql, { query: q });
          const edges = res?.orders?.edges || [];
          if (edges.length > 0) {
            const matched = edges.find((e: any) => {
              const nodeName = String(e.node?.name || "").replace(/^#/, "").trim().toLowerCase();
              const nodeId = String(e.node?.id || "").split("/").pop()?.trim().toLowerCase();
              return nodeName === cleanOrder.toLowerCase() || nodeId === cleanOrder.toLowerCase();
            });
            if (matched?.node) {
              return { order: matched.node };
            }
          }
        } catch (subErr) {}
      }
    } catch (gqlErr) {
      logger.error("GraphQL findOrderByNumber failed", gqlErr);
    }

    return { order: null };
  }

  async getOrder(orderId: string): Promise<any> {
    if (!orderId) return null;

    if (!orderId.startsWith("gid://shopify/Order/")) {
      return this.findOrderByNumber(orderId);
    }

    const query = `#graphql
      query getOrder($id: ID!) {
        order(id: $id) {
          id
          name
          createdAt
          processedAt
          cancelledAt
          cancelReason
          displayFinancialStatus
          displayFulfillmentStatus
          email
          phone
          totalPriceSet { presentmentMoney { amount currencyCode } }
          customer {
            id
            email
            phone
            firstName
            lastName
          }
          shippingAddress {
            name
            address1
            address2
            city
            province
            provinceCode
            country
            countryCodeV2
            zip
            phone
          }
          fulfillments {
            id
            createdAt
            status
            displayStatus
            estimatedDeliveryAt
            trackingInfo {
              company
              number
              url
            }
          }
          lineItems(first: 15) {
            edges {
              node {
                id
                title
                quantity
                variantTitle
                sku
                originalUnitPriceSet { presentmentMoney { amount currencyCode } }
              }
            }
          }
        }
      }
    `;

    try {
      const res = await this.graphql(query, { id: orderId });
      if (res?.order) {
        return res;
      }
    } catch (gqlErr) {
      logger.warn("GraphQL getOrder failed, attempting REST API fallback", { error: gqlErr });
    }

    // Fallback: extract numeric ID and query REST
    const cleanId = orderId.split("/").pop() || "";
    if (cleanId) {
      try {
        const accessToken = await this.getAccessToken();
        const endpoint = `https://${this.shopifyDomain}/admin/api/2025-01/orders/${cleanId}.json`;
        const response = await fetch(endpoint, {
          method: "GET",
          headers: {
            "Content-Type": "application/json",
            "X-Shopify-Access-Token": accessToken,
          },
        });
        if (response.ok) {
          const json = await response.json();
          if (json.order) {
            return { order: this.normalizeRestOrder(json.order) };
          }
        }
      } catch (restErr) {}
    }

    return { order: null };
  }

  /**
   * Safe Order Cancellation GraphQL mutation.
   */
  async cancelOrder(orderId: string, reason = "CUSTOMER", notifyCustomer = true): Promise<any> {
    const mutation = `#graphql
      mutation orderCancel($orderId: ID!, $reason: OrderCancelReason!, $notifyCustomer: Boolean!) {
        orderCancel(orderId: $orderId, reason: $reason, notifyCustomer: $notifyCustomer) {
          job { id status }
          userErrors { field message }
        }
      }
    `;
    return this.graphql(mutation, { orderId, reason, notifyCustomer });
  }

  // --- Content, Shop & Policy Admin API Methods ---

  /**
   * Fetches shop metadata, primary domain, contact emails, and legal policies.
   */
  async getShopDetails(): Promise<any> {
    const query = `#graphql
      query getShopDetails {
        shop {
          name
          email
          contactEmail
          myshopifyDomain
          url
          primaryDomain {
            url
            host
            sslEnabled
          }
        }
      }
    `;
    return this.graphql(query);
  }

  /**
   * Fetches all blogs and articles from the connected Shopify store.
   */
  async getStoreBlogsAndArticles(): Promise<any> {
    const query = `#graphql
      query getStoreBlogsAndArticles {
        shop {
          myshopifyDomain
          primaryDomain {
            url
            host
            sslEnabled
          }
        }
        blogs(first: 50) {
          edges {
            node {
              id
              title
              handle
              articles(first: 100) {
                edges {
                  node {
                    id
                    title
                    handle
                    summary
                    body
                    image {
                      url
                    }
                    author {
                      name
                    }
                    tags
                    publishedAt
                  }
                }
              }
            }
          }
        }
      }
    `;
    return this.graphql(query);
  }

  /**
   * Fetches all online store pages (e.g. Contact Us, About Us, FAQ).
   */
  async getStorePages(): Promise<any> {
    const query = `#graphql
      query getStorePages {
        shop {
          myshopifyDomain
          primaryDomain {
            url
            host
            sslEnabled
          }
        }
        pages(first: 100) {
          edges {
            node {
              id
              title
              handle
              body
              bodySummary
              createdAt
              updatedAt
            }
          }
        }
      }
    `;
    return this.graphql(query);
  }

  /**
   * Fetches active discount codes and automatic discounts configured in Shopify Admin.
   */
  async getActiveDiscounts(first = 25): Promise<any> {
    const query = `#graphql
      query getActiveDiscounts($first: Int!) {
        codeDiscountNodes(first: $first, query: "status:active") {
          edges {
            node {
              id
              codeDiscount {
                ... on DiscountCodeBasic {
                  title
                  status
                  summary
                  startsAt
                  endsAt
                  codes(first: 5) {
                    edges {
                      node {
                        code
                      }
                    }
                  }
                  customerGets {
                    value {
                      ... on DiscountPercentage {
                        percentage
                      }
                      ... on DiscountAmount {
                        amount {
                          amount
                          currencyCode
                        }
                      }
                    }
                  }
                }
                ... on DiscountCodeBxgy {
                  title
                  status
                  summary
                  startsAt
                  endsAt
                  codes(first: 5) {
                    edges {
                      node {
                        code
                      }
                    }
                  }
                }
                ... on DiscountCodeFreeShipping {
                  title
                  status
                  summary
                  startsAt
                  endsAt
                  codes(first: 5) {
                    edges {
                      node {
                        code
                      }
                    }
                  }
                }
                ... on DiscountCodeApp {
                  title
                  status
                  codes(first: 5) {
                    edges {
                      node {
                        code
                      }
                    }
                  }
                }
              }
            }
          }
        }
        automaticDiscountNodes(first: $first, query: "status:active") {
          edges {
            node {
              id
              automaticDiscount {
                ... on DiscountAutomaticBasic {
                  title
                  status
                  summary
                  startsAt
                  endsAt
                }
                ... on DiscountAutomaticBxgy {
                  title
                  status
                  summary
                  startsAt
                  endsAt
                }
                ... on DiscountAutomaticFreeShipping {
                  title
                  status
                  summary
                  startsAt
                  endsAt
                }
                ... on DiscountAutomaticApp {
                  title
                  status
                }
              }
            }
          }
        }
      }
    `;
    return this.graphql(query, { first });
  }
}

