import prisma from "./prisma";

export interface EffectiveSetting {
  category: string;
  key: string;
  value: unknown;
  source: "user" | "tenant" | "customer" | "default";
  dataType: string;
  description: string | null;
  allowedValues: unknown;
}

/**
 * Resolve effective settings using the 3-level cascade:
 *   definition default → customer override → tenant override → user override
 *
 * Only user-overridable settings are included.
 */
export async function getUserEffectiveSettings(
  userId: string,
  tenantId: string,
  customerId: string | undefined,
  categories: string[]
): Promise<Record<string, Record<string, EffectiveSetting>>> {
  // 1. Fetch all definitions for requested categories
  const definitions = await prisma.settingsDefinition.findMany({
    where: { category: { in: categories }, allow_user_override: true },
    orderBy: [{ category: "asc" }, { display_order: "asc" }],
  });

  // 2. Fetch customer overrides
  const customerSettings = customerId
    ? await prisma.customerSetting.findMany({
        where: {
          customerId,
          settingId: { in: definitions.map((d) => d.id) },
        },
      })
    : [];

  // 3. Fetch tenant overrides
  const tenantSettings = await prisma.tenantSetting.findMany({
    where: {
      tenantId,
      settingId: { in: definitions.map((d) => d.id) },
    },
  });

  // 4. Fetch user overrides
  const userSettings = await prisma.userSetting.findMany({
    where: {
      userId,
      settingId: { in: definitions.map((d) => d.id) },
    },
  });

  // Build lookup maps
  const custMap = new Map(customerSettings.map((s) => [s.settingId, s]));
  const tenantMap = new Map(tenantSettings.map((s) => [s.settingId, s]));
  const userMap = new Map(userSettings.map((s) => [s.settingId, s]));

  // 5. Resolve cascade
  const result: Record<string, Record<string, EffectiveSetting>> = {};

  for (const def of definitions) {
    let value: unknown = def.defaultValue;
    let source: EffectiveSetting["source"] = "default";

    const custSetting = custMap.get(def.id);
    if (custSetting) {
      value = custSetting.value;
      source = "customer";
    }

    const tenantSetting = tenantMap.get(def.id);
    if (tenantSetting) {
      // Only override if customer didn't mark as mandatory
      if (!custSetting?.is_mandatory || custSetting.allow_tenant_override) {
        value = tenantSetting.value;
        source = "tenant";
      }
    }

    const userSetting = userMap.get(def.id);
    if (userSetting) {
      // Only override if tenant didn't mark as mandatory
      const canUserOverride = tenantSetting ? tenantSetting.allow_user_override !== false : true;
      if (canUserOverride) {
        value = userSetting.value;
        source = "user";
      }
    }

    if (!result[def.category]) result[def.category] = {};
    result[def.category][def.key] = {
      category: def.category,
      key: def.key,
      value,
      source,
      dataType: def.dataType,
      description: def.description,
      allowedValues: def.allowed_values,
    };
  }

  return result;
}

/**
 * Batch upsert user settings. Each entry is { category, key, value }.
 */
export async function batchUpsertUserSettings(
  userId: string,
  settings: Array<{ category: string; key: string; value: unknown }>
) {
  // Look up setting definition IDs
  const keys = settings.map((s) => ({ category: s.category, key: s.key }));
  const definitions = await prisma.settingsDefinition.findMany({
    where: {
      OR: keys.map((k) => ({ category: k.category, key: k.key })),
    },
    select: { id: true, category: true, key: true },
  });

  const defMap = new Map(definitions.map((d) => [`${d.category}:${d.key}`, d.id]));

  // Build upserts
  const operations = settings
    .map((s) => {
      const settingId = defMap.get(`${s.category}:${s.key}`);
      if (!settingId) return null;
      return prisma.userSetting.upsert({
        where: { userId_settingId: { userId, settingId } },
        update: { value: s.value as never },
        create: { userId, settingId, value: s.value as never },
      });
    })
    .filter(Boolean) as ReturnType<typeof prisma.userSetting.upsert>[];

  await prisma.$transaction(operations);
}

/**
 * Get settings definitions for given categories.
 */
export async function getSettingsDefinitions(categories: string[]) {
  return prisma.settingsDefinition.findMany({
    where: { category: { in: categories } },
    orderBy: [{ category: "asc" }, { display_order: "asc" }],
  });
}
