import prisma from "../db.server";
import { encryptToken, decryptToken } from "../lib/security/encryption";

export interface RegisterShopParams {
  shopifyDomain: string;
  shopifyShopId?: string;
  shopifyAppId?: string;
  accessToken: string;
  refreshToken?: string;
  tokenExpiresAt?: Date;
  refreshTokenExpiresAt?: Date;
  tokenUpdatedAt?: Date;
  scopes: string;
  isReinstall?: boolean;
}

/**
 * Registers or updates a multi-tenant Shop record with AES-256 encrypted tokens.
 */
export async function registerShop(params: RegisterShopParams) {
  const normalizedDomain = params.shopifyDomain.toLowerCase().trim();
  const existingShop = await prisma.shop.findUnique({ where: { shopifyDomain: normalizedDomain } });

  // Fast-path for read-only page loads: if the shop already exists, is active, and no new real token is being registered, return immediately
  if (
    existingShop &&
    !existingShop.uninstalledAt &&
    !params.isReinstall &&
    (!params.accessToken || params.accessToken === "shpss_sample_token")
  ) {
    return existingShop;
  }

  const shouldUpdateToken = params.accessToken !== "shpss_sample_token" || !existingShop;
  const encryptedToken = shouldUpdateToken ? encryptToken(params.accessToken) : existingShop.accessToken;
  const encryptedRefreshToken = params.refreshToken ? encryptToken(params.refreshToken) : (existingShop?.refreshToken || null);

  // Automatically activate store on install / reinstall
  const wasUninstalled = Boolean(existingShop?.uninstalledAt);
  const uninstalledAt = null;
  const installedAt = wasUninstalled ? new Date() : (existingShop?.installedAt || new Date());
  const isReinstalling = wasUninstalled || Boolean(params.isReinstall);

  const now = new Date();
  const tokenUpdatedAt = params.tokenUpdatedAt || (shouldUpdateToken ? now : existingShop?.tokenUpdatedAt || now);
  const refreshTokenExpiresAt = params.refreshTokenExpiresAt || existingShop?.refreshTokenExpiresAt;

  const shop = await prisma.shop.upsert({
    where: { shopifyDomain: normalizedDomain },
    update: {
      shopifyShopId: params.shopifyShopId || existingShop?.shopifyShopId,
      shopifyAppId: params.shopifyAppId || existingShop?.shopifyAppId,
      accessToken: encryptedToken,
      refreshToken: encryptedRefreshToken,
      tokenExpiresAt: params.tokenExpiresAt || existingShop?.tokenExpiresAt,
      refreshTokenExpiresAt,
      tokenUpdatedAt,
      scopes: params.scopes,
      uninstalledAt,
      installedAt,
      ...(isReinstalling ? {
        activePlan: "FREE",
        planHandle: "free",
        subscriptionStatus: "ACTIVE",
        cancelAtEndOfCycle: false,
        trialEndsAt: null,
        currentBillingCycleStart: null,
        currentBillingCycleEnd: null,
        lastSubscriptionSync: null,
      } : {}),
      updatedAt: new Date(),
    },
    create: {
      shopifyDomain: normalizedDomain,
      shopifyShopId: params.shopifyShopId,
      shopifyAppId: params.shopifyAppId,
      accessToken: encryptedToken,
      refreshToken: encryptedRefreshToken,
      tokenExpiresAt: params.tokenExpiresAt,
      refreshTokenExpiresAt,
      tokenUpdatedAt,
      scopes: params.scopes,
      activePlan: "FREE",
      planHandle: "free",
      subscriptionStatus: "ACTIVE",
      cancelAtEndOfCycle: false,
      trialEndsAt: null,
      uninstalledAt: null,
      installedAt: new Date(),
    },
  });

  // If reinstalling, expire previous subscriptions to ensure clean state
  if (isReinstalling) {
    await prisma.subscription.updateMany({
      where: { shopId: shop.id, status: "ACTIVE" },
      data: { status: "REPLACED", endedAt: new Date() },
    }).catch(() => null);
  }

  // Ensure default ChatbotSettings record exists and chatbot is enabled
  const existingSettings = await prisma.chatbotSettings.findUnique({ where: { shopId: shop.id } });
  if (existingSettings) {
    if (wasUninstalled || params.isReinstall) {
      // Re-enable chatbot on install / reinstall
      const { parseWidgetSettings, serializeWidgetSettings } = await import("./widget-settings.server");
      const { systemPromptText, widgetTexts } = parseWidgetSettings(existingSettings);
      widgetTexts.adminDisabled = false;
      const serialized = serializeWidgetSettings(systemPromptText, widgetTexts);
      await prisma.chatbotSettings.update({
        where: { shopId: shop.id },
        data: {
          enabled: true,
          systemPrompt: serialized,
        },
      });
    }
  } else {
    await prisma.chatbotSettings.create({
      data: {
        shopId: shop.id,
        botName: "ShopPilot AI Assistant",
        welcomeMessage: "Hi! How can I help you find products, calculate shipping, or track orders today?",
        primaryColor: "#4F46E5",
        secondaryColor: "#4338CA",
        textColor: "#FFFFFF",
        botTextColor: "#1F2937",
        showPoweredBy: true,
        position: "bottom-right",
        enabled: true,
        model: "gpt-4o-mini",
      },
    });
  }

  // Ensure default SystemIntegration record exists in dedicated system_integrations table
  await prisma.systemIntegration.upsert({
    where: { id: "global" },
    update: {},
    create: {
      id: "global",
      aiProvider: "openai",
      openaiModel: "gpt-4o-mini",
      openaiTemperature: 0.7,
      openaiMaxTokens: 1000,
      openaiEnabled: true,
      geminiModel: "gemini-1.5-flash",
      geminiTemperature: 0.7,
      geminiMaxTokens: 1000,
      geminiEnabled: true,
    },
  });

  // Ensure default/active Subscription is authoritatively synced via BillingService
  try {
    const { BillingService } = await import("./billing.server");
    await BillingService.syncSubscription(shop.shopifyDomain);
  } catch (billingErr) {
    // If billing sync encounters an error, ensure local Free subscription exists
    const activeSub = await prisma.subscription.findFirst({
      where: { shopId: shop.id, status: "ACTIVE" },
    });
    if (!activeSub) {
      const now = new Date();
      const perpetualEnd = new Date(now.getTime() + 3650 * 24 * 60 * 60 * 1000);
      await prisma.subscription.create({
        data: {
          id: `sub_${shop.id}_${Date.now()}`,
          shopId: shop.id,
          plan: "FREE",
          status: "ACTIVE",
          price: 0.0,
          currency: "USD",
          billingInterval: "MONTHLY",
          monthlyLimit: 0,
          currentUsage: 0,
          startedAt: now,
          billingPeriodStart: now,
          billingPeriodEnd: perpetualEnd,
        },
      });
    }
  }

  // Trigger initial Store Intelligence discovery asynchronously in background
  import("./store-intelligence.server")
    .then(({ StoreIntelligenceService }) => StoreIntelligenceService.analyzeStoreAndBuildProfile(shop.id, shop.shopifyDomain))
    .catch(() => {});

  return shop;
}

