import { prisma } from "./db";
import { getBuffer } from "./storage";
import { logActivity, diffCardFields } from "./activity-log";
import { createNotification } from "./notifications";
import {
  createItem,
  updateItem,
  uploadFileToItem,
  mapCardToColumnValues,
} from "./monday";
import { sendWebhook } from "./webhook";
import { getProvider } from "./integrations/registry";
import type { CardData } from "./integrations/types";

/**
 * Flattens a card's fieldData JSON column into top-level keys prefixed
 * with `fieldData.` so field mappings can reference dynamic template fields
 * like any other column. Returns a new object; does not mutate input.
 */
export function flattenCardFieldData(
  card: Record<string, unknown>
): Record<string, unknown> {
  const flat = { ...card };
  const fieldData = card.fieldData;
  if (fieldData && typeof fieldData === "object" && !Array.isArray(fieldData)) {
    for (const [k, v] of Object.entries(fieldData as Record<string, unknown>)) {
      flat[`fieldData.${k}`] = v;
    }
  }
  return flat;
}

export type IntegrationEvent =
  | "ocr_complete"
  | "ocr_error"
  | "card_reviewed"
  | "card_exported"
  | "card_deleted";

export async function fireIntegrationEvent(
  event: IntegrationEvent,
  cardId: string,
  extra?: { oldCard?: Record<string, unknown> }
) {
  try {
    const card = await prisma.responseCard.findUnique({
      where: { id: cardId },
    });
    if (!card) return;

    const cardData = card as unknown as Record<string, unknown>;
    const flatCardData = flattenCardFieldData(cardData);

    logActivityForEvent(event, cardId, cardData, extra?.oldCard).catch(() => {});
    createNotificationForEvent(event, cardId, cardData).catch(() => {});

    // --- New Integration model-based dispatch ---
    if (card.organizationId) {
      const integrations = await prisma.integration.findMany({
        where: {
          organizationId: card.organizationId,
          enabled: true,
        },
      });

      for (const integration of integrations) {
        const triggers = (integration.triggerEvents as string[]) || [];
        if (!triggers.includes(event)) continue;

        const provider = getProvider(integration.provider);
        if (!provider) continue;

        const configWithId = {
          ...(integration.config as Record<string, unknown>),
          _integrationId: integration.id,
        };

        provider
          .pushCard(
            flatCardData as unknown as CardData,
            configWithId,
            integration.fieldMapping
          )
          .then(async (result) => {
            await prisma.integration.update({
              where: { id: integration.id },
              data: {
                lastSyncAt: new Date(),
                lastSyncStatus: result.success ? "success" : "error",
              },
            });

            if (!result.success) {
              createNotification({
                type: "integration_error",
                title: `${integration.name} Sync Failed`,
                message: result.message || "Unknown error",
                cardId,
                actionUrl: `/cards/${cardId}`,
              }).catch(() => {});
            }
          })
          .catch((err) => {
            console.error(
              `[integrations] ${integration.provider} error:`,
              err
            );
          });
      }
    }

    // --- Legacy AppSettings-based dispatch (deprecated, kept for backward compat) ---
    const settings = await prisma.appSettings
      .findUnique({ where: { id: "singleton" } })
      .catch(() => null);

    if (settings) {
      if (
        settings.mondayEnabled &&
        settings.mondayApiToken &&
        settings.mondayBoardId
      ) {
        handleMonday(
          event,
          settings,
          card as unknown as Record<string, unknown>,
          cardId
        ).catch((err) => {
          console.error("[integrations] Monday.com legacy error:", err);
        });
      }

      if (settings.webhookEnabled && settings.webhookUrl) {
        const events = (settings.webhookEvents as string[] | null) ?? [];
        if (events.includes(event)) {
          sendWebhook(
            settings.webhookUrl,
            settings.webhookSecret,
            event,
            cardData
          ).catch(() => {});
        }
      }
    }
  } catch (err) {
    console.error("[integrations] fireIntegrationEvent error:", err);
  }
}

async function logActivityForEvent(
  event: IntegrationEvent,
  cardId: string,
  card: Record<string, unknown>,
  oldCard?: Record<string, unknown>
) {
  const name = (card.name as string) || "Unnamed";
  const confidence = card.ocrConfidence as number | null;

  switch (event) {
    case "ocr_complete":
      await logActivity(cardId, "ocr_complete", "system",
        `Card created via OCR processing${confidence != null ? ` (${Math.round(confidence)}% confidence)` : ""}`);
      break;
    case "ocr_error":
      await logActivity(cardId, "ocr_error", "system",
        `OCR processing failed: ${card.ocrError || "Unknown error"}`);
      break;
    case "card_reviewed":
      await logActivity(cardId, "status_change", "user", `Status changed to reviewed`,
        [{ field: "reviewStatus", from: "unreviewed", to: "reviewed" }]);
      break;
    case "card_exported":
      await logActivity(cardId, "status_change", "user", `Status changed to exported`,
        [{ field: "reviewStatus", from: oldCard?.reviewStatus as string ?? "reviewed", to: "exported" }]);
      break;
    case "card_deleted":
      await logActivity(cardId, "status_change", "user", `Card "${name}" deleted`);
      break;
  }
}

