import { positionAfter } from "./position";
import { prisma } from "./prisma";
import { emitToBoard } from "./socketBus";
import { decryptSecret, encryptSecret } from "./tokenCrypto";
import {
  fetchInboxDelta,
  getMicrosoftAppConfig,
  refreshTokens,
  type GraphMessage,
} from "./microsoftGraph";

const SYNC_INTERVAL_MS = 45 * 1000;
const REFRESH_MARGIN_MS = 5 * 60 * 1000;

function listNameFor(message: GraphMessage): "Flagged" | "Needs Reply" | "Other" {
  if (message.flag?.flagStatus === "flagged") return "Flagged";
  if (!message.isRead) return "Needs Reply";
  return "Other";
}

async function syncAccount(accountId: string) {
  const account = await prisma.mailAccount.findUnique({ where: { id: accountId } });
  if (!account) return;

  const config = await getMicrosoftAppConfig();
  if (!config) return;

  let accessToken = decryptSecret(account.accessToken);

  if (account.tokenExpiresAt.getTime() - Date.now() < REFRESH_MARGIN_MS) {
    const refreshed = await refreshTokens(config, decryptSecret(account.refreshToken));
    accessToken = refreshed.access_token;
    await prisma.mailAccount.update({
      where: { id: account.id },
      data: {
        accessToken: encryptSecret(refreshed.access_token),
        refreshToken: encryptSecret(refreshed.refresh_token),
        tokenExpiresAt: new Date(Date.now() + refreshed.expires_in * 1000),
      },
    });
  }

  const { messages, nextDeltaLink } = await fetchInboxDelta(accessToken, account.deltaLink);
  if (messages.length === 0) {
    await prisma.mailAccount.update({
      where: { id: account.id },
      data: { deltaLink: nextDeltaLink, lastSyncedAt: new Date(), lastSyncError: null },
    });
    return;
  }

  const lists = await prisma.list.findMany({ where: { boardId: account.boardId } });
  const listByName = new Map(lists.map((l) => [l.name, l]));

  for (const message of messages) {
    if (message["@removed"]) continue;

    const targetList = listByName.get(listNameFor(message));
    if (!targetList) continue;

    const from = message.from?.emailAddress;
    const senderLine = from ? `${from.name ?? ""} <${from.address ?? ""}>`.trim() : "Unknown sender";
    const description = `From: ${senderLine}\n\n${message.bodyPreview ?? ""}`;

    const existingCard = await prisma.card.findUnique({
      where: { boardId_externalMessageId: { boardId: account.boardId, externalMessageId: message.id } },
    });

    if (existingCard) {
      await prisma.card.update({
        where: { id: existingCard.id },
        data: {
          title: message.subject || "(No subject)",
          description,
          ...(existingCard.listId !== targetList.id ? { listId: targetList.id } : {}),
        },
      });
    } else {
      const last = await prisma.card.findFirst({
        where: { listId: targetList.id, isArchived: false },
        orderBy: { position: "desc" },
      });
      await prisma.card.create({
        data: {
          listId: targetList.id,
          boardId: account.boardId,
          title: message.subject || "(No subject)",
          description,
          position: positionAfter(last?.position),
          externalMessageId: message.id,
        },
      });
    }
  }

  await prisma.mailAccount.update({
    where: { id: account.id },
    data: { deltaLink: nextDeltaLink, lastSyncedAt: new Date(), lastSyncError: null },
  });

  emitToBoard(account.boardId, "board:changed", {});
}

export async function syncAllMailAccounts() {
  const accounts = await prisma.mailAccount.findMany({ select: { id: true } });
  for (const { id } of accounts) {
    try {
      // eslint-disable-next-line no-await-in-loop
      await syncAccount(id);
    } catch (err) {
      console.error(`[mail-sync] Failed to sync account ${id}:`, err);
      await prisma.mailAccount
        .update({
          where: { id },
          data: { lastSyncError: err instanceof Error ? err.message : "Unknown sync error" },
        })
        .catch(() => {});
    }
  }
}

export function startMailSyncJob() {
  syncAllMailAccounts().catch((err) => console.error("[mail-sync]", err));
  setInterval(() => {
    syncAllMailAccounts().catch((err) => console.error("[mail-sync]", err));
  }, SYNC_INTERVAL_MS);
}
