import { AIProvider, AgentRunParams, AIProviderResponse } from "./types";
import { REGISTERED_AI_TOOLS } from "../openai/tools";
import { executeAITool } from "../openai/agent";
import { getTenantByDomain } from "../security/tenant-context";
import { logger } from "../security/logger";
import { buildStoreSystemPrompt } from "./system-prompt";
import prisma from "../../db.server";
import { decryptToken } from "../security/encryption";
import { runLocalShopifyFallback } from "./local-provider";
import { parseWidgetSettings } from "../../services/widget-settings.server";

export class GeminiProvider implements AIProvider {
  name: "gemini" = "gemini";

  async generateResponse(params: AgentRunParams): Promise<AIProviderResponse> {
    const { shopDomain, sessionId, userMessage, customerAccessToken, config } = params;
    const tenant = await getTenantByDomain(shopDomain);
    const systemIntegration = await prisma.systemIntegration.findFirst({
      orderBy: { createdAt: "asc" },
    });
    const settings = await prisma.chatbotSettings.findUnique({
      where: { shopId: tenant.shopId },
    });

    const apiKeyEncrypted = settings?.geminiApiKey || systemIntegration?.geminiApiKey;
    const decryptedKey = apiKeyEncrypted ? decryptToken(apiKeyEncrypted) : process.env.GEMINI_API_KEY || process.env.GOOGLE_API_KEY;
    const geminiApiKey = config?.apiKey || decryptedKey;
    const rawModel = config?.model || (settings?.model?.startsWith("gemini-") ? settings.model : undefined) || settings?.geminiModel || systemIntegration?.geminiModel || "gemini-1.5-flash";
    const model = rawModel === "gemini-3.6-flash" ? "gemini-1.5-flash" : rawModel;
    const temperature = config?.temperature ?? settings?.geminiTemperature ?? settings?.temperature ?? systemIntegration?.geminiTemperature ?? 0.7;
    const maxTokens = config?.maxTokens ?? settings?.geminiMaxTokens ?? systemIntegration?.geminiMaxTokens ?? 1000;
    const isEnabled = settings ? settings.geminiEnabled : (systemIntegration?.geminiEnabled ?? true);

    if (!geminiApiKey || isEnabled === false) {
      logger.warn("Google Gemini API key is not configured or integration disabled. Falling back to local Shopify engine.");
      return runLocalShopifyFallback(params);
    }

    const availableProducts = await prisma.productCache.findMany({
      where: { shopId: tenant.shopId },
      orderBy: { updatedAt: "desc" },
      take: 6,
    });

    const knowledgeDocs = await prisma.knowledgeBase.findMany({
      where: { shopId: tenant.shopId, enabled: true },
      take: 15,
    });

    const pastMessages = await prisma.message.findMany({
      where: { shopId: tenant.shopId, sessionId },
      orderBy: { createdAt: "asc" },
      take: 10,
    });

    const geminiTools = [
      {
        functionDeclarations: REGISTERED_AI_TOOLS.map((t) => ({
          name: t.name,
          description: t.description,
          parameters: {
            type: "OBJECT",
            properties: t.parameters.properties,
            required: t.parameters.required || [],
          },
        })),
      },
    ];

    let activePlan = "PRO";
    try {
      const sub = await prisma.subscription.findFirst({
        where: { shopId: tenant.shopId, status: "ACTIVE" },
        orderBy: { createdAt: "desc" },
      });
      if (sub?.plan) activePlan = sub.plan;
    } catch (e) {}

    const { widgetTexts } = parseWidgetSettings(settings);

    const systemInstruction = buildStoreSystemPrompt({
      shopDomain,
      botName: settings?.botName,
      tone: settings?.tone,
      welcomeMessage: settings?.welcomeMessage,
      customSystemPrompt: config?.systemPrompt || settings?.systemPrompt || undefined,
      plan: activePlan,
      storeEmail: widgetTexts.storeSupportEmail || undefined,
      storePhone: widgetTexts.storeSupportPhone || undefined,
      storeHours: widgetTexts.storeSupportHours || undefined,
      storeAddress: widgetTexts.storeAddress || undefined,
      contactUrl: widgetTexts.storeContactUrl || undefined,
    });

    const contentsPayload = [
      ...pastMessages.map((m) => ({
        role: m.role === "USER" ? "user" : "model",
        parts: [{ text: m.content }],
      })),
      { role: "user", parts: [{ text: userMessage }] },
    ];

    try {
      let modelName = model;
      if (!modelName || modelName.startsWith("gpt-") || modelName === "gemini-3.6-flash") {
        modelName = "gemini-1.5-flash";
      }

      logger.info(`[Gemini Provider] Executing API call to Google Gemini for shop '${shopDomain}' using model '${modelName}'`);
      let res = await fetch(
        `https://generativelanguage.googleapis.com/v1beta/models/${modelName}:generateContent?key=${geminiApiKey}`,
        {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({
            systemInstruction: { parts: [{ text: systemInstruction }] },
            contents: contentsPayload,
            tools: geminiTools,
            generationConfig: {
              temperature,
              maxOutputTokens: maxTokens,
            },
          }),
        }
      );

      if (!res.ok && res.status === 429 && modelName !== "gemini-1.5-flash") {
        logger.info("Primary Gemini model rate limited (429), retrying with gemini-1.5-flash");
        const fallbackRes = await fetch(
          `https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash:generateContent?key=${geminiApiKey}`,
          {
            method: "POST",
            headers: { "Content-Type": "application/json" },
            body: JSON.stringify({
              systemInstruction: { parts: [{ text: systemInstruction }] },
              contents: contentsPayload,
              tools: geminiTools,
              generationConfig: {
                temperature: config?.temperature ?? settings?.temperature ?? 0.7,
                maxOutputTokens: config?.maxTokens ?? 1000,
              },
            }),
          }
        );
        if (fallbackRes.ok) {
          res = fallbackRes;
        }
      }

      if (!res.ok) {
        const errText = await res.text();
        logger.error("Google Gemini API error, falling back to local Shopify catalog", { status: res.status, errText });
        return runLocalShopifyFallback(params);
      }

      const data = await res.json();
      const candidate = data.candidates?.[0];
      const parts = candidate?.content?.parts || [];

      const functionCallPart = parts.find((p: any) => p.functionCall);
      if (functionCallPart) {
        const { name: toolName, args: toolArgs } = functionCallPart.functionCall;
        const toolResult: any = await executeAITool({
          toolName,
          args: toolArgs || {},
          shopDomain,
          tenantShopId: tenant.shopId,
          sessionId,
          customerAccessToken,
        });

        // 2nd turn: Send function result back to Gemini so Gemini dynamically generates response
        try {
          const secondTurnPayload = [
            ...contentsPayload,
            {
              role: "model",
              parts: [{ functionCall: functionCallPart.functionCall }],
            },
            {
              role: "user",
              parts: [
                {
                  functionResponse: {
                    name: toolName,
                    response: { name: toolName, content: toolResult },
                  },
                },
              ],
            },
          ];

          const secondRes = await fetch(
            `https://generativelanguage.googleapis.com/v1beta/models/${modelName}:generateContent?key=${geminiApiKey}`,
            {
              method: "POST",
              headers: { "Content-Type": "application/json" },
              body: JSON.stringify({
                systemInstruction: { parts: [{ text: systemInstruction }] },
                contents: secondTurnPayload,
                tools: geminiTools,
                generationConfig: {
                  temperature: config?.temperature ?? settings?.temperature ?? 0.7,
                  maxOutputTokens: config?.maxTokens ?? 1000,
                },
              }),
            }
          );

          if (secondRes.ok) {
            const secondData = await secondRes.json();
            const secondCandidate = secondData.candidates?.[0];
            const secondParts = secondCandidate?.content?.parts || [];
            const secondText = secondParts.find((p: any) => p.text)?.text;

            if (secondText) {
              const cleanReply = (toolName === "search_products" && toolResult.products && toolResult.products.length > 0)
                ? toolResult.reply
                : secondText;

              return {
                provider: "gemini",
                model: modelName,
                reply: cleanReply,
                intent: toolResult.intent || "general_qa",
                products: toolResult.products,
                articles: toolResult.articles,
                orderInfo: toolResult.orderInfo,
                toolCalls: [{ name: toolName, args: toolArgs }],
                toolResults: [toolResult],
              };
            }
          }
        } catch (secondErr) {
          logger.warn("Gemini 2nd turn after function response failed, using tool result reply", { error: String(secondErr) });
        }

        return {
          provider: "gemini",
          model: modelName,
          reply: toolResult.reply || toolResult.message || "I have processed your request.",
          intent: toolResult.intent || "general_qa",
          products: toolResult.products,
          articles: toolResult.articles,
          orderInfo: toolResult.orderInfo,
          toolCalls: [{ name: toolName, args: toolArgs }],
          toolResults: [toolResult],
        };
      }

      const textPart = parts.find((p: any) => p.text);
      return {
        provider: "gemini",
        model: modelName,
        reply: textPart?.text || "Welcome to our store! How can I assist you today?",
        intent: "general_qa",
      };
    } catch (err: any) {
      logger.error("Gemini Provider Exception, falling back to local Shopify catalog", err);
      return runLocalShopifyFallback(params);
    }
  }
}
