import { promises as fs } from "fs";
import path from "path";
import type { ConnectedAccount } from "@/types/workflow";
import { isMariaDbEnabled } from "@/lib/db/config";
import {
  deleteProjectSocialAccountFromDb,
  loadProjectSocialAccountsFromDb,
  saveProjectSocialAccountsToDb,
  upsertProjectSocialAccountToDb,
  type StoredSocialAccount,
} from "@/lib/db/repositories/social-accounts-repo";

export type { StoredSocialAccount };

const ROOT = path.join(process.cwd(), "data", "social-accounts");

function safeId(id: string): string {
  return id.replace(/[^a-zA-Z0-9_-]/g, "_");
}

function filePath(userId: string, projectId: string) {
  return path.join(ROOT, safeId(userId), `${safeId(projectId)}.json`);
}

async function ensureDir(userId: string) {
  await fs.mkdir(path.join(ROOT, safeId(userId)), { recursive: true });
}

async function loadJson(userId: string, projectId: string): Promise<StoredSocialAccount[]> {
  try {
    const raw = await fs.readFile(filePath(userId, projectId), "utf-8");
    return JSON.parse(raw) as StoredSocialAccount[];
  } catch {
    return [];
  }
}

async function saveJson(
  userId: string,
  projectId: string,
  accounts: StoredSocialAccount[],
): Promise<void> {
  await ensureDir(userId);
  await fs.writeFile(filePath(userId, projectId), JSON.stringify(accounts, null, 2), "utf-8");
}

export async function loadProjectSocialAccounts(
  userId: string,
  projectId: string,
): Promise<StoredSocialAccount[]> {
  if (isMariaDbEnabled()) {
    try {
      return await loadProjectSocialAccountsFromDb(userId, projectId);
    } catch (err) {
      console.error("[PostSync] MariaDB loadProjectSocialAccounts failed:", err);
      // Dual-read: keep JSON as emergency mirror when DB is down.
      const json = await loadJson(userId, projectId);
      if (json.length) return json;
      throw err;
    }
  }
  return loadJson(userId, projectId);
}

export async function saveProjectSocialAccounts(
  userId: string,
  projectId: string,
  accounts: StoredSocialAccount[],
): Promise<void> {
  if (isMariaDbEnabled()) {
    await saveProjectSocialAccountsToDb(userId, projectId, accounts);
    // Mirror to JSON so rollback STORAGE_BACKEND=json still has tokens.
    try {
      await saveJson(userId, projectId, accounts);
    } catch {
      // ignore mirror failures
    }
    return;
  }
  await saveJson(userId, projectId, accounts);
}

export async function upsertProjectSocialAccount(
  userId: string,
  projectId: string,
  account: Omit<StoredSocialAccount, "updatedAt">,
): Promise<StoredSocialAccount> {
  if (isMariaDbEnabled()) {
    const saved = await upsertProjectSocialAccountToDb(userId, projectId, account);
    try {
      const existing = await loadJson(userId, projectId);
      const now = new Date().toISOString();
      const record: StoredSocialAccount = { ...saved, updatedAt: now };
      const index = existing.findIndex((a) => a.platform === account.platform);
      if (index === -1) existing.push(record);
      else existing[index] = { ...existing[index], ...record };
      await saveJson(userId, projectId, existing);
    } catch {
      // ignore mirror failures
    }
    return saved;
  }

  const existing = await loadJson(userId, projectId);
  const now = new Date().toISOString();
  const record: StoredSocialAccount = { ...account, updatedAt: now };
  const index = existing.findIndex((a) => a.platform === account.platform);
  if (index === -1) existing.push(record);
  else existing[index] = { ...existing[index], ...record, updatedAt: now };
  await saveJson(userId, projectId, existing);
  return record;
}

export async function deleteProjectSocialAccount(
  userId: string,
  projectId: string,
  platform: string,
): Promise<void> {
  if (isMariaDbEnabled()) {
    await deleteProjectSocialAccountFromDb(userId, projectId, platform);
    try {
      const existing = await loadJson(userId, projectId);
      await saveJson(
        userId,
        projectId,
        existing.filter((a) => a.platform !== platform),
      );
    } catch {
      // ignore
    }
    return;
  }

  const existing = await loadJson(userId, projectId);
  const filtered = existing.filter((a) => a.platform !== platform);
  if (filtered.length === existing.length) return;
  await saveJson(userId, projectId, filtered);
}

export function mergeAccountsForPublish(
  clientAccounts: ConnectedAccount[],
  serverAccounts: StoredSocialAccount[],
): Array<ConnectedAccount & { publishToken?: string | null }> {
  const byPlatform = new Map(serverAccounts.map((a) => [a.platform, a]));
  const merged: Array<ConnectedAccount & { publishToken?: string | null }> = clientAccounts.map(
    (client) => {
      const server = byPlatform.get(client.platform);
      const hasToken = Boolean(server?.publishToken || server?.accessToken);
      if (!server?.connected || !hasToken) return client;
      return {
        ...client,
        connected: true,
        connectionStatus:
          server.connectionStatus === "expired"
            ? ("expired" as const)
            : server.connectionStatus === "error"
              ? ("error" as const)
              : ("connected" as const),
        profileName: server.profileName,
        // Keep user token on accessToken; page token on publishToken (Facebook/Instagram).
        accessToken: server.accessToken ?? server.publishToken ?? null,
        publishToken: server.publishToken ?? null,
        refreshToken: server.refreshToken ?? client.refreshToken,
        expiresAt: server.expiresAt ?? client.expiresAt,
        externalId: server.externalId ?? client.externalId,
        lastSyncedAt: server.lastSyncedAt ?? client.lastSyncedAt,
      };
    },
  );

  const clientPlatforms = new Set(clientAccounts.map((a) => a.platform));
  for (const server of serverAccounts) {
    const hasToken = Boolean(server.publishToken || server.accessToken);
    if (clientPlatforms.has(server.platform) || !server.connected || !hasToken) continue;
    merged.push({
      platform: server.platform,
      label: server.label,
      profileName: server.profileName,
      connected: true,
      connectionStatus: server.connectionStatus,
      accessToken: server.accessToken ?? server.publishToken ?? null,
      publishToken: server.publishToken ?? null,
      refreshToken: server.refreshToken ?? null,
      expiresAt: server.expiresAt ?? null,
      externalId: server.externalId ?? null,
      lastSyncedAt: server.lastSyncedAt ?? null,
    });
  }

  return merged;
}

/** Public-safe account list (no tokens). */
export function maskAccountsForClient(accounts: StoredSocialAccount[]): ConnectedAccount[] {
  return accounts.map((a) => ({
    platform: a.platform,
    label: a.label,
    profileName: a.profileName,
    connected: a.connected,
    connectionStatus: a.connectionStatus,
    accessToken: null,
    refreshToken: null,
    expiresAt: a.expiresAt ?? null,
    lastSyncedAt: a.lastSyncedAt ?? null,
    externalId: a.externalId ?? null,
  }));
}
