"use client";

import { useState, useTransition, useMemo } from "react";
import { useRouter } from "next/navigation";
import {
  Recommendation,
  RecommendationType,
} from "@/app/db/schema/recommendations";
import { InstalledMeasureSummary } from "@/app/portfolio/[slug]/building-passport/[propertyId]/utils";
import {
  setDefaultRecommendation,
  clearCategoryDefault,
  updatePlanMetrics,
} from "@/app/actions/recommendations";
import RecommendationCard, { AugmentedRec } from "./RecommendationCard";
import StickyImpactBar from "./StickyImpactBar";
import { sapToEpc } from "@/app/utils";
import { WrenchScrewdriverIcon } from "@heroicons/react/24/outline";

export type { AugmentedRec };

const typeToCategoryMap: Partial<
  Record<RecommendationType, RecommendationType>
> = {
  internal_wall_insulation: "wall_insulation",
  external_wall_insulation: "wall_insulation",
  cavity_wall_insulation: "wall_insulation",
  extension_cavity_wall_insulation: "extension_cavity_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",
  windows_glazing: "windows_glazing",
  heating: "heating",
};

interface Props {
  recommendations: Recommendation[];
  installedMeasures: InstalledMeasureSummary[];
  planId: string;
  slug: string;
  propertyId: string;
  currentSapPoints: number;
  savedCostOfWorks: number;
  savedContingencyCost: number;
}

