"use server";
import { db } from "@/app/db/db";
import {
  recommendation,
  planRecommendations,
  plan,
} from "@/app/db/schema/recommendations";
import { eq, inArray, and } from "drizzle-orm";
import { revalidatePath } from "next/cache";

// Maps specific recommendation types to their parent category.
// Mirrors the categorisation in RecommendationContainer.
const typeToCategoryMap: Record<string, string> = {
  internal_wall_insulation: "wall_insulation",
  external_wall_insulation: "wall_insulation",
  cavity_wall_insulation: "wall_insulation",
  loft_insulation: "roof_insulation",
  room_roof_insulation: "roof_insulation",
  flat_roof_insulation: "roof_insulation",
  suspended_floor_insulation: "floor_insulation",
  solid_floor_insulation: "floor_insulation",
  exposed_floor_insulation: "floor_insulation",
};

function getCategoryTypes(type: string): string[] {
  const category = typeToCategoryMap[type] ?? type;
  const types = Object.entries(typeToCategoryMap)
    .filter(([, cat]) => cat === category)
    .map(([t]) => t);
  if (!types.includes(category)) types.push(category);
  return types;
}

// Per-measure contingency rates (fractional, e.g. 0.26 = 26%)
const CONTINGENCIES: Record<string, number> = {
  cavity_wall_insulation: 0.1,
  internal_wall_insulation: 0.26,
  external_wall_insulation: 0.26,
  loft_insulation: 0.1,
  solar_pv: 0.15,
  air_source_heat_pump: 0.25,
  flat_roof_insulation: 0.26,
  suspended_floor_insulation: 0.2,
  solid_floor_insulation: 0.26,
  low_energy_lighting: 0.26,
  high_heat_retention_storage_heaters: 0.1,
  windows_glazing: 0.15,
  boiler_upgrade: 0.26,
  time_and_temperature_zone_control: 0.1,
  roomstat_programmer_trvs: 0.1,
  room_roof_insulation: 0.26,
  heater_removal: 0.1,
  sealing_open_fireplace: 0.1,
  mechanical_ventilation: 0.26,
  sloping_ceiling_insulation: 0.26,
};

// Local SAP → EPC letter mapping (mirrors sapToEpc in @/app/utils)
function sapToEpcLetter(sapPoints: number): string {
  if (sapPoints >= 92) return "A";
  if (sapPoints >= 81) return "B";
  if (sapPoints >= 69) return "C";
  if (sapPoints >= 55) return "D";
  if (sapPoints >= 39) return "E";
  if (sapPoints >= 21) return "F";
  return "G";
}

/**
 * Sets a recommendation as the default for its category within a plan.
 * Clears the default flag from all other recommendations in the same category,
 * then sets it on the selected recommendation.
 */
export async function setDefaultRecommendation(
  planId: string,
  selectedRecId: string,
  slug: string,
  propertyId: string,
  options?: { skipRevalidate?: boolean },
) {
  const planRecs = await db
    .select({ recId: planRecommendations.recommendationId })
    .from(planRecommendations)
    .where(eq(planRecommendations.planId, BigInt(planId)));

  const recIds = planRecs.map((r) => r.recId);
  if (recIds.length === 0) return;

  const [selectedRec] = await db
    .select({ type: recommendation.type })
    .from(recommendation)
    .where(eq(recommendation.id, BigInt(selectedRecId)));

  if (!selectedRec) return;

  const categoryTypes = getCategoryTypes(selectedRec.type);

  await db.transaction(async (tx) => {
    await tx
      .update(recommendation)
      .set({ default: false })
      .where(
        and(
          inArray(recommendation.id, recIds),
          inArray(recommendation.type, categoryTypes),
        ),
      );
    await tx
      .update(recommendation)
      .set({ default: true })
      .where(eq(recommendation.id, BigInt(selectedRecId)));
  });

  if (!options?.skipRevalidate) {
    revalidatePath(
      `/portfolio/${slug}/building-passport/${propertyId}/plans/${planId}`,
    );
  }
}

/**
 * Clears the default flag from every recommendation in a category for this plan.
 * Used to remove a measure entirely from the plan.
 */
export async function clearCategoryDefault(
  planId: string,
  categoryType: string,
  slug: string,
  propertyId: string,
  options?: { skipRevalidate?: boolean },
) {
  const planRecs = await db
    .select({ recId: planRecommendations.recommendationId })
    .from(planRecommendations)
    .where(eq(planRecommendations.planId, BigInt(planId)));

  const recIds = planRecs.map((r) => r.recId);
  if (recIds.length === 0) return;

  const categoryTypes = getCategoryTypes(categoryType);

  await db
    .update(recommendation)
    .set({ default: false })
    .where(
      and(
        inArray(recommendation.id, recIds),
        inArray(recommendation.type, categoryTypes),
      ),
    );

  if (!options?.skipRevalidate) {
    revalidatePath(
      `/portfolio/${slug}/building-passport/${propertyId}/plans/${planId}`,
    );
  }
}

/**
 * Recalculates and persists plan-level metrics based on the current set of
 * default recommendations. Contingency is calculated per-measure using
 * measure-specific rates.
 */
export async function updatePlanMetrics(
  planId: string,
  currentSapPoints: number,
  slug: string,
  propertyId: string,
) {
  const planRecs = await db
    .select({ recId: planRecommendations.recommendationId })
    .from(planRecommendations)
    .where(eq(planRecommendations.planId, BigInt(planId)));

  const recIds = planRecs.map((r) => r.recId);

  const defaultRecs =
    recIds.length > 0
      ? await db
          .select()
          .from(recommendation)
          .where(
            and(
              inArray(recommendation.id, recIds),
              eq(recommendation.default, true),
            ),
          )
      : [];

  const costOfWorks = defaultRecs.reduce(
    (s, r) => s + (r.estimatedCost ?? 0),
    0,
  );
  const contingencyCost = defaultRecs.reduce((s, r) => {
    const rate = CONTINGENCIES[r.type] ?? 0.125;
    return s + (r.estimatedCost ?? 0) * rate;
  }, 0);
  const co2Savings = defaultRecs.reduce(
    (s, r) => s + (r.co2EquivalentSavings ?? 0),
    0,
  );
  const energyBillSavings = defaultRecs.reduce(
    (s, r) => s + (r.energyCostSavings ?? 0),
    0,
  );
  const sapPointsGain = defaultRecs.reduce(
    (s, r) => s + (r.sapPoints ?? 0),
    0,
  );
  const postSapPoints = currentSapPoints + sapPointsGain;
  const postEpcRating = sapToEpcLetter(postSapPoints) as
    | "A"
    | "B"
    | "C"
    | "D"
    | "E"
    | "F"
    | "G";

  await db
    .update(plan)
    .set({
      costOfWorks,
      contingencyCost,
      co2Savings,
      energyBillSavings,
      postSapPoints,
      postEpcRating,
    })
    .where(eq(plan.id, BigInt(planId)));

  revalidatePath(
    `/portfolio/${slug}/building-passport/${propertyId}/plans/${planId}`,
  );
}