/**
 * Gets a Shop and returns the decrypted access token.
 */
export async function getShopWithDecryptedToken(shopifyDomain: string) {
  const normalizedDomain = shopifyDomain.toLowerCase().trim();
  const shop = await prisma.shop.findUnique({
    where: { shopifyDomain: normalizedDomain },
  });

  if (!shop) return null;

  return {
    ...shop,
    decryptedAccessToken: decryptToken(shop.accessToken),
    decryptedRefreshToken: shop.refreshToken ? decryptToken(shop.refreshToken) : null,
  };
}

/**
 * Ensures an active, non-expired offline access token is returned for Shopify Admin API calls.
 * Automatically exchanges legacy non-expiring tokens for expiring tokens and refreshes expired tokens.
 * Utilizes TokenManager with concurrency deduplication, proactive refresh, and client credentials recovery.
 */
export async function getValidAdminAccessToken(shopDomainOrRecord: any, forceRefresh = false): Promise<string> {
  const { getValidAdminAccessToken: getValidFromManager } = await import("./token-manager.server");
  return getValidFromManager(shopDomainOrRecord, forceRefresh);
}

/**
 * Legacy pass-through for token migration. Existing offline tokens are valid.
 */
export async function forceMigrateToken(shopDomain: string, currentToken?: string, scopes?: string): Promise<string | null> {
  return currentToken || null;
}