export default function RecommendationContainer({
  recommendations,
  installedMeasures,
  planId,
  slug,
  propertyId,
  currentSapPoints,
  savedCostOfWorks,
  savedContingencyCost,
}: Props) {
  const installedTypeSet = new Set(installedMeasures.map((m) => m.measureType));

  const categorized = recommendations.reduce(
    (acc, rec) => {
      const category = (typeToCategoryMap[rec.type as RecommendationType] ??
        rec.type) as RecommendationType;
      const alreadyInstalled =
        rec.measureType != null && installedTypeSet.has(rec.measureType);
      const augmented: AugmentedRec = {
        ...rec,
        alreadyInstalled,
        sapPoints: alreadyInstalled ? 0 : rec.sapPoints,
        estimatedCost: alreadyInstalled ? 0 : rec.estimatedCost,
        co2EquivalentSavings: alreadyInstalled ? 0 : rec.co2EquivalentSavings,
        energyCostSavings: alreadyInstalled ? 0 : rec.energyCostSavings,
        kwhSavings: alreadyInstalled ? 0 : rec.kwhSavings,
        labourDays: alreadyInstalled ? 0 : rec.labourDays,
      };
      if (!acc[category]) acc[category] = [];
      acc[category].push(augmented);
      return acc;
    },
    {} as Record<RecommendationType, AugmentedRec[]>,
  );

  const [selections, setSelections] = useState<
    Record<string, AugmentedRec | null>
  >(() =>
    Object.fromEntries(
      Object.entries(categorized).map(([cat, recs]) => [
        cat,
        recs.find((r) => r.default) ?? null,
      ]),
    ),
  );

  // Pending selections — changes not yet saved to the database
  const [pendingSelections, setPendingSelections] = useState<
    Record<string, AugmentedRec | null>
  >({});

  const router = useRouter();
  const [isPending, startTransition] = useTransition();

  // Sort: included first, then not-included, then already-installed
  const sortedCategories = Object.entries(categorized).sort(
    ([catA, recsA], [catB, recsB]) => {
      const score = (cat: string, recs: AugmentedRec[]) => {
        if (recs.some((r) => r.alreadyInstalled)) return 2;
        if (selections[cat]) return 0;
        return 1;
      };
      return score(catA, recsA) - score(catB, recsB);
    },
  );

  // Only stages a pending change — does NOT call server actions
  function handleSelect(category: string, rec: AugmentedRec) {
    setPendingSelections((prev) => ({ ...prev, [category]: rec }));
  }

  // Stages a removal as a pending change — does NOT call server actions
  function handleRemove(category: string) {
    setPendingSelections((prev) => ({ ...prev, [category]: null }));
  }

  // Discard all pending changes
  function handleDiscardAll() {
    setPendingSelections({});
  }

  // Save all pending changes to the database
  function handleSaveAll() {
    const snapshot = { ...pendingSelections };

    setSelections((prev) => {
      const merged = { ...prev };
      for (const [cat, rec] of Object.entries(snapshot)) {
        merged[cat] = rec;
      }
      return merged;
    });
    setPendingSelections({});

    startTransition(async () => {
      await Promise.all(
        Object.entries(snapshot).map(([cat, rec]) => {
          if (rec) {
            return setDefaultRecommendation(
              planId,
              String(rec.id),
              slug,
              propertyId,
              { skipRevalidate: true },
            );
          } else {
            const anyRec = categorized[cat as RecommendationType]?.[0];
            if (!anyRec) return Promise.resolve();
            return clearCategoryDefault(planId, anyRec.type, slug, propertyId, {
              skipRevalidate: true,
            });
          }
        }),
      );
      await updatePlanMetrics(planId, currentSapPoints, slug, propertyId);
      router.refresh();
    });
  }

  // Projected metrics: merge pending on top of committed, then sum
  const projectedMetrics = useMemo(() => {
    const effectiveRecs = Object.keys(categorized)
      .map((cat) =>
        cat in pendingSelections ? pendingSelections[cat] : selections[cat],
      )
      .filter((r): r is AugmentedRec => r != null);

    const sapGain = effectiveRecs.reduce((s, r) => s + (r.sapPoints ?? 0), 0);
    const projectedSap = currentSapPoints + sapGain;

    const projectedCost = effectiveRecs.reduce(
      (s, r) => s + (r.estimatedCost ?? 0),
      0,
    );
    // Scale contingency proportionally from saved plan values
    const contingencyRate = savedCostOfWorks > 0 ? savedContingencyCost / savedCostOfWorks : 0;
    const projectedContingency = Math.round(projectedCost * contingencyRate);

    return {
      projectedSap,
      projectedEpc: sapToEpc(projectedSap),
      projectedCo2: effectiveRecs.reduce(
        (s, r) => s + (r.co2EquivalentSavings ?? 0),
        0,
      ),
      projectedHeatDemand: effectiveRecs.reduce(
        (s, r) => s + (r.heatDemand ?? 0),
        0,
      ),
      projectedCost,
      projectedContingency,
    };
  }, [pendingSelections, selections, categorized, currentSapPoints, savedCostOfWorks, savedContingencyCost]);

  // Current saved baseline derived from committed selections
  const savedMetrics = useMemo(() => {
    const committed = Object.values(selections).filter(
      (r): r is AugmentedRec => r != null,
    );
    const sapGain = committed.reduce((s, r) => s + (r.sapPoints ?? 0), 0);
    const savedSap = currentSapPoints + sapGain;
    return { savedSap, savedEpc: sapToEpc(savedSap) };
  }, [selections, currentSapPoints]);

  const hasPendingChanges = Object.keys(pendingSelections).length > 0;
  const pendingCount = Object.keys(pendingSelections).length;

  if (Object.keys(categorized).length === 0) {
    return (
      <div className="bg-white rounded-2xl border border-dashed border-gray-200 p-16 flex flex-col items-center justify-center text-center gap-4">
        <div className="w-14 h-14 rounded-2xl bg-gray-50 border border-gray-200 flex items-center justify-center">
          <WrenchScrewdriverIcon className="w-7 h-7 text-gray-400" />
        </div>
        <p className="text-sm text-gray-400">
          No recommendations for this plan.
        </p>
      </div>
    );
  }

  return (
    <>
      <div className="space-y-10 pb-24">
        {sortedCategories.map(([category, recs]) => {
          const isIncluded =
            !!selections[category] && !recs.some((r) => r.alreadyInstalled);
          return (
            <RecommendationCard
              key={category}
              category={category as RecommendationType}
              recs={recs}
              selected={selections[category] ?? null}
              pendingSelection={pendingSelections[category] ?? null}
              hasPendingChange={category in pendingSelections}
              isIncluded={isIncluded}
              isPending={isPending}
              onSelect={(rec) => handleSelect(category, rec)}
              onRemove={() => handleRemove(category)}
            />
          );
        })}
      </div>

      <StickyImpactBar
        hasPendingChanges={hasPendingChanges}
        pendingCount={pendingCount}
        savedSap={savedMetrics.savedSap}
        savedEpc={savedMetrics.savedEpc}
        projectedSap={projectedMetrics.projectedSap}
        projectedEpc={projectedMetrics.projectedEpc}
        projectedCo2={projectedMetrics.projectedCo2}
        projectedHeatDemand={projectedMetrics.projectedHeatDemand}
        projectedCost={projectedMetrics.projectedCost}
        projectedContingency={projectedMetrics.projectedContingency}
        onSaveAll={handleSaveAll}
        onDiscard={handleDiscardAll}
        isPending={isPending}
      />
    </>
  );
}