async function createNotificationForEvent(
  event: IntegrationEvent,
  cardId: string,
  card: Record<string, unknown>
) {
  const name = (card.name as string) || "Unnamed Card";
  const confidence = card.ocrConfidence as number | null;

  switch (event) {
    case "ocr_complete":
      await createNotification({
        type: "ocr_complete",
        title: "OCR Complete",
        message: `Card for ${name} processed${confidence != null ? ` (${Math.round(confidence)}% confidence)` : ""}`,
        cardId,
        actionUrl: `/cards/${cardId}`,
      });
      await createNotification({
        type: "card_needs_review",
        title: "Card Needs Review",
        message: `${name} is ready for review`,
        cardId,
        actionUrl: `/cards/${cardId}`,
      });
      break;
    case "ocr_error":
      await createNotification({
        type: "ocr_error",
        title: "OCR Error",
        message: `Processing failed for card: ${card.ocrError || "Unknown error"}`,
        cardId,
        actionUrl: `/cards/${cardId}`,
      });
      break;
    case "card_reviewed":
      await createNotification({
        type: "ocr_complete",
        title: "Card Reviewed",
        message: `${name} has been marked as reviewed`,
        cardId,
        actionUrl: `/cards/${cardId}`,
      });
      break;
    case "card_exported":
      await createNotification({
        type: "ocr_complete",
        title: "Card Exported",
        message: `${name} has been exported`,
        cardId,
        actionUrl: `/cards/${cardId}`,
      });
      break;
  }
}

async function handleMonday(
  event: IntegrationEvent,
  settings: { mondayApiToken: string; mondayBoardId: string; mondayColumnMap: unknown },
  card: Record<string, unknown>,
  cardId: string
) {
  if (event === "card_reviewed" || event === "card_exported") {
    await pushCardToMonday(cardId, settings);
  }
  // ocr_complete no longer triggers Monday.com push -- cards are pushed when reviewed
}

export async function pushCardToMonday(
  cardId: string,
  settingsOverride?: { mondayApiToken: string; mondayBoardId: string; mondayColumnMap: unknown } | null
) {
  const settings = settingsOverride ?? await prisma.appSettings.findUnique({ where: { id: "singleton" } });
  if (!settings?.mondayApiToken || !settings?.mondayBoardId) {
    throw new Error("Monday.com is not configured");
  }

  const card = await prisma.responseCard.findUnique({ where: { id: cardId } });
  if (!card) throw new Error("Card not found");

  const cardData = card as unknown as Record<string, unknown>;
  const token = settings.mondayApiToken;
  const boardId = settings.mondayBoardId;
  const columnMap = (settings.mondayColumnMap as Record<string, unknown>) ?? {};

  const columnValues = mapCardToColumnValues(cardData, columnMap);
  const itemName = (card.name as string) || "Unnamed Card";

  console.log(`[pushCardToMonday] Card "${itemName}" (${cardId}), columnValues:`, JSON.stringify(columnValues));

  if (card.mondayItemId) {
    await updateItem(token, boardId, card.mondayItemId, columnValues);
    return { action: "updated" as const, mondayItemId: card.mondayItemId };
  }

  const itemId = await createItem(token, boardId, itemName, columnValues);
  await prisma.responseCard.update({
    where: { id: cardId },
    data: { mondayItemId: itemId },
  });

  const filesColId = columnMap._files as string | undefined;
  if (filesColId) {
    for (const imgPath of [cardData.backImagePath, cardData.frontImagePath]) {
      if (typeof imgPath === "string" && imgPath) {
        try {
          const buffer = await getBuffer(imgPath);
          const fileName = imgPath.split("/").pop() || "scan.jpg";
          await uploadFileToItem(token, itemId, filesColId, buffer, fileName);
        } catch (err) {
          console.error("[integrations] File upload to Monday failed:", err);
        }
      }
    }
  }

  await logActivity(cardId, "monday_sync", "system", "Card pushed to Monday.com");

  return { action: "created" as const, mondayItemId: itemId };
}
