import { getColumnCount, findFirstEmptySlotIndex, isPageBlock } from "@/admin/pages/sectionLayouts";
import type { PageBlock, PageSection } from "@/admin/types";
import { normalizeSectionStyle } from "@/lib/sectionStyle";
import { sectionWidthForBlock } from "@/lib/sectionWidthForBlock";

/** Section in editor may have null slots for empty column positions. */
export type SectionWithSlots = Omit<PageSection, "blocks"> & { blocks: (PageBlock | null)[] };

export type BlockInsertFailureReason = "no-section" | "invalid-slot" | "slot-filled" | "section-full";

export type BlockInsertResult =
  | {
      ok: true;
      mode: "slot" | "new-section" | "first-section";
      nextSections: SectionWithSlots[];
      staleSectionRedirect?: boolean;
      staleSlotRedirect?: boolean;
    }
  | { ok: false; reason: BlockInsertFailureReason };

export type PlacePageBlockOptions = {
  sectionIndex?: number;
  slotIndex?: number;
  allowNewSection?: boolean;
};

export function createSectionWithBlock(block: PageBlock): SectionWithSlots {
  return {
    width: sectionWidthForBlock(block),
    layout: "100",
    blocks: [block],
    id: `section-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`,
    style: normalizeSectionStyle({}),
  };
}

/** Map a possibly stale section index to a valid index in the current sections array. */
export function resolveSectionIndex(list: SectionWithSlots[], sectionIndex?: number): number | null {
  if (list.length === 0) return null;
  if (sectionIndex !== undefined && sectionIndex >= 0 && sectionIndex < list.length) {
    return sectionIndex;
  }
  return list.length - 1;
}

/** Keep slot index only when it still points at an empty slot on the resolved section. */
export function resolveSlotIndex(section: SectionWithSlots, slotIndex?: number): number | undefined {
  if (slotIndex === undefined) return undefined;
  const slotCount = getColumnCount(section.layout ?? "100");
  if (slotIndex < 0 || slotIndex >= slotCount) return undefined;
  if (isPageBlock(section.blocks[slotIndex])) return undefined;
  return slotIndex;
}

export function blockInsertFailureMessage(
  reason: BlockInsertFailureReason,
  source: "paste" | "library" = "paste",
): string {
  switch (reason) {
    case "no-section":
      return source === "library"
        ? "Target section is no longer available. Add a section and choose the component again."
        : "Target section no longer exists. Try pasting again.";
    case "invalid-slot":
      return "Invalid slot for this section layout.";
    case "slot-filled":
      return "That slot is already filled. Remove the block or pick an empty slot.";
    case "section-full":
      return "This section has no empty slots. Add a new section or remove a block first.";
    default:
      return source === "library"
        ? "Failed to add component from the library. Please try again."
        : "Could not insert the component. Please try again.";
  }
}

function insertBlockAtSlot(
  sections: SectionWithSlots[],
  sectionIndex: number,
  slotIndex: number,
  block: PageBlock,
): SectionWithSlots[] {
  return sections.map((s, i) => {
    if (i !== sectionIndex) return s;
    const blocks: (PageBlock | null)[] = [...s.blocks];
    while (blocks.length < slotIndex) blocks.push(null);
    blocks[slotIndex] = block;
    // Promote empty single-column container sections to full when the block owns its own container.
    const isEmptySingleColumn =
      (s.layout ?? "100") === "100" &&
      blocks.every((b, idx) => (idx === slotIndex ? true : !isPageBlock(b)));
    const nextWidth =
      isEmptySingleColumn && sectionWidthForBlock(block) === "full" ? "full" : s.width;
    return { ...s, blocks, width: nextWidth };
  });
}

/**
 * Pure placement: returns the next sections array on success, or the original array on failure.
 * Callers should only call setState when result.ok is true.
 */
export function placePageBlock(
  prev: SectionWithSlots[],
  block: PageBlock,
  options: PlacePageBlockOptions = {},
): BlockInsertResult {
  const { sectionIndex, slotIndex, allowNewSection } = options;

  if (prev.length === 0) {
    return { ok: true, mode: "first-section", nextSections: [createSectionWithBlock(block)] };
  }

  const resolvedSectionIndex = resolveSectionIndex(prev, sectionIndex);
  if (resolvedSectionIndex === null) {
    return { ok: true, mode: "first-section", nextSections: [createSectionWithBlock(block)] };
  }

  const staleSectionRedirect =
    sectionIndex !== undefined && (sectionIndex < 0 || sectionIndex >= prev.length);

  const section = prev[resolvedSectionIndex];
  if (!section) {
    return { ok: false, reason: "no-section" };
  }

  const resolvedSlot = slotIndex !== undefined ? resolveSlotIndex(section, slotIndex) : undefined;
  const staleSlotRedirect = slotIndex !== undefined && resolvedSlot === undefined;

  // Explicit request for an occupied slot (and no overflow to a new section).
  if (
    slotIndex !== undefined &&
    resolvedSlot === undefined &&
    !allowNewSection &&
    slotIndex >= 0 &&
    slotIndex < getColumnCount(section.layout ?? "100") &&
    isPageBlock(section.blocks[slotIndex])
  ) {
    return { ok: false, reason: "slot-filled" };
  }

  if (resolvedSlot !== undefined) {
    const slotCount = getColumnCount(section.layout ?? "100");
    if (resolvedSlot < 0 || resolvedSlot >= slotCount) {
      return { ok: false, reason: "invalid-slot" };
    }
    if (isPageBlock(section.blocks[resolvedSlot])) {
      return { ok: false, reason: "slot-filled" };
    }

    return {
      ok: true,
      mode: "slot",
      nextSections: insertBlockAtSlot(prev, resolvedSectionIndex, resolvedSlot, block),
      staleSectionRedirect,
      staleSlotRedirect,
    };
  }

  const emptySlot = findFirstEmptySlotIndex(section);
  if (emptySlot !== null) {
    const slotCount = getColumnCount(section.layout ?? "100");
    if (emptySlot < 0 || emptySlot >= slotCount || isPageBlock(section.blocks[emptySlot])) {
      return { ok: false, reason: "slot-filled" };
    }

    return {
      ok: true,
      mode: "slot",
      nextSections: insertBlockAtSlot(prev, resolvedSectionIndex, emptySlot, block),
      staleSectionRedirect,
      staleSlotRedirect,
    };
  }

  if (allowNewSection) {
    return {
      ok: true,
      mode: "new-section",
      nextSections: [...prev, createSectionWithBlock(block)],
      staleSectionRedirect,
      staleSlotRedirect,
    };
  }

  return { ok: false, reason: "section-full" };
}
