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 { LocalFallbackProvider } from "./local-provider";
import { parseWidgetSettings } from "../../services/widget-settings.server";

export class OpenAIProvider implements AIProvider {
  name: "openai" = "openai";

  async generateResponse({
    shopDomain,
    sessionId,
    userMessage,
    customerAccessToken,
    config,
  }: AgentRunParams): Promise<AIProviderResponse> {
    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 = systemIntegration?.openaiApiKey || settings?.openaiApiKey;
    const decryptedKey = apiKeyEncrypted ? decryptToken(apiKeyEncrypted) : process.env.OPENAI_API_KEY;
    const openaiApiKey = config?.apiKey || decryptedKey;
    const model = config?.model || (settings?.model && settings.model.startsWith("gpt-") ? settings.model : (settings?.openaiModel || systemIntegration?.openaiModel || "gpt-4o-mini"));
    const temperature = config?.temperature ?? systemIntegration?.openaiTemperature ?? settings?.openaiTemperature ?? settings?.temperature ?? 0.7;
    const maxTokens = config?.maxTokens ?? systemIntegration?.openaiMaxTokens ?? settings?.openaiMaxTokens ?? 1000;
    const isEnabled = systemIntegration ? systemIntegration.openaiEnabled : (settings?.openaiEnabled ?? true);

    if (!openaiApiKey || isEnabled === false) {
      logger.warn("OpenAI API key is missing or integration disabled. Falling back to Local Shopify Engine.");
      const fallback = new LocalFallbackProvider();
      return fallback.generateResponse({ shopDomain, sessionId, userMessage, customerAccessToken, config });
    }

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

    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 systemPrompt = 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 openAITools = REGISTERED_AI_TOOLS.map((t) => ({
      type: "function" as const,
      function: {
        name: t.name,
        description: t.description,
        parameters: t.parameters,
      },
    }));

    const messagesPayload: any[] = [
      { role: "system", content: systemPrompt },
      ...pastMessages.map((m) => ({
        role: m.role === "USER" ? "user" : "assistant",
        content: m.content,
      })),
      { role: "user", content: userMessage },
    ];

    try {
      logger.info(`[OpenAI Provider] Executing API call to https://api.openai.com/v1/chat/completions for shop '${shopDomain}' using model '${model}'`);
      const res = await fetch("https://api.openai.com/v1/chat/completions", {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          "Authorization": `Bearer ${openaiApiKey}`,
        },
        body: JSON.stringify({
          model,
          messages: messagesPayload,
          tools: openAITools,
          temperature,
          max_tokens: maxTokens,
        }),
      });

      if (!res.ok) {
        const errText = await res.text();
        logger.error("OpenAI API call failed, falling back to Gemini/Local Shopify Catalog Provider", { status: res.status, errText });
        const fallback = new LocalFallbackProvider();
        return fallback.generateResponse({ shopDomain, sessionId, userMessage, customerAccessToken, config });
      }

      const data = await res.json();
      const choice = data.choices?.[0];
      const messageNode = choice?.message;

      if (messageNode?.tool_calls && messageNode.tool_calls.length > 0) {
        const toolCall = messageNode.tool_calls[0];
        const toolName = toolCall.function.name;
        const toolArgs = JSON.parse(toolCall.function.arguments || "{}");

        const toolResult: any = await executeAITool({
          toolName,
          args: toolArgs,
          shopDomain,
          tenantShopId: tenant.shopId,
          sessionId,
          customerAccessToken,
        });

        // 2nd turn: Send tool execution result back to OpenAI for final response generation
        try {
          const secondTurnMessages = [
            ...messagesPayload,
            messageNode,
            {
              role: "tool",
              tool_call_id: toolCall.id,
              content: JSON.stringify(toolResult),
            },
          ];

          const secondRes = await fetch("https://api.openai.com/v1/chat/completions", {
            method: "POST",
            headers: {
              "Content-Type": "application/json",
              "Authorization": `Bearer ${openaiApiKey}`,
            },
            body: JSON.stringify({
              model,
              messages: secondTurnMessages,
              tools: openAITools,
              temperature: config?.temperature ?? settings?.temperature ?? 0.7,
              max_tokens: config?.maxTokens ?? 1000,
            }),
          });

          if (secondRes.ok) {
            const secondData = await secondRes.json();
            const secondText = secondData.choices?.[0]?.message?.content;

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

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

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

      return {
        provider: "openai",
        model,
        reply: messageNode?.content || "How else can I assist you with your shopping today?",
        intent: "general_qa",
      };
    } catch (err) {
      logger.error("OpenAI Provider Exception, falling back to Gemini/Local Shopify Catalog Provider", err);
      const fallback = new LocalFallbackProvider();
      return fallback.generateResponse({ shopDomain, sessionId, userMessage, customerAccessToken, config });
    }
  }
}