/**
 * Exchanges a Shopify App Bridge Session Token (id_token JWT) for an offline access token
 * using Shopify's OAuth 2.0 Token Exchange protocol (RFC 8693).
 * Eliminates top-level OAuth redirects for embedded app installations.
 */
export async function exchangeSessionTokenForAccessToken(
  shopDomain: string,
  sessionTokenJwt: string
): Promise<string | null> {
  if (!shopDomain || !sessionTokenJwt) return null;
  const normalizedDomain = shopDomain.includes(".")
    ? shopDomain.toLowerCase().trim()
    : `${shopDomain.toLowerCase().trim()}.myshopify.com`;
  const apiKey = process.env.SHOPIFY_API_KEY || "ff484160a48beda29e79144ee4e9a819";
  const apiSecret = process.env.SHOPIFY_API_SECRET || "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";
  const defaultScopes =
    process.env.SCOPES || "read_products,read_content,read_customers,read_orders,read_discounts";

  try {
    const cleanToken = sessionTokenJwt.replace(/^Bearer\s+/i, "").trim();
    const parts = cleanToken.split(".");
    if (parts.length === 3) {
      try {
        const payload = JSON.parse(Buffer.from(parts[1], "base64url").toString("utf8"));
        if (payload.exp && payload.exp * 1000 <= Date.now()) {
          // Token is expired, do not make request to Shopify
          return null;
        }
      } catch {}
    }

    const exchangeParams = new URLSearchParams({
      client_id: apiKey,
      client_secret: apiSecret,
      grant_type: "urn:ietf:params:oauth:grant-type:token-exchange",
      subject_token: cleanToken,
      subject_token_type: "urn:ietf:params:oauth:token-type:id_token",
      requested_token_type: "urn:shopify:params:oauth:token-type:offline-access-token",
    });

    const response = await fetch(`https://${normalizedDomain}/admin/oauth/access_token`, {
      method: "POST",
      headers: {
        "Content-Type": "application/x-www-form-urlencoded",
        Accept: "application/json",
      },
      body: exchangeParams.toString(),
    });

    if (response.ok) {
      const tokenData = await response.json();
      if (tokenData.access_token) {
        const accessToken = tokenData.access_token;
        const refreshToken = tokenData.refresh_token || undefined;
        const expiresIn = tokenData.expires_in ? Number(tokenData.expires_in) : undefined;
        const tokenExpiresAt = expiresIn ? new Date(Date.now() + expiresIn * 1000) : undefined;
        const refreshTokenExpiresAt = refreshToken ? new Date(Date.now() + 90 * 24 * 60 * 60 * 1000) : undefined;
        const tokenUpdatedAt = new Date();

        await registerShop({
          shopifyDomain: normalizedDomain,
          accessToken,
          refreshToken,
          tokenExpiresAt,
          refreshTokenExpiresAt,
          tokenUpdatedAt,
          scopes: tokenData.scope || defaultScopes,
          isReinstall: false,
        });

        try {
          await (prisma as any).session?.upsert({
            where: { id: `offline_${normalizedDomain}` },
            update: {
              shop: normalizedDomain,
              state: "active",
              isOnline: false,
              scope: tokenData.scope || defaultScopes,
              accessToken,
            },
            create: {
              id: `offline_${normalizedDomain}`,
              shop: normalizedDomain,
              state: "active",
              isOnline: false,
              scope: tokenData.scope || defaultScopes,
              accessToken,
            },
          });
        } catch {}

        return accessToken;
      }
    } else {
      const errText = await response.text();
      console.warn(`[exchangeSessionTokenForAccessToken] HTTP ${response.status}:`, errText.slice(0, 120));
    }
  } catch (err) {
    console.warn("[exchangeSessionTokenForAccessToken] Warning:", err);
  }

  return null;
}

