import { promises as fs } from "fs";
import path from "path";
import { PrismaClient } from "@prisma/client";

export type SystemConfigData = {
  app?: { initialized?: boolean };
  db?: { databaseUrl?: string };
  auth?: { jwtSecret?: string };
  email?: {
    enabled?: boolean;
    provider?: "smtp";
    fromAddress?: string;
    from?: string;
    smtp?: { host?: string; port?: number; user?: string; pass?: string };
  };
  stripe?: {
    enabled?: boolean;
    secretKey?: string;
    webhookSecret?: string;
    publishableKey?: string;
  };
  oauth?: {
    google?: { enabled?: boolean; clientId?: string; clientSecret?: string };
    github?: { enabled?: boolean; clientId?: string; clientSecret?: string };
    facebook?: { enabled?: boolean; clientId?: string; clientSecret?: string };
    discord?: { enabled?: boolean; clientId?: string; clientSecret?: string };
  };
  googleAuth?: {
    enabled?: boolean;
    clientId?: string;
    clientSecret?: string;
  };
  googleCalendar?: {
    enabled?: boolean;
    serviceAccountEmail?: string;
    serviceAccountKey?: string;
    calendarId?: string;
  };
};

const DATA_DIR = process.env.CONFIG_DIR || path.join(process.cwd(), "data");
const FILE_PATH = path.join(DATA_DIR, "system-config.json");

const defaultConfig: SystemConfigData = {
  app: { initialized: false },
  db: { databaseUrl: process.env.DATABASE_URL },
  auth: { jwtSecret: process.env.JWT_SECRET || process.env.BETTER_AUTH_SECRET },
  email: {
    enabled: Boolean(process.env.EMAIL_FROM),
    provider: (process.env.EMAIL_PROVIDER as any) || undefined,
    fromAddress: process.env.EMAIL_FROM,
    from: process.env.EMAIL_FROM,
    smtp: {
      host: process.env.SMTP_HOST,
      port: process.env.SMTP_PORT ? Number(process.env.SMTP_PORT) : undefined,
      user: process.env.SMTP_USER,
      pass: process.env.SMTP_PASS,
    },
  },
  stripe: {
    enabled: Boolean(process.env.STRIPE_SECRET_KEY),
    secretKey: process.env.STRIPE_SECRET_KEY,
    webhookSecret: process.env.STRIPE_WEBHOOK_SECRET,
    publishableKey: process.env.STRIPE_PUBLISHABLE_KEY,
  },
  oauth: {
    google: {
      enabled: Boolean(process.env.GOOGLE_CLIENT_ID),
      clientId: process.env.GOOGLE_CLIENT_ID,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET,
    },
    github: {
      enabled: Boolean(process.env.GITHUB_CLIENT_ID),
      clientId: process.env.GITHUB_CLIENT_ID,
      clientSecret: process.env.GITHUB_CLIENT_SECRET,
    },
    facebook: {
      enabled: Boolean(process.env.FACEBOOK_CLIENT_ID),
      clientId: process.env.FACEBOOK_CLIENT_ID,
      clientSecret: process.env.FACEBOOK_CLIENT_SECRET,
    },
    discord: {
      enabled: Boolean(process.env.DISCORD_CLIENT_ID),
      clientId: process.env.DISCORD_CLIENT_ID,
      clientSecret: process.env.DISCORD_CLIENT_SECRET,
    },
  },
  googleAuth: {
    enabled: Boolean(process.env.GOOGLE_CLIENT_ID),
    clientId: process.env.GOOGLE_CLIENT_ID,
    clientSecret: process.env.GOOGLE_CLIENT_SECRET,
  },
};

async function ensureDir() {
  await fs.mkdir(DATA_DIR, { recursive: true });
}

export async function loadSystemConfig(): Promise<SystemConfigData> {
  await ensureDir();
  try {
    const raw = await fs.readFile(FILE_PATH, "utf-8");
    const fileCfg = JSON.parse(raw) as SystemConfigData;
    return deepMerge(defaultConfig, fileCfg);
  } catch {
    // create initial file config
    await fs.writeFile(FILE_PATH, JSON.stringify(defaultConfig, null, 2), "utf-8");
    return defaultConfig;
  }
}

export async function saveSystemConfig(partial: SystemConfigData, prisma?: PrismaClient | null) {
  const current = await loadSystemConfig();
  const merged = deepMerge(current, partial);
  await ensureDir();
  await fs.writeFile(FILE_PATH, JSON.stringify(merged, null, 2), "utf-8");

  // Also persist to DB if available (best-effort)
  if (prisma) {
    try {
      await prisma.systemConfig.upsert({
        where: { id: 1 },
        update: { data: merged as any },
        create: { id: 1, data: merged as any },
      });
    } catch {
      // ignore
    }
  }

  return merged;
}

function isObject(v: any) {
  return v && typeof v === "object" && !Array.isArray(v);
}

function deepMerge<T>(base: T, patch: any): T {
  if (!isObject(base) || !isObject(patch)) return (patch ?? base) as T;
  const out: any = { ...base };
  for (const k of Object.keys(patch)) {
    const bv = (base as any)[k];
    const pv = patch[k];
    out[k] = isObject(bv) && isObject(pv) ? deepMerge(bv, pv) : pv;
  }
  return out;
}
