/* Shared work-plan editor for Create Project wizard and WorkItems page */

function createDraftTaskId() {
  return `draft_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
}

function parseDateAtNoon(dateStr) {
  if (!dateStr) return null;
  const date = new Date(`${dateStr}T12:00:00`);
  return Number.isNaN(date.getTime()) ? null : date;
}

function addCalendarDays(dateStr, days) {
  const date = parseDateAtNoon(dateStr);
  if (!date) return "";
  date.setDate(date.getDate() + Number(days) || 0);
  return date.toISOString().slice(0, 10);
}

function calendarDaysBetween(startStr, endStr) {
  const start = parseDateAtNoon(startStr);
  const end = parseDateAtNoon(endStr);
  if (!start || !end) return 0;
  return Math.max(1, Math.round((end - start) / 86400000));
}

function clampDateString(dateStr, minStr, maxStr) {
  if (!dateStr) return dateStr;
  if (minStr && dateStr < minStr) return minStr;
  if (maxStr && dateStr > maxStr) return maxStr;
  return dateStr;
}

function roundBudgetToLakh(amount) {
  const value = Math.max(0, Number(amount) || 0);
  if (!value) return 0;
  return Math.round(value / 100000) * 100000;
}

function distributeRoundLakhBudget(totalBudget, weights) {
  const count = weights.length;
  if (!count) return [];
  const totalLakhs = Math.max(count, Math.round(roundBudgetToLakh(totalBudget) / 100000));
  const weightSum = weights.reduce((sum, weight) => sum + weight, 0);
  let assignedLakhs = 0;
  return weights.map((weight, index) => {
    let lakhs;
    if (index === count - 1) {
      lakhs = Math.max(1, totalLakhs - assignedLakhs);
    } else {
      lakhs = weightSum > 0
        ? Math.max(1, Math.round(totalLakhs * (weight / weightSum)))
        : Math.max(1, Math.round(totalLakhs / count));
      assignedLakhs += lakhs;
    }
    return lakhs * 100000;
  });
}

function parseIndianCostInput(raw) {
  const text = String(raw || "").trim().replace(/[₹,\s]/g, "");
  if (!text) return 0;
  const lakhMatch = text.match(/^([\d.]+)(lakh|lac|l)$/i);
  if (lakhMatch) return Math.round(parseFloat(lakhMatch[1])) * 100000;
  const kMatch = text.match(/^([\d.]+)k$/i);
  if (kMatch) return roundBudgetToLakh(Math.round(parseFloat(kMatch[1]) * 1000));
  const crMatch = text.match(/^([\d.]+)(cr|crore)$/i);
  if (crMatch) return Math.round(parseFloat(crMatch[1])) * 10000000;
  const num = Number(text);
  if (!Number.isFinite(num)) return 0;
  if (num >= 1000) return roundBudgetToLakh(num);
  return Math.round(num);
}

function defaultTaskDates({ projectStart = "", projectEnd = "", parentTask = null, targetDays = 30 } = {}) {
  const min = parentTask?.startDate || projectStart || "";
  const max = parentTask?.dueDate || projectEnd || "";
  const start = min || projectStart || "";
  let due = "";
  if (start) {
    due = addCalendarDays(start, Number(targetDays) || 30);
    if (max && due > max) due = max;
  } else if (max) {
    due = max;
  }
  return { startDate: start, dueDate: due };
}

function getTaskDateBounds(draft, siblingTasks, projectStartDate, projectEndDate) {
  const parentTask = draft?.parentTaskId
    ? (siblingTasks || []).find((task) => task.id === draft.parentTaskId)
    : null;
  return {
    min: parentTask?.startDate || projectStartDate || "",
    max: parentTask?.dueDate || projectEndDate || "",
    parentTask
  };
}

function validateTaskSchedule(draft, siblingTasks, projectStartDate, projectEndDate) {
  if (isQcType(draft?.type)) return "";
  const { min, max, parentTask } = getTaskDateBounds(draft, siblingTasks, projectStartDate, projectEndDate);
  if (!draft.startDate || !draft.dueDate) {
    return "Add start and end dates for this work item.";
  }
  if (draft.startDate > draft.dueDate) {
    return "End date must be on or after the start date.";
  }
  if (min && draft.startDate < min) {
    return parentTask
      ? `Start date must be on or after ${min} (parent task start).`
      : `Start date must be on or after ${min} (project start).`;
  }
  if (max && draft.dueDate > max) {
    return parentTask
      ? `End date must be on or before ${max} (parent task end).`
      : `End date must be on or before ${max} (project end).`;
  }
  return "";
}

function isQcType(type) {
  return type === "qc";
}

function emptyQcBreakdown() {
  return {
    labor: { estCount: 0, estDailyCost: 0, estDays: 0 },
    materials: []
  };
}

function stripQcScheduling(task) {
  if (!isQcType(task?.type)) return task;
  const breakdown = emptyQcBreakdown();
  return {
    ...task,
    targetDays: 0,
    budgetPlanned: 0,
    startDate: "",
    dueDate: "",
    labor: breakdown.labor,
    materials: breakdown.materials
  };
}

function emptyTaskBreakdown(targetDays = 1) {
  const days = Math.max(1, Number(targetDays) || 1);
  return {
    labor: { estCount: 0, estDailyCost: 0, estDays: days },
    materials: []
  };
}

function applyDirectBudget(task, amount) {
  const breakdown = emptyTaskBreakdown(task.targetDays);
  return {
    ...task,
    budgetPlanned: roundBudgetToLakh(amount),
    labor: breakdown.labor,
    materials: breakdown.materials
  };
}

function createDraftTask(overrides = {}) {
  const targetDays = Number(overrides.targetDays) || 30;
  const title = overrides.title || "";
  const breakdown = emptyTaskBreakdown(targetDays);
  const labor = overrides.labor || breakdown.labor;
  const materials = Array.isArray(overrides.materials) ? overrides.materials : breakdown.materials;
  const task = {
    id: overrides.id || createDraftTaskId(),
    title,
    type: overrides.type || "task",
    parentTaskId: overrides.parentTaskId || null,
    workTypeName: overrides.workTypeName || null,
    isPreset: Boolean(overrides.isPreset),
    targetDays,
    budgetPlanned: Number(overrides.budgetPlanned) || 0,
    labor,
    materials,
    progressPct: 0,
    dependencyEnabled: Boolean(overrides.dependencyEnabled),
    dependencyTaskId: overrides.dependencyTaskId || null,
    dependencyPosition: overrides.dependencyPosition === "before" ? "before" : "after",
    dependsOnTaskId: overrides.dependsOnTaskId || null,
    startDate: overrides.startDate || "",
    dueDate: overrides.dueDate || ""
  };
  if ("budgetPlanned" in overrides) {
    task.budgetPlanned = Number(overrides.budgetPlanned) || 0;
  } else {
    task.budgetPlanned = calculateEstimateBudget(task);
  }
  return task;
}

const LINKABLE_DEPENDENCY_TYPES = new Set(["task", "subtask"]);

function isLinkableDependencyType(type) {
  return LINKABLE_DEPENDENCY_TYPES.has(type);
}

function supportsDependencies(type) {
  return isLinkableDependencyType(type);
}

function formatDependencyOptionLabel(task) {
  const prefix = task.type === "subtask" ? "Subtask · " : "Task · ";
  return `${prefix}${task.title || "Untitled"}`;
}

function resolveDependencyPatches(draft, workItemId, allItems) {
  const linkable = (allItems || []).filter((item) => isLinkableDependencyType(item.type));
  const patchMap = new Map();

  linkable.forEach((item) => {
    if (item.id === workItemId) return;
    const deps = Array.isArray(item.dependencies) ? item.dependencies : [];
    if (deps.includes(workItemId)) {
      patchMap.set(item.id, { dependencies: deps.filter((id) => id !== workItemId) });
    }
  });

  let selfDependencies = [];
  if (draft?.dependencyEnabled && draft.dependencyTaskId && draft.dependencyTaskId !== workItemId) {
    if (draft.dependencyPosition === "before") {
      const related = linkable.find((item) => item.id === draft.dependencyTaskId);
      if (related) {
        const deps = Array.isArray(related.dependencies) ? related.dependencies : [];
        patchMap.set(draft.dependencyTaskId, {
          dependencies: [...new Set([...deps.filter((id) => id !== workItemId), workItemId])]
        });
      }
    } else {
      selfDependencies = [draft.dependencyTaskId];
    }
  }

  patchMap.set(workItemId, { dependencies: selfDependencies });
  return Array.from(patchMap.entries()).map(([id, patch]) => ({ id, ...patch }));
}

function syncBlockDependencies(tasks) {
  const next = (tasks || []).map((task) => ({ ...task, dependsOnTaskId: null }));
  next.forEach((task) => {
    if (!task.dependencyEnabled || !task.dependencyTaskId || task.dependencyTaskId === task.id) return;
    if (task.dependencyPosition === "before") {
      const relIdx = next.findIndex((t) => t.id === task.dependencyTaskId);
      if (relIdx >= 0) next[relIdx] = { ...next[relIdx], dependsOnTaskId: task.id };
      return;
    }
    const idx = next.findIndex((t) => t.id === task.id);
    if (idx >= 0) next[idx] = { ...next[idx], dependsOnTaskId: task.dependencyTaskId };
  });
  return next;
}

/** Expand parent task dates to cover all scheduled subtasks. */
function syncParentTaskDatesFromSubtasks(tasks) {
  if (!Array.isArray(tasks) || !tasks.length) return tasks || [];

  const next = tasks.map((task) => ({ ...task }));
  const indexById = new Map(next.map((task, index) => [task.id, index]));

  next.forEach((task) => {
    if (task.type !== "task" || isQcType(task.type)) return;
    const children = next.filter(
      (entry) => entry.parentTaskId === task.id
        && !isQcType(entry.type)
        && entry.startDate
        && entry.dueDate
    );
    if (!children.length) return;

    const startDate = children.map((entry) => entry.startDate).sort()[0];
    const dueDate = children.map((entry) => entry.dueDate).sort().slice(-1)[0];
    if (!startDate || !dueDate) return;

    const idx = indexById.get(task.id);
    if (idx === undefined) return;

    const targetDays = calendarDaysBetween(startDate, dueDate);
    next[idx] = {
      ...next[idx],
      startDate,
      dueDate,
      targetDays,
      labor: next[idx].labor
        ? { ...next[idx].labor, estDays: targetDays }
        : next[idx].labor
    };
  });

  return next;
}

function cascadeParentBudgetToSubtasks(tasks, parentId, newTotalBudget) {
  if (!Array.isArray(tasks) || !tasks.length) return tasks || [];

  const children = tasks.filter(
    (task) => task.parentTaskId === parentId && task.type === "subtask" && !isQcType(task.type)
  );
  if (!children.length) return tasks;

  const newTotal = roundBudgetToLakh(newTotalBudget);
  const oldTotal = children.reduce((sum, child) => sum + (Number(child.budgetPlanned) || 0), 0);
  if (newTotal === oldTotal) return tasks;

  const weights = children.map((child) => Math.max(0, Number(child.budgetPlanned) || 0));
  const budgetById = new Map();
  const shares = distributeRoundLakhBudget(newTotal, weights);
  children.forEach((child, index) => {
    budgetById.set(child.id, shares[index]);
  });

  const childTotal = [...budgetById.values()].reduce((sum, value) => sum + value, 0);
  return tasks.map((task) => {
    if (budgetById.has(task.id)) {
      const withBudget = applyDirectBudget(task, budgetById.get(task.id));
      return applySimpleCostSplit(withBudget, deriveSimpleCostSplit(withBudget));
    }
    if (task.id === parentId) {
      return { ...task, budgetPlanned: childTotal };
    }
    return task;
  });
}

function syncProjectDependencies(blocks) {
  const next = (blocks || []).map((block) => ({
    ...block,
    tasks: (block.tasks || []).map((task) => ({ ...task, dependsOnTaskId: null }))
  }));

  const applyTaskUpdate = (taskId, patch) => {
    for (let i = 0; i < next.length; i++) {
      const idx = next[i].tasks.findIndex((t) => t.id === taskId);
      if (idx >= 0) {
        next[i] = {
          ...next[i],
          tasks: next[i].tasks.map((t, j) => (j === idx ? { ...t, ...patch } : t))
        };
        return true;
      }
    }
    return false;
  };

  next.forEach((block) => {
    (block.tasks || []).forEach((task) => {
      if (!task.dependencyEnabled || !task.dependencyTaskId || task.dependencyTaskId === task.id) return;
      if (task.dependencyPosition === "before") {
        applyTaskUpdate(task.dependencyTaskId, { dependsOnTaskId: task.id });
      } else {
        applyTaskUpdate(task.id, { dependsOnTaskId: task.dependencyTaskId });
      }
    });
  });

  return next.map((block) => ({
    ...block,
    tasks: syncParentTaskDatesFromSubtasks(block.tasks || [])
  }));
}

function buildProjectTaskIndex(blocks) {
  const index = new Map();
  (blocks || []).forEach((block) => {
    (block.tasks || []).forEach((task) => {
      index.set(task.id, {
        ...task,
        blockId: block.id,
        blockName: block.name?.trim() || "Block"
      });
    });
  });
  return index;
}

function collectProjectDependencyLinks(blocks) {
  const taskIndex = buildProjectTaskIndex(blocks);
  const links = [];
  const seen = new Set();

  taskIndex.forEach((task) => {
    if (!task.dependencyEnabled || !task.dependencyTaskId) return;
    const related = taskIndex.get(task.dependencyTaskId);
    if (!related) return;
    let from;
    let to;
    if (task.dependencyPosition === "before") {
      from = task;
      to = related;
    } else {
      from = related;
      to = task;
    }
    const key = `${from.id}->${to.id}`;
    if (seen.has(key)) return;
    seen.add(key);
    links.push({
      id: key,
      from: { id: from.id, title: from.title, blockId: from.blockId, blockName: from.blockName },
      to: { id: to.id, title: to.title, blockId: to.blockId, blockName: to.blockName },
      crossBlock: from.blockId !== to.blockId
    });
  });

  return { links, taskIndex };
}

function collectProjectDependencySummary(blocks) {
  const { links, taskIndex } = collectProjectDependencyLinks(blocks);
  const linkedIds = new Set();
  links.forEach((link) => {
    linkedIds.add(link.from.id);
    linkedIds.add(link.to.id);
  });
  let taskCount = 0;
  taskIndex.forEach(() => { taskCount += 1; });
  return {
    links,
    taskIndex,
    taskCount,
    linkedCount: linkedIds.size,
    independentCount: Math.max(0, taskCount - linkedIds.size)
  };
}

function orderBlockTasksByDependency(tasks, taskIndex) {
  const blockTasks = (tasks || []).filter((t) => isLinkableDependencyType(t.type));
  if (blockTasks.length <= 1) return blockTasks;

  const ids = new Set(blockTasks.map((t) => t.id));
  const indegree = new Map(blockTasks.map((t) => [t.id, 0]));
  const adj = new Map(blockTasks.map((t) => [t.id, []]));

  blockTasks.forEach((task) => {
    const predId = task.dependsOnTaskId || (task.dependencyEnabled && task.dependencyPosition !== "before" ? task.dependencyTaskId : null);
    if (predId && ids.has(predId)) {
      adj.get(predId).push(task.id);
      indegree.set(task.id, (indegree.get(task.id) || 0) + 1);
    }
    if (task.dependencyEnabled && task.dependencyPosition === "before" && task.dependencyTaskId && ids.has(task.dependencyTaskId)) {
      adj.get(task.id).push(task.dependencyTaskId);
      indegree.set(task.dependencyTaskId, (indegree.get(task.dependencyTaskId) || 0) + 1);
    }
  });

  const queue = blockTasks.filter((t) => (indegree.get(t.id) || 0) === 0);
  const ordered = [];
  while (queue.length) {
    const current = queue.shift();
    ordered.push(current);
    (adj.get(current.id) || []).forEach((nextId) => {
      indegree.set(nextId, indegree.get(nextId) - 1);
      if (indegree.get(nextId) === 0) {
        const nextTask = blockTasks.find((t) => t.id === nextId);
        if (nextTask) queue.push(nextTask);
      }
    });
  }

  blockTasks.forEach((task) => {
    if (!ordered.some((t) => t.id === task.id)) ordered.push(task);
  });
  return ordered;
}

function ProjectDependencyOverview({ blocks, onFocusTask }) {
  const summary = collectProjectDependencySummary(blocks);
  const { links, taskCount, linkedCount, independentCount } = summary;
  const crossBlockCount = links.filter((l) => l.crossBlock).length;

  if (!taskCount) return null;

  const blockChains = (blocks || [])
    .filter((b) => b.name?.trim())
    .map((block) => ({
      id: block.id,
      name: block.name.trim(),
      orderedTasks: orderBlockTasksByDependency(block.tasks, summary.taskIndex)
    }))
    .filter((block) => block.orderedTasks.length > 0);

  return (
    <section className="project-dependency-overview" aria-label="Project task dependencies">
      <header className="project-dependency-overview__header">
        <div>
          <h3 className="project-dependency-overview__title">Task dependencies</h3>
          <p className="project-dependency-overview__subtitle">Sequence links across your whole project — edit any task to add or change.</p>
        </div>
        <div className="project-dependency-overview__stats">
          <span className="project-dependency-overview__stat">
            <strong>{links.length}</strong> link{links.length === 1 ? "" : "s"}
          </span>
          <span className="project-dependency-overview__stat">
            <strong>{linkedCount}</strong> linked
          </span>
          <span className="project-dependency-overview__stat">
            <strong>{independentCount}</strong> independent
          </span>
          {crossBlockCount > 0 ? (
            <span className="project-dependency-overview__stat project-dependency-overview__stat--accent">
              <strong>{crossBlockCount}</strong> cross-block
            </span>
          ) : null}
        </div>
      </header>

      {links.length === 0 ? (
        <div className="project-dependency-overview__empty">
          <p>No dependencies yet.</p>
          <p className="project-dependency-overview__empty-hint">Open a task or subtask → set Dependency to <strong>Yes</strong> → choose <strong>After</strong> or <strong>Before</strong> and pick a related item.</p>
        </div>
      ) : (
        <>
          <div className="project-dependency-overview__links">
            {links.map((link) => (
              <div key={link.id} className={`project-dependency-link${link.crossBlock ? " project-dependency-link--cross-block" : ""}`}>
                <button
                  type="button"
                  className="project-dependency-link__node"
                  onClick={() => onFocusTask?.(link.from.blockId, link.from.id)}
                >
                  <span className="project-dependency-link__block">{link.from.blockName}</span>
                  <span className="project-dependency-link__task">{link.from.title || "Untitled"}</span>
                </button>
                <div className="project-dependency-link__arrow" aria-hidden="true">
                  <span className="project-dependency-link__arrow-line" />
                  <span className="project-dependency-link__arrow-label">then</span>
                </div>
                <button
                  type="button"
                  className="project-dependency-link__node"
                  onClick={() => onFocusTask?.(link.to.blockId, link.to.id)}
                >
                  <span className="project-dependency-link__block">{link.to.blockName}</span>
                  <span className="project-dependency-link__task">{link.to.title || "Untitled"}</span>
                </button>
              </div>
            ))}
          </div>

          <div className="project-dependency-overview__chains">
            <p className="project-dependency-overview__chains-label">Sequence by block</p>
            <div className="project-dependency-overview__chains-grid">
              {blockChains.map((block) => (
                <div key={block.id} className="project-dependency-block-chain">
                  <p className="project-dependency-block-chain__title">{block.name}</p>
                  <div className="project-dependency-block-chain__row">
                    {block.orderedTasks.map((task, index) => (
                      <React.Fragment key={task.id}>
                        {index > 0 ? <span className="project-dependency-block-chain__sep" aria-hidden="true">→</span> : null}
                        <button
                          type="button"
                          className={`project-dependency-block-chain__chip${task.dependencyEnabled ? " project-dependency-block-chain__chip--linked" : ""}`}
                          onClick={() => onFocusTask?.(block.id, task.id)}
                          title={task.dependencyEnabled ? "Has dependency — click to locate" : "Click to locate task"}
                        >
                          {task.title || "Untitled"}
                        </button>
                      </React.Fragment>
                    ))}
                  </div>
                </div>
              ))}
            </div>
          </div>
        </>
      )}
    </section>
  );
}

function calculateEstimateBudget(task) {
  const laborTotal = (Number(task.labor?.estCount) || 0)
    * (Number(task.labor?.estDailyCost) || 0)
    * (Number(task.labor?.estDays || task.targetDays) || 1);
  const materialTotal = (task.materials || []).reduce(
    (sum, m) => sum + (Number(m.quantity) || 0) * (Number(m.estUnitCost) || 0),
    0
  );
  return Math.round(laborTotal + materialTotal);
}

function recalcTaskBudget(task) {
  return { ...task, budgetPlanned: calculateEstimateBudget(task) };
}

function deriveSimpleCostSplit(draft) {
  const days = Number(draft?.labor?.estDays || draft?.targetDays) || 1;
  const laborCost = (Number(draft?.labor?.estCount) || 0)
    * (Number(draft?.labor?.estDailyCost) || 0)
    * days;
  let materialCost = 0;
  let otherCost = 0;
  (draft?.materials || []).forEach((m) => {
    const line = (Number(m.quantity) || 0) * (Number(m.estUnitCost) || 0);
    if (m.name === "Other") otherCost += line;
    else materialCost += line;
  });
  if (laborCost <= 0 && materialCost <= 0 && otherCost <= 0 && Number(draft?.budgetPlanned) > 0) {
    const half = Math.round(Number(draft.budgetPlanned) / 2);
    return { laborCost: half, materialCost: Math.max(0, Number(draft.budgetPlanned) - half), otherCost: 0 };
  }
  return { laborCost, materialCost, otherCost };
}

function applySimpleCostSplit(draft, { laborCost, materialCost, otherCost }) {
  const days = Math.max(1, Number(draft?.targetDays) || 1);
  const labor = {
    estCount: days > 0 ? 1 : 0,
    estDailyCost: days > 0 ? Math.round((Number(laborCost) || 0) / days) : 0,
    estDays: days
  };
  const materials = [
    ...(Number(materialCost) > 0 ? [{ name: "Materials", quantity: 1, unit: "Lot", estUnitCost: Number(materialCost) }] : []),
    ...(Number(otherCost) > 0 ? [{ name: "Other", quantity: 1, unit: "Lot", estUnitCost: Number(otherCost) }] : [])
  ];
  return recalcTaskBudget({ ...draft, labor, materials });
}

function calculateBlockBudget(block) {
  const tasks = flattenBlockTasks(block);
  const tasksWithChildren = new Set(
    tasks.filter((t) => t.parentTaskId).map((t) => t.parentTaskId)
  );
  return tasks.reduce((sum, t) => {
    if (isQcType(t.type)) return sum;
    if (tasksWithChildren.has(t.id)) return sum;
    return sum + (Number(t.budgetPlanned) || 0);
  }, 0);
}

function calculateProjectBudget(form) {
  return (form.blocks || []).reduce((sum, block) => sum + calculateBlockBudget(block), 0);
}

function normalizePresetBreakdown(meta, fallbackDays = 30) {
  const days = Math.max(1, Number(fallbackDays) || 1);
  const base = emptyTaskBreakdown(days);
  const rawLabor = meta?.labor || {};
  const labor = {
    estCount: Number(rawLabor.estCount) || 0,
    estDailyCost: Number(rawLabor.estDailyCost) || 0,
    estDays: Math.max(1, Number(rawLabor.estDays || days) || days)
  };
  const materials = Array.isArray(meta?.materials)
    ? meta.materials.map((m) => ({
      name: String(m?.name || "Material"),
      quantity: Number(m?.quantity) || 0,
      unit: String(m?.unit || "Unit"),
      estUnitCost: Number(m?.estUnitCost) || 0
    }))
    : base.materials;
  return { labor, materials };
}

function defaultPresetMeta(block, presetDefaults, workType) {
  const fromMap = presetDefaults?.[workType];
  const targetDays = Number(fromMap?.targetDays) || Number(block?.targetDays) || 30;
  const breakdown = normalizePresetBreakdown(fromMap, targetDays);
  return {
    targetDays,
    budgetPlanned: Number(fromMap?.budgetPlanned) || 0,
    startDate: fromMap?.startDate || "",
    dueDate: fromMap?.dueDate || "",
    hasBreakdown: fromMap?.hasBreakdown !== false,
    labor: breakdown.labor,
    materials: breakdown.materials
  };
}

function applyPresetMetaToTask(task, meta, block) {
  const targetDays = Number(meta?.targetDays) || Number(block?.targetDays) || 30;
  const breakdown = normalizePresetBreakdown(meta, targetDays);
  let next = {
    ...task,
    targetDays,
    startDate: meta?.startDate || task.startDate || "",
    dueDate: meta?.dueDate || task.dueDate || "",
    labor: { ...breakdown.labor, estDays: Number(breakdown.labor.estDays) || targetDays },
    materials: breakdown.materials
  };
  if (meta?.hasBreakdown) {
    next = recalcTaskBudget(next);
  } else if (meta && "budgetPlanned" in meta) {
    next = applyDirectBudget(next, Number(meta.budgetPlanned) || 0);
  } else {
    next = recalcTaskBudget(next);
  }
  return next;
}

function syncPresetTasksForBlock(block, typesOfWork, presetDefaults = {}, schedule = {}) {
  const types = typesOfWork || [];
  const existing = block.tasks || [];
  const custom = existing.filter((t) => !t.isPreset);
  const presetByType = new Map(
    existing.filter((t) => t.isPreset && t.workTypeName).map((t) => [t.workTypeName, t])
  );
  const presetDates = (meta) => {
    if (meta.startDate && meta.dueDate) {
      return { startDate: meta.startDate, dueDate: meta.dueDate };
    }
    return defaultTaskDates({
      projectStart: schedule.projectStart || "",
      projectEnd: schedule.projectEnd || "",
      targetDays: meta.targetDays
    });
  };
  const presets = types.map((workType) => {
    const meta = defaultPresetMeta(block, presetDefaults, workType);
    const prev = presetByType.get(workType);
    if (prev) {
      const updated = applyPresetMetaToTask(prev, meta, block);
      if ((!updated.startDate || !updated.dueDate) && schedule.projectStart) {
        return { ...updated, ...presetDates(meta) };
      }
      return updated;
    }
    const task = createDraftTask({
      title: workType,
      type: "task",
      workTypeName: workType,
      isPreset: true,
      targetDays: meta.targetDays,
      ...presetDates(meta),
      labor: emptyTaskBreakdown(meta.targetDays).labor,
      materials: []
    });
    if (meta && "budgetPlanned" in meta) {
      task.budgetPlanned = Number(meta.budgetPlanned) || 0;
    }
    return task;
  });
  return { ...block, tasks: [...presets, ...custom] };
}

function syncPresetTasksForAllBlocks(blocks, typesOfWork, presetDefaults = {}, schedule = {}) {
  return (blocks || []).map((block) => syncPresetTasksForBlock(block, typesOfWork, presetDefaults, schedule));
}

function normalizePresetDefaults(typesOfWork = [], presetDefaults = {}, blockTargetDays = 30) {
  const next = { ...presetDefaults };
  typesOfWork.forEach((type) => {
    if (!next[type]) {
      const targetDays = blockTargetDays || 30;
      const breakdown = emptyTaskBreakdown(targetDays);
      next[type] = {
        targetDays,
        budgetPlanned: 0,
        hasBreakdown: true,
        labor: breakdown.labor,
        materials: breakdown.materials
      };
    }
  });
  Object.keys(next).forEach((key) => {
    if (!typesOfWork.includes(key)) delete next[key];
  });
  return next;
}

function inferPresetDefaultsFromBlocks(blocks, typesOfWork = []) {
  const defaults = {};
  typesOfWork.forEach((type) => {
    let found = null;
    (blocks || []).some((block) => {
      const task = (block.tasks || []).find((t) => t.isPreset && t.workTypeName === type);
      if (task) {
        found = {
          targetDays: task.targetDays || 30,
          budgetPlanned: task.budgetPlanned || 0,
          startDate: task.startDate || "",
          dueDate: task.dueDate || "",
          hasBreakdown: true,
          labor: task.labor || emptyTaskBreakdown(task.targetDays || 30).labor,
          materials: task.materials || []
        };
        return true;
      }
      return false;
    });
    if (found) {
      defaults[type] = found;
    } else {
      const breakdown = emptyTaskBreakdown(30);
      defaults[type] = {
        targetDays: 30,
        budgetPlanned: 0,
        startDate: "",
        dueDate: "",
        hasBreakdown: true,
        labor: breakdown.labor,
        materials: breakdown.materials
      };
    }
  });
  return defaults;
}

function flattenBlockTasks(block) {
  const tasks = block.tasks || [];
  const byParent = new Map();
  tasks.forEach((t) => {
    const key = t.parentTaskId || "__root__";
    if (!byParent.has(key)) byParent.set(key, []);
    byParent.get(key).push(t);
  });
  const result = [];
  const walk = (parentId, level) => {
    const key = parentId || "__root__";
    (byParent.get(key) || []).forEach((task) => {
      result.push({ ...task, level });
      walk(task.id, level + 1);
    });
  };
  walk(null, 0);
  return result;
}

function mergeImportedTasksIntoBlocks(blocks, importedRows) {
  if (!importedRows?.length) return blocks;
  return blocks.map((block) => {
    const blockName = block.name?.trim();
    const imports = importedRows.filter((row) => {
      const rowBlock = row.blockName?.trim();
      return !rowBlock || rowBlock === blockName;
    });
    if (!imports.length) return block;
    const newTasks = imports.map((row) => {
      const task = createDraftTask({
        title: row.title,
        type: row.type || "task",
        targetDays: row.targetDays || 30,
        isPreset: false
      });
      if (row.budgetPlanned) task.budgetPlanned = Number(row.budgetPlanned);
      return task;
    });
    return { ...block, tasks: [...(block.tasks || []), ...newTasks] };
  });
}

const FOUNDATION_PLAN_TAG = "foundation-plan";

function blockHasFoundationSubtasks(block, parentId) {
  return (block.tasks || []).some((task) => (
    task.parentTaskId === parentId
    || (Array.isArray(task.tags) && task.tags.includes(FOUNDATION_PLAN_TAG))
  ));
}

function draftTaskFromFoundationSubtask(subtask, parentId, predecessorId) {
  const days = Math.max(1, Number(subtask.durationDays) || calendarDaysBetween(subtask.startDate, subtask.dueDate) || 1);
  let draft = createDraftTask({
    id: subtask.id,
    title: subtask.title,
    type: "subtask",
    parentTaskId: parentId,
    workTypeName: subtask.workTypeName,
    isPreset: false,
    targetDays: days,
    startDate: subtask.startDate,
    dueDate: subtask.dueDate
  });
  draft = applySimpleCostSplit(draft, {
    laborCost: subtask.laborCost,
    materialCost: subtask.materialCost,
    otherCost: 0
  });
  if (predecessorId) {
    draft.dependencyEnabled = true;
    draft.dependencyTaskId = predecessorId;
    draft.dependencyPosition = "after";
    draft.dependsOnTaskId = predecessorId;
  }
  draft.tags = [...new Set([...(draft.tags || []), FOUNDATION_PLAN_TAG])];
  return draft;
}

function applyFoundationPlanToBlock(block, options = {}) {
  const Foundation = window.ConstructProFoundationPlan;
  if (!Foundation?.buildFoundationPlan) return block;

  const workType = Foundation.FOUNDATION_WORK_TYPE;
  const existing = block.tasks || [];
  const parentPreset = existing.find((task) => task.isPreset && task.workTypeName === workType);
  const plan = Foundation.buildFoundationPlan(options);

  const withoutFoundation = existing.filter((task) => {
    if (parentPreset && task.id === parentPreset.id) return false;
    if (parentPreset && task.parentTaskId === parentPreset.id) return false;
    if (Array.isArray(task.tags) && task.tags.includes(FOUNDATION_PLAN_TAG)) return false;
    return true;
  });

  const parentId = parentPreset?.id || plan.parent.id;
  let parent = createDraftTask({
    id: parentId,
    title: workType,
    type: "task",
    workTypeName: workType,
    isPreset: true,
    targetDays: plan.parent.targetDays,
    startDate: plan.parent.startDate,
    dueDate: plan.parent.dueDate
  });
  parent = applySimpleCostSplit(parent, {
    laborCost: plan.summary.totalLabor,
    materialCost: plan.summary.totalMaterial,
    otherCost: 0
  });

  const subtasks = [];
  plan.subtasks.forEach((subtask, index) => {
    const predecessorId = index > 0 ? plan.subtasks[index - 1].id : null;
    subtasks.push(draftTaskFromFoundationSubtask(subtask, parentId, predecessorId));
  });

  return {
    ...block,
    tasks: syncParentTaskDatesFromSubtasks(syncBlockDependencies([...withoutFoundation, parent, ...subtasks])),
    foundationPlanSummary: {
      ...plan.summary,
      materialSummary: plan.materialSummary
    }
  };
}

function applyFoundationPlanToAllBlocks(blocks, options = {}) {
  return (blocks || []).map((block) => applyFoundationPlanToBlock(block, options));
}

function foundationPresetDefaultsFromPlan(plan) {
  const workType = window.ConstructProFoundationPlan?.FOUNDATION_WORK_TYPE || "Foundation Work";
  return {
    [workType]: {
      targetDays: plan.parent.targetDays,
      budgetPlanned: plan.summary.totalCost,
      startDate: plan.parent.startDate,
      dueDate: plan.parent.dueDate,
      hasBreakdown: true,
      labor: {
        estCount: 1,
        estDailyCost: Math.round(plan.summary.totalLabor / Math.max(1, plan.parent.targetDays)),
        estDays: plan.parent.targetDays
      },
      materials: plan.summary.totalMaterial > 0
        ? [{ name: "Materials", quantity: 1, unit: "Lot", estUnitCost: plan.summary.totalMaterial }]
        : []
    }
  };
}

function WorkPlanFilterBar({ filter, onChange }) {
  const options = [
    { id: "all", label: "All types" },
    { id: "task", label: "Tasks" },
    { id: "subtask", label: "Subtasks" },
    { id: "qc", label: "QC checks" }
  ];
  return (
    <div className="work-plan-filter-bar" role="group" aria-label="Filter tasks by type">
      {options.map((opt) => (
        <button
          key={opt.id}
          type="button"
          className={`work-plan-filter-bar__pill ${filter === opt.id ? "work-plan-filter-bar__pill--active" : ""}`}
          onClick={() => onChange(opt.id)}
        >
          {opt.label}
        </button>
      ))}
    </div>
  );
}

function TaskEditorModal({ open, title, subtitle, children, onClose, wide = false, headerAction = null }) {
  React.useEffect(() => {
    if (!open) return undefined;
    const previousOverflow = document.body.style.overflow;
    document.body.style.overflow = "hidden";
    const onKeyDown = (event) => {
      if (event.key === "Escape") onClose();
    };
    window.addEventListener("keydown", onKeyDown);
    return () => {
      document.body.style.overflow = previousOverflow;
      window.removeEventListener("keydown", onKeyDown);
    };
  }, [open, onClose]);

  if (!open) return null;

  return (
    <div className="task-editor-modal-overlay project-modal-overlay" role="presentation" onClick={onClose}>
      <div className={`task-editor-modal-shell project-modal-shell ${wide ? "task-editor-modal-shell--wide" : ""}`}>
        <section
          role="dialog"
          aria-modal="true"
          aria-labelledby="task-editor-modal-title"
          className={`task-editor-modal-surface project-modal-surface ${wide ? "task-editor-modal-surface--wide" : ""}`}
          onClick={(event) => event.stopPropagation()}
        >
          <header className="project-modal-header m3-sheet-header">
            <div className="min-w-0 flex-1 pr-2">
              <h2 id="task-editor-modal-title" className="m3-sheet-header__title">{title}</h2>
              {subtitle ? <p className="m3-sheet-header__subtitle">{subtitle}</p> : null}
            </div>
            <div className="flex shrink-0 items-center gap-2">
              {headerAction}
              <button type="button" onClick={onClose} aria-label="Close" className="m3-icon-button m3-icon-button--tonal">
                <Icons.close size={24} />
              </button>
            </div>
          </header>
          <div className="task-editor-modal-body project-modal-body">{children}</div>
        </section>
      </div>
    </div>
  );
}

function SimpleCostEstimateFields({
  laborCost,
  materialCost,
  otherCost,
  onLaborChange,
  onMaterialChange,
  onOtherChange,
  idPrefix = "cost",
  variant = "inline",
  children = null
}) {
  const ff = window.ConstructProData.formatCurrency;
  const total = Math.max(0, Number(laborCost) || 0)
    + Math.max(0, Number(materialCost) || 0)
    + Math.max(0, Number(otherCost) || 0);
  const isModal = variant === "modal";

  return (
    <div className={`simple-cost-estimate${isModal ? " simple-cost-estimate--modal" : ""}`}>
      <span className={isModal ? "preset-work-type-modal__label" : "simple-cost-estimate__label"}>Estimate cost</span>
      <div className="preset-work-type-modal__cost-grid">
        <CurrencyQuickInput
          value={laborCost}
          onChange={onLaborChange}
          label="Labour cost"
          hint="e.g. 50k, 2L, 250000"
          id={`${idPrefix}-labour`}
          showChips={false}
          compact
        />
        <CurrencyQuickInput
          value={materialCost}
          onChange={onMaterialChange}
          label="Material cost"
          hint="e.g. 50k, 2L, 250000"
          id={`${idPrefix}-material`}
          showChips={false}
          compact
        />
        <CurrencyQuickInput
          value={otherCost}
          onChange={onOtherChange}
          label="Other cost"
          hint="Any additional estimate"
          id={`${idPrefix}-other`}
          showChips={false}
          compact
        />
      </div>
      <div className="preset-work-type-modal__cost-summary" aria-live="polite">
        <p>
          Estimated cost = Labour + Material + Other: <strong>{ff(total)}</strong>
        </p>
        {children}
      </div>
    </div>
  );
}

function CurrencyQuickInput({ value, onChange, label = "Estimated cost", hint, id, showChips = true, compact = false }) {
  const [text, setText] = React.useState("");
  const ff = window.ConstructProData.formatCurrency;
  const inputId = id || "currency-quick-input";

  React.useEffect(() => {
    setText(value ? String(value) : "");
  }, [value]);

  const commit = (nextValue) => {
    const amount = typeof nextValue === "number" ? nextValue : parseIndianCostInput(nextValue);
    onChange?.(amount);
    setText(amount ? String(amount) : "");
  };

  const addAmount = (delta) => commit((Number(value) || 0) + delta);

  return (
    <div className={`currency-quick-input${compact ? " currency-quick-input--compact" : ""}`}>
      <label className="currency-quick-input__field" htmlFor={inputId}>
        <span className="currency-quick-input__label">{label}</span>
        <div className="currency-quick-input__row">
          <span className="currency-quick-input__prefix" aria-hidden="true">₹</span>
          <input
            id={inputId}
            type="text"
            inputMode="decimal"
            value={text}
            onChange={(e) => setText(e.target.value)}
            onBlur={() => commit(text)}
            onKeyDown={(e) => {
              if (e.key === "Enter") {
                e.preventDefault();
                commit(text);
              }
            }}
            placeholder="e.g. 5L, 50k, 250000"
            className="currency-quick-input__input"
          />
        </div>
        {hint ? <span className="currency-quick-input__hint">{hint}</span> : (
          <span className="currency-quick-input__hint">Type 5L or 50k · {ff(value || 0)}</span>
        )}
      </label>
      {showChips ? (
        <div className="currency-quick-input__chips" role="group" aria-label="Quick cost shortcuts">
          {[
            { label: "+1K", delta: 1000 },
            { label: "+10K", delta: 10000 },
            { label: "+1L", delta: 100000 },
            { label: "+5L", delta: 500000 }
          ].map((chip) => (
            <button key={chip.label} type="button" className="currency-quick-input__chip" onClick={() => addAmount(chip.delta)}>
              {chip.label}
            </button>
          ))}
        </div>
      ) : null}
    </div>
  );
}

function PresetWorkTypeModal({ open, draft, blockCount = 1, projectStartDate = "", projectEndDate = "", onClose, onConfirm }) {
  const [targetDays, setTargetDays] = React.useState(30);
  const [laborCost, setLaborCost] = React.useState(0);
  const [materialCost, setMaterialCost] = React.useState(0);
  const [otherCost, setOtherCost] = React.useState(0);
  const [startDate, setStartDate] = React.useState("");
  const [dueDate, setDueDate] = React.useState("");
  const ff = window.ConstructProData.formatCurrency;
  const blocks = Math.max(1, Number(blockCount) || 1);
  const dateMin = projectStartDate || "";
  const dateMax = projectEndDate || "";

  React.useEffect(() => {
    if (!open || !draft) return;
    const days = Number(draft.targetDays) || 30;
    const breakdown = normalizePresetBreakdown(draft, days);
    setTargetDays(days);
    const existingLabor = (Number(breakdown.labor.estCount) || 0)
      * (Number(breakdown.labor.estDailyCost) || 0)
      * (Number(breakdown.labor.estDays || days) || 1);
    const existingMaterials = (breakdown.materials || []).reduce(
      (sum, m) => sum + (Number(m.quantity) || 0) * (Number(m.estUnitCost) || 0),
      0
    );
    if (existingLabor > 0 || existingMaterials > 0) {
      setLaborCost(existingLabor);
      setMaterialCost(existingMaterials);
      const derivedTotal = Number(draft.budgetPlanned) || 0;
      setOtherCost(Math.max(0, derivedTotal - existingLabor - existingMaterials));
    } else {
      const half = Math.round((Number(draft.budgetPlanned) || 0) / 2);
      setLaborCost(half);
      setMaterialCost(Math.max(0, (Number(draft.budgetPlanned) || 0) - half));
      setOtherCost(0);
    }
    const dates = draft.startDate && draft.dueDate
      ? { startDate: draft.startDate, dueDate: draft.dueDate }
      : defaultTaskDates({
        projectStart: projectStartDate,
        projectEnd: projectEndDate,
        targetDays: days
      });
    setStartDate(dates.startDate);
    setDueDate(dates.dueDate);
  }, [open, draft?.workType, draft?.targetDays, draft?.budgetPlanned, draft?.startDate, draft?.dueDate, projectStartDate, projectEndDate]);

  if (!open || !draft?.workType) return null;

  const isNew = draft.mode === "add";
  const title = isNew ? "Add preset work type" : "Edit preset defaults";
  const subtitle = draft.workType;

  const syncDaysFromDates = (start, end) => {
    if (start && end) setTargetDays(calendarDaysBetween(start, end));
  };

  const handleDaysChange = (days) => {
    const nextDays = Math.max(1, Number(days) || 1);
    setTargetDays(nextDays);
    if (startDate) {
      setDueDate(clampDateString(addCalendarDays(startDate, nextDays), dateMin, dateMax));
    }
  };

  const handleStartChange = (value) => {
    const start = clampDateString(value, dateMin, dateMax);
    setStartDate(start);
    if (start && dueDate && start > dueDate) {
      setDueDate(start);
      setTargetDays(1);
      return;
    }
    if (start && dueDate) syncDaysFromDates(start, dueDate);
    else if (start && targetDays) setDueDate(clampDateString(addCalendarDays(start, targetDays), dateMin, dateMax));
  };

  const handleDueChange = (value) => {
    const due = clampDateString(value, dateMin, dateMax);
    setDueDate(due);
    if (startDate && due && startDate > due) {
      setStartDate(due);
      setTargetDays(1);
      return;
    }
    if (startDate && due) syncDaysFromDates(startDate, due);
  };

  const effectiveBudget = Math.max(0, Number(laborCost) || 0)
    + Math.max(0, Number(materialCost) || 0)
    + Math.max(0, Number(otherCost) || 0);

  const submit = (event) => {
    event?.preventDefault();
    if (!startDate || !dueDate) {
      window.ConstructProData.showToast("Add start and end dates for this work type.");
      return;
    }
    if (startDate > dueDate) {
      window.ConstructProData.showToast("End date must be on or after the start date.");
      return;
    }
    onConfirm?.({
      workType: draft.workType,
      targetDays: Number(targetDays) || calendarDaysBetween(startDate, dueDate) || 30,
      budgetPlanned: Number(effectiveBudget) || 0,
      hasBreakdown: true,
      labor: {
        estCount: Number(targetDays) > 0 ? 1 : 0,
        estDailyCost: Number(targetDays) > 0 ? Math.round((Number(laborCost) || 0) / Number(targetDays)) : 0,
        estDays: Number(targetDays) || 1
      },
      materials: (Number(materialCost) > 0 || Number(otherCost) > 0)
        ? [
          ...(Number(materialCost) > 0 ? [{ name: "Materials", quantity: 1, unit: "Lot", estUnitCost: Number(materialCost) || 0 }] : []),
          ...(Number(otherCost) > 0 ? [{ name: "Other", quantity: 1, unit: "Lot", estUnitCost: Number(otherCost) || 0 }] : [])
        ]
        : [],
      startDate,
      dueDate,
      isNew
    });
  };

  return (
    <TaskEditorModal
      open={open}
      title={title}
      subtitle={subtitle}
      onClose={onClose}
    >
      <form className="preset-work-type-modal" onSubmit={submit}>
        <div className="preset-work-type-modal__hero">
          <span className="preset-work-type-modal__badge">Preset task</span>
          <p className="preset-work-type-modal__lead">
            This work type will be added as a default task under <strong>every block</strong> in your project structure.
          </p>
        </div>
        <div className="preset-work-type-modal__fields">
          <label className="preset-work-type-modal__field">
            <span className="preset-work-type-modal__label">Total days</span>
            <input
              type="number"
              min="1"
              step="1"
              value={targetDays}
              onChange={(e) => handleDaysChange(e.target.value)}
              className="preset-work-type-modal__input"
              autoFocus
              required
            />
            <span className="preset-work-type-modal__hint">Planned duration for this work package</span>
          </label>
          <div className="task-schedule-fields preset-work-type-modal__schedule">
            <span className="preset-work-type-modal__label">Schedule</span>
            <div className="task-schedule-fields__grid">
              <label className="block text-xs">
                <span className="mb-1 block font-semibold text-text-muted">Start date</span>
                <input
                  type="date"
                  value={startDate}
                  min={dateMin || undefined}
                  max={dateMax || undefined}
                  onChange={(e) => handleStartChange(e.target.value)}
                  className="preset-work-type-modal__input"
                  required
                />
              </label>
              <label className="block text-xs">
                <span className="mb-1 block font-semibold text-text-muted">End date</span>
                <input
                  type="date"
                  value={dueDate}
                  min={startDate || dateMin || undefined}
                  max={dateMax || undefined}
                  onChange={(e) => handleDueChange(e.target.value)}
                  className="preset-work-type-modal__input"
                  required
                />
              </label>
            </div>
            {(dateMin || dateMax) ? (
              <p className="task-schedule-fields__hint">Project window: {dateMin || "—"} → {dateMax || "—"}</p>
            ) : null}
          </div>
          <SimpleCostEstimateFields
            laborCost={laborCost}
            materialCost={materialCost}
            otherCost={otherCost}
            onLaborChange={setLaborCost}
            onMaterialChange={setMaterialCost}
            onOtherChange={setOtherCost}
            idPrefix="preset-work-type"
            variant="modal"
          >
            <p>
              Project total <strong>{ff((Number(effectiveBudget) || 0) * blocks)}</strong> across {blocks} block{blocks === 1 ? "" : "s"}
            </p>
          </SimpleCostEstimateFields>
        </div>
        <div className="preset-work-type-modal__footer">
          <Button type="button" variant="secondary" onClick={onClose}>Cancel</Button>
          <Button type="submit">{isNew ? "Add to project" : "Save defaults"}</Button>
        </div>
      </form>
    </TaskEditorModal>
  );
}
function getWorkItemTypeMeta(typeOrItem) {
  let type = "task";
  let isBlock = false;
  let isPhase = false;

  if (typeOrItem && typeof typeOrItem === "object") {
    type = typeOrItem.type || "task";
    if (type === "phase") {
      const isChild = Boolean(typeOrItem.parentId) || typeOrItem.level > 0 || typeOrItem.parentTaskId || typeOrItem.isPreset;
      if (isChild) {
        isPhase = true;
      } else {
        isBlock = true;
      }
    }
  } else if (typeof typeOrItem === "string") {
    type = typeOrItem;
    if (type === "phase") {
      isPhase = true;
    } else if (type === "block") {
      type = "phase";
      isBlock = true;
    }
  }

  const map = {
    block: { label: "Block", modifier: "phase", tone: "info" },
    phase: { label: "Phase", modifier: "phase", tone: "info" },
    task: { label: "Task", modifier: "task", tone: "neutral" },
    subtask: { label: "Subtask", modifier: "subtask", tone: "info" },
    qc: { label: "QC", modifier: "qc", tone: "warning" }
  };

  if (type === "phase") {
    if (isBlock) return map.block;
    return map.phase;
  }
  return map[type] || map.task;
}


function formatWorkStatus(status) {
  return window.ConstructProData?.statuses?.find((entry) => entry.id === status)?.label || status;
}

function WorkItemDraftRow({
  task,
  level = 0,
  onEdit,
  onDelete,
  onAddChild,
  projectTaskIndex,
  formatCurrency
}) {
  const ff = formatCurrency || window.ConstructProData.formatCurrency;
  const typeMeta = getWorkItemTypeMeta(task.type);
  const nested = level > 0;
  const relatedTask = task.dependencyTaskId ? projectTaskIndex?.get(task.dependencyTaskId) : null;

  return (
    <div
      className={`work-item-draft-row work-item-row--${typeMeta.modifier}${nested ? " work-item-draft-row--nested" : ""}`}
      data-level={Math.min(level, 4)}
      data-task-id={task.id}
    >
      <div className="work-item-draft-row__main">
        {nested ? <span className="work-item-tree-guide" aria-hidden="true" /> : null}
        <div className="work-item-draft-row__content">
          <div className="work-item-row__title-row">
            <span className={`work-item-type-tag work-item-type-tag--${typeMeta.modifier}`}>{typeMeta.label}</span>
            <p className="work-item-draft-row__title">{task.title || "Untitled"}</p>
            {task.isPreset ? <Badge tone="success" className="shrink-0 work-item-draft-row__preset">Preset</Badge> : null}
          </div>
          {task.dependencyEnabled && task.dependencyTaskId ? (
            <p className="work-item-draft-row__dependency">
              {task.dependencyPosition === "before" ? "Before" : "After"}{" "}
              {relatedTask ? (
                <>
                  <span className="work-item-draft-row__dependency-task">{relatedTask.title}</span>
                  {relatedTask.blockName && relatedTask.blockName !== (projectTaskIndex?.get(task.id)?.blockName) ? (
                    <span className="work-item-draft-row__dependency-block"> · {relatedTask.blockName}</span>
                  ) : null}
                </>
              ) : "linked task"}
            </p>
          ) : null}
          {task.startDate && task.dueDate && !isQcType(task.type) ? (
            <p className="work-item-draft-row__schedule">{task.startDate} → {task.dueDate}</p>
          ) : null}
        </div>
      </div>
      <div className="work-item-draft-row__budget-col" data-label="Budget">
        <span className="work-item-metric__value">{isQcType(task.type) ? "—" : ff(task.budgetPlanned || 0)}</span>
      </div>
      <div className="work-item-draft-row__days-col" data-label="Days">
        <span className="work-item-metric__value">{isQcType(task.type) ? "—" : (task.targetDays || 0)}</span>
      </div>
      <div className="work-item-draft-row__actions">
        {task.type === "task" ? (
          <RowAddControl
            label="Subtask / QC"
            compact
            ariaLabel="Add subtask or QC check under this task"
            options={[
              { label: "Subtask", onClick: () => onAddChild(task, "subtask") },
              { label: "QC check", onClick: () => onAddChild(task, "qc") }
            ]}
          />
        ) : null}
        <RowActionMenu
          onEdit={() => onEdit(task)}
          onDelete={() => onDelete(task)}
        />
      </div>
    </div>
  );
}

function workItemToDraft(item, itemById) {
  if (!item) return createDraftTask();
  const parent = item.parentId ? itemById?.get(item.parentId) : null;
  const parentTaskId = parent && parent.type === "task" ? parent.id : null;
  const start = item.startDate ? new Date(item.startDate) : null;
  const end = item.dueDate ? new Date(item.dueDate) : null;
  let targetDays = 30;
  if (start && end && !Number.isNaN(start) && !Number.isNaN(end)) {
    targetDays = Math.max(1, Math.round((end - start) / 86400000));
  }
  const predecessorId = item.dependencies?.[0] || null;
  let dependencyEnabled = Boolean(predecessorId);
  let dependencyTaskId = predecessorId;
  let dependencyPosition = "after";
  if (!predecessorId && itemById) {
    itemById.forEach((other) => {
      if (other.id === item.id) return;
      if ((other.dependencies || []).includes(item.id)) {
        dependencyEnabled = true;
        dependencyTaskId = other.id;
        dependencyPosition = "before";
      }
    });
  }
  // For parent tasks (with children), rollup.budgetPlanned is the live sum from children
  // and is always more accurate than the stored field. Prefer it so the edit form starts
  // with the correct value. Never recalculate from labor×days — that would lose the user's
  // directly-entered budget due to floating-point rounding.
  const budgetPlanned = Number(
    item.rollup?.budgetPlanned || item.budgetPlanned || 0
  );
  return stripQcScheduling({
    id: item.id,
    title: item.title || "",
    type: ["task", "subtask", "qc"].includes(item.type) ? item.type : "task",
    parentTaskId,
    workTypeName: item.workTypeName || null,
    isPreset: false,
    targetDays,
    startDate: item.startDate || "",
    dueDate: item.dueDate || "",
    budgetPlanned,
    labor: item.labor || { estCount: 15, estDailyCost: 4500, estDays: targetDays },
    materials: (item.materials || []).map((m) => ({
      name: m.name,
      quantity: m.quantity,
      unit: m.unit || "Unit",
      estUnitCost: m.estUnitCost
    })),
    progressPct: item.rollup?.progressPct ?? item.progressPct ?? 0,
    dependencyEnabled,
    dependencyTaskId,
    dependencyPosition,
    dependsOnTaskId: predecessorId
  });
}

function getBlockLinkableItemsFromTree(flatTree, blockName, blockRootNames) {
  const phase = flatTree.find((item) => item.type === "phase" && item.title === blockName && blockRootNames.has(item.title));
  if (!phase) return [];
  const inBlock = new Set([phase.id]);
  let changed = true;
  while (changed) {
    changed = false;
    flatTree.forEach((item) => {
      if (item.parentId && inBlock.has(item.parentId) && !inBlock.has(item.id)) {
        inBlock.add(item.id);
        changed = true;
      }
    });
  }
  const itemById = new Map(flatTree.map((entry) => [entry.id, entry]));
  return flatTree
    .filter((item) => inBlock.has(item.id) && isLinkableDependencyType(item.type))
    .map((item) => workItemToDraft(item, itemById));
}

function getBlockTasksFromTree(flatTree, blockName, blockRootNames) {
  const phase = flatTree.find((item) => item.type === "phase" && item.title === blockName && blockRootNames.has(item.title));
  if (!phase) return [];
  const inBlock = new Set([phase.id]);
  let changed = true;
  while (changed) {
    changed = false;
    flatTree.forEach((item) => {
      if (item.parentId && inBlock.has(item.parentId) && !inBlock.has(item.id)) {
        inBlock.add(item.id);
        changed = true;
      }
    });
  }
  return flatTree
    .filter((item) => inBlock.has(item.id) && item.type === "task")
    .map((item) => workItemToDraft(item, new Map(flatTree.map((entry) => [entry.id, entry]))));
}

function WorkItemLiveForm({
  draft,
  block,
  siblingTasks,
  projectBlocks,
  projectStartDate = "",
  projectEndDate = "",
  assigneeRole,
  onChange,
  onMetaChange,
  onSave,
  onCancel,
  saveLabel = "Save task"
}) {
  const updateMeta = (key, value) => onMetaChange?.({ assigneeRole, [key]: value });

  const handleSave = () => {
    if (!draft.title?.trim()) return;
    const payload = isQcType(draft.type)
      ? stripQcScheduling(draft)
      : applySimpleCostSplit(draft, deriveSimpleCostSplit(draft));
    if (!isQcType(draft.type)) {
      const scheduleError = validateTaskSchedule(payload, siblingTasks, projectStartDate, projectEndDate);
      if (scheduleError) {
        window.ConstructProData.showToast(scheduleError);
        return;
      }
    }
    onChange?.(payload);
    onSave?.();
  };

  return (
    <div className="space-y-3">
      <WorkItemDraftForm
        draft={draft}
        block={block}
        siblingTasks={siblingTasks}
        projectBlocks={projectBlocks}
        projectStartDate={projectStartDate}
        projectEndDate={projectEndDate}
        onChange={onChange}
        onSave={() => { }}
        onCancel={onCancel}
        hideActions
        embedded
      />
      <div className="work-item-live-meta rounded-xl border border-border-light bg-white p-4">
        <label className="block text-xs">
          <span className="mb-1 block font-bold uppercase tracking-wider text-text-muted">Assignee</span>
          <select value={assigneeRole} onChange={(e) => updateMeta("assigneeRole", e.target.value)} className="w-full rounded-lg border border-border-light px-3 py-2 text-sm">
            {window.ConstructProData.roles.map((role) => <option key={role} value={role}>{role}</option>)}
          </select>
        </label>
      </div>
      <div className="flex justify-end gap-2">
        <Button type="button" variant="secondary" onClick={onCancel}>Cancel</Button>
        <Button type="button" onClick={handleSave} disabled={!draft.title?.trim()}>{saveLabel}</Button>
      </div>
    </div>
  );
}

function WorkItemDraftForm({
  draft,
  block,
  siblingTasks,
  projectBlocks,
  projectStartDate = "",
  projectEndDate = "",
  onChange,
  onSave,
  onCancel,
  saveLabel = "Save task",
  hideActions = false,
  embedded = false
}) {
  const ff = window.ConstructProData.formatCurrency;
  const parentOptions = (siblingTasks || []).filter((t) => t.type === "task" && t.id !== draft.id);
  const { min: dateMin, max: dateMax, parentTask } = getTaskDateBounds(draft, siblingTasks, projectStartDate, projectEndDate);
  const dependencyBlockOptions = React.useMemo(() => {
    const source = projectBlocks?.length
      ? projectBlocks
      : [{ id: block.id, name: block.name, tasks: siblingTasks || [] }];
    return source
      .map((entry) => ({
        id: entry.id,
        name: entry.name?.trim() || "Block",
        tasks: (entry.tasks || []).filter((t) => isLinkableDependencyType(t.type) && t.id !== draft.id)
      }))
      .filter((entry) => entry.tasks.length > 0);
  }, [projectBlocks, block.id, block.name, siblingTasks, draft.id]);
  const dependencyOptions = dependencyBlockOptions.flatMap((entry) => entry.tasks);
  const isQc = isQcType(draft.type);
  const isSubtask = draft.type === "subtask";
  const costSplit = deriveSimpleCostSplit(draft);
  const applyCostPatch = (patch) => onChange(applySimpleCostSplit(draft, { ...costSplit, ...patch }));
  const siblingSubtaskTotal = isSubtask && draft.parentTaskId
    ? (siblingTasks || [])
      .filter((t) => t.type === "subtask" && t.parentTaskId === draft.parentTaskId && t.id !== draft.id)
      .reduce((sum, t) => sum + (Number(t.budgetPlanned) || 0), 0)
    : 0;
  const parentBudget = parentTask ? (Number(parentTask.budgetPlanned) || 0) : 0;
  const parentBudgetRemaining = parentBudget - siblingSubtaskTotal;
  const childSubtaskTotal = !isSubtask && draft.type === "task"
    ? (siblingTasks || [])
      .filter((t) => t.type === "subtask" && t.parentTaskId === draft.id)
      .reduce((sum, t) => sum + (Number(t.budgetPlanned) || 0), 0)
    : 0;

  const update = (key, value) => {
    let next = { ...draft, [key]: value };
    if (key === "type" && value === "qc") {
      onChange(stripQcScheduling({ ...next, type: "qc" }));
      return;
    }
    if (key === "type" && value !== "qc" && isQcType(draft.type)) {
      const nextParent = next.parentTaskId
        ? (siblingTasks || []).find((task) => task.id === next.parentTaskId)
        : null;
      const dates = defaultTaskDates({
        projectStart: projectStartDate,
        projectEnd: projectEndDate,
        parentTask: nextParent,
        targetDays: 30
      });
      next = {
        ...next,
        type: value,
        targetDays: dates.startDate && dates.dueDate ? calendarDaysBetween(dates.startDate, dates.dueDate) : 30,
        startDate: dates.startDate,
        dueDate: dates.dueDate,
        labor: emptyTaskBreakdown(30).labor,
        materials: []
      };
    }
    if (isQcType(next.type)) {
      onChange(stripQcScheduling(next));
      return;
    }
    if (key === "budgetPlanned") {
      onChange(applyDirectBudget(next, Number(value) || 0));
      return;
    }
    if (key === "type" && value === "task") next.parentTaskId = null;
    if (key === "parentTaskId") {
      const nextParent = value ? (siblingTasks || []).find((task) => task.id === value) : null;
      const bounds = getTaskDateBounds({ ...next, parentTaskId: value }, siblingTasks, projectStartDate, projectEndDate);
      const dates = defaultTaskDates({
        projectStart: projectStartDate,
        projectEnd: projectEndDate,
        parentTask: nextParent,
        targetDays: next.targetDays
      });
      next.startDate = clampDateString(dates.startDate, bounds.min, bounds.max);
      next.dueDate = clampDateString(dates.dueDate, bounds.min, bounds.max);
      if (next.startDate && next.dueDate) {
        next.targetDays = calendarDaysBetween(next.startDate, next.dueDate);
        next.labor = { ...next.labor, estDays: next.targetDays };
      }
    }
    if (key === "targetDays") {
      const days = Math.max(1, Number(value) || 1);
      next.targetDays = days;
      next.labor = { ...next.labor, estDays: days };
      if (next.startDate) {
        next.dueDate = clampDateString(addCalendarDays(next.startDate, days), dateMin, dateMax);
      }
    }
    if (key === "startDate") {
      const start = clampDateString(value, dateMin, dateMax);
      next.startDate = start;
      if (start && next.dueDate && start > next.dueDate) {
        next.dueDate = start;
      }
      if (start && next.dueDate) {
        next.targetDays = calendarDaysBetween(start, next.dueDate);
        next.labor = { ...next.labor, estDays: next.targetDays };
      } else if (start && next.targetDays) {
        next.dueDate = clampDateString(addCalendarDays(start, next.targetDays), dateMin, dateMax);
      }
    }
    if (key === "dueDate") {
      const due = clampDateString(value, dateMin, dateMax);
      next.dueDate = due;
      if (next.startDate && due && next.startDate > due) {
        next.startDate = due;
      }
      if (next.startDate && due) {
        next.targetDays = calendarDaysBetween(next.startDate, due);
        next.labor = { ...next.labor, estDays: next.targetDays };
      }
    }
    if (["task", "subtask"].includes(next.type) && ["targetDays", "startDate", "dueDate", "parentTaskId"].includes(key)) {
      const split = deriveSimpleCostSplit(draft);
      next = applySimpleCostSplit(next, split);
    }
    const scheduleKeys = new Set(["startDate", "dueDate", "targetDays", "parentTaskId", "budgetPlanned"]);
    if (!scheduleKeys.has(key) && !["task", "subtask"].includes(next.type)) {
      next.budgetPlanned = calculateEstimateBudget(next);
    }
    onChange(next);
  };

  const handleSave = () => {
    if (!draft.title?.trim()) return;
    let payload = isQc ? stripQcScheduling(draft) : applySimpleCostSplit(draft, deriveSimpleCostSplit(draft));
    if (!isQc) {
      const scheduleError = validateTaskSchedule(payload, siblingTasks, projectStartDate, projectEndDate);
      if (scheduleError) {
        window.ConstructProData.showToast(scheduleError);
        return;
      }
    }
    onSave(payload);
  };

  const setDependencyEnabled = (enabled) => {
    onChange({
      ...draft,
      dependencyEnabled: enabled,
      dependencyTaskId: enabled ? draft.dependencyTaskId : null,
      dependencyPosition: draft.dependencyPosition || "after"
    });
  };

  const setDependencyPosition = (position) => {
    onChange({ ...draft, dependencyPosition: position });
  };

  const setDependencyTaskId = (taskId) => {
    onChange({ ...draft, dependencyTaskId: taskId || null, dependencyEnabled: true });
  };

  const relatedTask = dependencyOptions.find((t) => t.id === draft.dependencyTaskId)
    || buildProjectTaskIndex(projectBlocks || [{ id: block.id, name: block.name, tasks: siblingTasks }]).get(draft.dependencyTaskId);
  const dependencyHint = draft.dependencyPosition === "before"
    ? "The selected task starts after this one finishes."
    : "This task starts after the selected task finishes.";

  return (
    <div className={`work-item-draft-form ${embedded ? "work-item-draft-form--embedded" : "rounded-xl border border-border-light bg-white p-4"}`}>
      <div className="mb-3 flex flex-wrap items-center gap-2">
        <span className="block-context-chip">{block.name}{block.floor ? ` · ${block.floor}` : ""}</span>
        {draft.parentTaskId ? (
          <span className="block-context-chip block-context-chip--parent">
            Under: {(siblingTasks || []).find((t) => t.id === draft.parentTaskId)?.title || "Parent"}
          </span>
        ) : null}
      </div>

      <div className="mb-3 flex gap-1 rounded-lg border border-border-light p-1" role="group" aria-label="Task type">
        {["task", "subtask", "qc"].map((type) => (
          <button
            key={type}
            type="button"
            className={`task-type-segment flex-1 rounded-md px-2 py-2 text-xs font-bold uppercase ${draft.type === type ? "task-type-segment--active" : ""}`}
            onClick={() => update("type", type)}
            disabled={type !== "task" && !draft.parentTaskId && parentOptions.length === 0}
          >
            {type}
          </button>
        ))}
      </div>

      {(draft.type === "subtask" || draft.type === "qc") && parentOptions.length > 0 ? (
        <label className="mb-3 block">
          <span className="mb-1 block text-xs font-bold uppercase tracking-wider text-text-muted">Parent task</span>
          <select
            value={draft.parentTaskId || ""}
            onChange={(e) => update("parentTaskId", e.target.value || null)}
            className="w-full rounded-lg border border-border-light px-3 py-2 text-sm"
          >
            <option value="">Select parent</option>
            {parentOptions.map((t) => <option key={t.id} value={t.id}>{t.title}</option>)}
          </select>
        </label>
      ) : null}

      <label className="mb-3 block">
        <span className="mb-1 block text-xs font-bold uppercase tracking-wider text-text-muted">{isQc ? "QC check name" : "Task name"}</span>
        <input
          type="text"
          value={draft.title}
          onChange={(e) => update("title", e.target.value)}
          placeholder={isQc ? "e.g. Concrete strength test" : "e.g. Foundation Work"}
          className="w-full rounded-lg border border-border-light px-3 py-2 text-sm"
          required
        />
      </label>

      {isQc ? (
        <p className="mb-3 rounded-lg border border-border-light bg-surface-container-low px-3 py-2 text-xs text-text-muted">
          QC checks track quality only — no schedule or cost.
        </p>
      ) : (
        <>
          <div className="task-quick-fields mb-3">
            <label className="task-quick-fields__field">
              <span className="mb-1 block text-xs font-bold uppercase tracking-wider text-text-muted">Total days</span>
              <input
                type="number"
                inputMode="numeric"
                min="1"
                value={draft.targetDays}
                onChange={(e) => update("targetDays", Number(e.target.value))}
                className="w-full rounded-lg border border-border-light px-3 py-2 text-sm"
              />
            </label>
          </div>

          <div className="task-schedule-fields mb-3">
            <span className="mb-2 block text-xs font-bold uppercase tracking-wider text-text-muted">Schedule</span>
            <div className="task-schedule-fields__grid">
              <label className="block text-xs">
                <span className="mb-1 block font-semibold text-text-muted">Start date</span>
                <input
                  type="date"
                  value={draft.startDate || ""}
                  min={dateMin || undefined}
                  max={dateMax || undefined}
                  onChange={(e) => update("startDate", e.target.value)}
                  className="w-full rounded-lg border border-border-light px-3 py-2 text-sm"
                  required
                />
              </label>
              <label className="block text-xs">
                <span className="mb-1 block font-semibold text-text-muted">End date</span>
                <input
                  type="date"
                  value={draft.dueDate || ""}
                  min={draft.startDate || dateMin || undefined}
                  max={dateMax || undefined}
                  onChange={(e) => update("dueDate", e.target.value)}
                  className="w-full rounded-lg border border-border-light px-3 py-2 text-sm"
                  required
                />
              </label>
            </div>
            {parentTask ? (
              <p className="task-schedule-fields__hint">
                Subtask dates must stay within parent task: {parentTask.startDate || "—"} → {parentTask.dueDate || "—"}
              </p>
            ) : (dateMin || dateMax) ? (
              <p className="task-schedule-fields__hint">
                Project window: {dateMin || "—"} → {dateMax || "—"}
              </p>
            ) : null}
          </div>

          {!isQc ? (
            <div className="mb-3">
              <SimpleCostEstimateFields
                laborCost={costSplit.laborCost}
                materialCost={costSplit.materialCost}
                otherCost={costSplit.otherCost}
                onLaborChange={(amount) => applyCostPatch({ laborCost: amount })}
                onMaterialChange={(amount) => applyCostPatch({ materialCost: amount })}
                onOtherChange={(amount) => applyCostPatch({ otherCost: amount })}
                idPrefix={`${draft.type}-cost-${draft.id}`}
              >
                {isSubtask && parentTask ? (
                  <p>
                    Parent task budget <strong>{ff(parentBudget)}</strong>
                    {" · "}Other subtasks <strong>{ff(siblingSubtaskTotal)}</strong>
                    {" · "}Available <strong>{ff(parentBudgetRemaining)}</strong>
                  </p>
                ) : null}
                {!isSubtask && childSubtaskTotal > 0 ? (
                  <p>
                    Subtasks already total <strong>{ff(childSubtaskTotal)}</strong>
                    {" · "}Use parent cost as envelope only, or keep at <strong>₹0</strong> and price subtasks.
                  </p>
                ) : null}
              </SimpleCostEstimateFields>
            </div>
          ) : null}
        </>
      )}

      {supportsDependencies(draft.type) ? (
        <div className="task-dependency-panel mb-3">
          <span className="mb-2 block text-xs font-bold uppercase tracking-wider text-text-muted">
            Dependency{draft.type === "subtask" ? " (subtask sequence)" : ""}
          </span>
          <div className="task-dependency-panel__row">
            <div className="task-dependency-panel__toggle flex gap-1 rounded-lg border border-border-light p-1" role="group" aria-label="Has dependency">
              <button
                type="button"
                className={`task-type-segment flex-1 rounded-md px-3 py-2 text-xs font-bold uppercase ${!draft.dependencyEnabled ? "task-type-segment--active" : ""}`}
                onClick={() => setDependencyEnabled(false)}
              >
                No
              </button>
              <button
                type="button"
                className={`task-type-segment flex-1 rounded-md px-3 py-2 text-xs font-bold uppercase ${draft.dependencyEnabled ? "task-type-segment--active" : ""}`}
                onClick={() => setDependencyEnabled(true)}
              >
                Yes
              </button>
            </div>
          </div>
          {draft.dependencyEnabled ? (
            <div className="task-dependency-panel__config">
              <div className="task-dependency-panel__timing flex gap-1 rounded-lg border border-border-light p-1" role="group" aria-label="Dependency timing">
                <button
                  type="button"
                  className={`task-type-segment flex-1 rounded-md px-2 py-2 text-xs font-bold uppercase ${draft.dependencyPosition !== "before" ? "task-type-segment--active" : ""}`}
                  onClick={() => setDependencyPosition("after")}
                >
                  After
                </button>
                <button
                  type="button"
                  className={`task-type-segment flex-1 rounded-md px-2 py-2 text-xs font-bold uppercase ${draft.dependencyPosition === "before" ? "task-type-segment--active" : ""}`}
                  onClick={() => setDependencyPosition("before")}
                >
                  Before
                </button>
              </div>
              <label className="task-dependency-panel__select block">
                <span className="mb-1 block text-xs font-semibold text-text-muted">Related task</span>
                <select
                  value={draft.dependencyTaskId || ""}
                  onChange={(e) => setDependencyTaskId(e.target.value || null)}
                  className="w-full rounded-lg border border-border-light px-3 py-2 text-sm"
                  required={draft.dependencyEnabled}
                >
                  <option value="">Select task or subtask</option>
                  {dependencyBlockOptions.map((entry) => (
                    <optgroup key={entry.id} label={entry.name}>
                      {entry.tasks.map((t) => (
                        <option key={t.id} value={t.id}>{formatDependencyOptionLabel(t)}</option>
                      ))}
                    </optgroup>
                  ))}
                </select>
              </label>
              {relatedTask ? (
                <p className="task-dependency-panel__hint">
                  {draft.dependencyPosition === "before"
                    ? <>Runs <strong>before</strong> {relatedTask.title}{relatedTask.blockName && relatedTask.blockName !== block.name?.trim() ? ` (${relatedTask.blockName})` : ""}</>
                    : <>Runs <strong>after</strong> {relatedTask.title}{relatedTask.blockName && relatedTask.blockName !== block.name?.trim() ? ` (${relatedTask.blockName})` : ""}</>}
                </p>
              ) : dependencyOptions.length === 0 ? (
                <p className="task-dependency-panel__hint">Add another task or subtask in this project to link dependencies.</p>
              ) : (
                <p className="task-dependency-panel__hint">{dependencyHint}</p>
              )}
            </div>
          ) : null}
        </div>
      ) : null}

      {!hideActions ? (
        <div className="flex justify-end gap-2">
          <Button type="button" variant="secondary" onClick={onCancel}>Cancel</Button>
          <Button type="button" onClick={handleSave} disabled={!draft.title?.trim()}>{saveLabel}</Button>
        </div>
      ) : null}
    </div>
  );
}

function BlockWorkPlanPanel({
  block,
  blockIndex = 0,
  filter = "all",
  projectBlocks,
  projectStartDate = "",
  projectEndDate = "",
  onUpdateTasks,
  onUpdateBlock,
  onRemoveBlock,
  canRemoveBlock = true,
  collapsed = false,
  onToggleCollapse
}) {
  const [editing, setEditing] = React.useState(null);
  const flat = flattenBlockTasks(block);
  const visible = flat.filter((t) => filter === "all" || t.type === filter);
  const ff = window.ConstructProData.formatCurrency;
  const blockBudget = calculateBlockBudget(block);
  const taskCount = flat.length;
  const projectTaskIndex = React.useMemo(
    () => buildProjectTaskIndex(projectBlocks || [block]),
    [projectBlocks, block]
  );

  const saveTask = (task) => {
    let normalized = isQcType(task.type)
      ? stripQcScheduling(task)
      : applySimpleCostSplit(task, deriveSimpleCostSplit(task));
    if (normalized.dependencyEnabled && !normalized.dependencyTaskId) {
      normalized = { ...normalized, dependencyEnabled: false, dependencyTaskId: null };
    }
    let tasks = (block.tasks || []).map((t) => (t.id === normalized.id ? normalized : t));
    if (!tasks.some((t) => t.id === normalized.id)) tasks.push(normalized);
    if (normalized.type === "task" && !isQcType(normalized.type)) {
      const hasSubtasks = tasks.some((t) => t.parentTaskId === normalized.id && t.type === "subtask");
      if (hasSubtasks) {
        tasks = cascadeParentBudgetToSubtasks(tasks, normalized.id, normalized.budgetPlanned);
      }
    }
    tasks = syncParentTaskDatesFromSubtasks(tasks);
    onUpdateTasks(tasks);
    setEditing(null);
  };

  const deleteTask = (task) => {
    const removeIds = new Set([task.id]);
    let changed = true;
    while (changed) {
      changed = false;
      (block.tasks || []).forEach((t) => {
        if (removeIds.has(t.parentTaskId) && !removeIds.has(t.id)) {
          removeIds.add(t.id);
          changed = true;
        }
      });
    }
    onUpdateTasks(syncParentTaskDatesFromSubtasks((block.tasks || []).filter((t) => !removeIds.has(t.id))));
  };

  const openAdd = (parentTask, type = "task") => {
    const parentRecord = parentTask
      ? (block.tasks || []).find((task) => task.id === parentTask.id)
      : null;
    const isQc = type === "qc";
    const targetDays = parentRecord
      ? Math.max(1, calendarDaysBetween(parentRecord.startDate, parentRecord.dueDate) || 7)
      : (block.targetDays || 30);
    const dates = defaultTaskDates({
      projectStart: projectStartDate,
      projectEnd: projectEndDate,
      parentTask: parentRecord,
      targetDays
    });
    const draft = createDraftTask({
      type: parentTask ? (type || "subtask") : "task",
      parentTaskId: parentTask?.id || null,
      ...(isQc ? {
        targetDays: 0,
        budgetPlanned: 0,
        startDate: "",
        dueDate: "",
        labor: emptyQcBreakdown().labor,
        materials: []
      } : {
        targetDays,
        startDate: dates.startDate,
        dueDate: dates.dueDate
      })
    });
    setEditing({ mode: "add", draft: isQc ? stripQcScheduling(draft) : draft });
  };

  const stopHeaderInput = (event) => event.stopPropagation();

  return (
    <div className="block-work-plan-panel block-work-plan-panel--v2" id={`block-panel-${block.id}`}>
      <div className="block-work-plan-panel__header">
        <div className="block-work-plan-panel__header-fields">
          <label className="block-work-plan-panel__field block-work-plan-panel__field--name">
            <span className="block-work-plan-panel__field-label">Block name</span>
            <input
              type="text"
              value={block.name}
              onChange={(e) => onUpdateBlock?.("name", e.target.value)}
              onClick={stopHeaderInput}
              placeholder={`Block ${String.fromCharCode(65 + blockIndex)}`}
              className="block-work-plan-panel__input"
            />
          </label>
          <label className="block-work-plan-panel__field block-work-plan-panel__field--floor">
            <span className="block-work-plan-panel__field-label">Floor</span>
            <input
              type="text"
              value={block.floor || ""}
              onChange={(e) => onUpdateBlock?.("floor", e.target.value)}
              onClick={stopHeaderInput}
              placeholder="e.g. Ground to G+4"
              className="block-work-plan-panel__input"
            />
          </label>
          <label className="block-work-plan-panel__field block-work-plan-panel__field--days">
            <span className="block-work-plan-panel__field-label">Target days</span>
            <input
              type="number"
              value={block.targetDays}
              onChange={(e) => onUpdateBlock?.("targetDays", Number(e.target.value))}
              onClick={stopHeaderInput}
              className="block-work-plan-panel__input"
            />
          </label>
        </div>
        <div className="block-work-plan-panel__header-stats">
          <span className="block-work-plan-panel__stat">
            <span className="block-work-plan-panel__stat-value">{taskCount}</span>
            <span className="block-work-plan-panel__stat-label">tasks</span>
          </span>
          <span className="block-work-plan-panel__stat">
            <span className="block-work-plan-panel__stat-value">{ff(blockBudget)}</span>
            <span className="block-work-plan-panel__stat-label">budget</span>
          </span>
        </div>
        <div className="block-work-plan-panel__header-actions">
          <Button type="button" variant="secondary" onClick={() => openAdd(null, "task")}>
            <Icons.plus size={18} /> Task
          </Button>
          {canRemoveBlock ? (
            <button
              type="button"
              className="block-work-plan-panel__remove"
              onClick={() => onRemoveBlock?.()}
              title="Remove block"
              aria-label="Remove block"
            >
              <Icons.trash size={18} />
            </button>
          ) : null}
          <button
            type="button"
            className="block-work-plan-panel__collapse"
            onClick={onToggleCollapse}
            aria-expanded={!collapsed}
            aria-label={collapsed ? "Expand block" : "Collapse block"}
          >
            {collapsed ? "›" : "▾"}
          </button>
        </div>
      </div>
      {!collapsed ? (
        <div className="block-work-plan-panel__body">
          {visible.length === 0 ? (
            <div className="block-work-plan-panel__empty">
              <p>No tasks yet.</p>
              <p className="block-work-plan-panel__empty-hint">Select work types above or add a task.</p>
              <Button type="button" variant="secondary" onClick={() => openAdd(null, "task")}>
                <Icons.plus size={18} /> Add first task
              </Button>
            </div>
          ) : (
            <>
              <div className="block-work-plan-panel__task-head">
                <span>Task</span>
                <span>Budget</span>
                <span>Days</span>
                <span className="sr-only">Actions</span>
              </div>
              <div className="block-work-plan-panel__task-table">
                {visible.map((task) => (
                  <WorkItemDraftRow
                    key={task.id}
                    task={task}
                    level={task.level}
                    projectTaskIndex={projectTaskIndex}
                    onEdit={(t) => setEditing({ mode: "edit", draft: { ...t } })}
                    onDelete={deleteTask}
                    onAddChild={(t, childType) => openAdd(t, childType)}
                  />
                ))}
              </div>
            </>
          )}
          <TaskEditorModal
            open={Boolean(editing)}
            title={editing?.mode === "edit" ? "Edit task" : "Add task"}
            subtitle={`${block.name || "Block"}${block.floor ? ` · ${block.floor}` : ""}`}
            onClose={() => setEditing(null)}
          >
            {editing ? (
              <WorkItemDraftForm
                draft={editing.draft}
                block={block}
                siblingTasks={block.tasks || []}
                projectBlocks={projectBlocks || [block]}
                projectStartDate={projectStartDate}
                projectEndDate={projectEndDate}
                onChange={(draft) => setEditing((c) => ({ ...c, draft }))}
                onSave={(task) => saveTask(task)}
                onCancel={() => setEditing(null)}
                saveLabel={editing.mode === "edit" ? "Update task" : "Add task"}
                embedded
              />
            ) : null}
          </TaskEditorModal>
        </div>
      ) : null}
    </div>
  );
}

function WizardBudgetSummary({ form }) {
  const ff = window.ConstructProData.formatCurrency;
  const total = calculateProjectBudget(form);
  return (
    <div className="wizard-budget-summary rounded-xl border border-primary/30 bg-primary-fixed/30 p-4" aria-live="polite">
      <div className="flex flex-wrap items-center justify-between gap-2">
        <span className="text-xs font-bold uppercase tracking-wider text-text-muted">Total estimated budget</span>
        <span className="text-xl font-bold text-primary">{ff(total)}</span>
      </div>
      <div className="mt-2 flex flex-wrap gap-3">
        {(form.blocks || []).filter((b) => b.name?.trim()).map((block) => (
          <span key={block.id} className="text-xs text-text-muted">
            {block.name}: <b className="text-text-primary">{ff(calculateBlockBudget(block))}</b>
          </span>
        ))}
      </div>
    </div>
  );
}

function FoundationPlanPanel({ config, location, projectStartDate, onChange, onApply }) {
  const Foundation = window.ConstructProFoundationPlan;
  if (!Foundation) return null;

  const ff = window.ConstructProData.formatCurrency;
  const preview = Foundation.buildFoundationPlan({
    ...config,
    location,
    startDate: projectStartDate || config.startDate
  });

  const update = (key, value) => onChange({ ...config, [key]: value });

  return (
    <div className="rounded-xl border border-primary/20 bg-primary-fixed/30 p-5">
      <div className="mb-4 flex flex-wrap items-start justify-between gap-3">
        <div>
          <h3 className="text-sm font-bold text-text-primary">Foundation BOQ & Schedule</h3>
          <p className="mt-1 text-xs text-text-muted">
            16 sequential subtasks with material and labour split — scaled to site inputs below.
          </p>
        </div>
        <Button type="button" onClick={onApply}>
          {config.applied ? "Update foundation plan" : "Apply foundation plan"}
        </Button>
      </div>
      <div className="grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-3">
        <label>
          <span className="mb-1 block text-xs font-bold uppercase tracking-wider text-text-muted">Foundation type</span>
          <select
            value={config.foundationType}
            onChange={(e) => update("foundationType", e.target.value)}
            className="w-full rounded-lg border border-border-light px-3 py-2 text-sm"
          >
            {Foundation.FOUNDATION_TYPES.map((type) => (
              <option key={type.id} value={type.id}>{type.label}</option>
            ))}
          </select>
        </label>
        <label>
          <span className="mb-1 block text-xs font-bold uppercase tracking-wider text-text-muted">Columns (optional)</span>
          <input
            type="number"
            min="4"
            step="1"
            value={config.columnCount}
            onChange={(e) => update("columnCount", Number(e.target.value) || 8)}
            className="w-full rounded-lg border border-border-light px-3 py-2 text-sm"
            placeholder="e.g. 8"
          />
        </label>
        <label>
          <span className="mb-1 block text-xs font-bold uppercase tracking-wider text-text-muted">Foundation start</span>
          <input
            type="date"
            value={projectStartDate || config.startDate || ""}
            onChange={(e) => update("startDate", e.target.value)}
            className="w-full rounded-lg border border-border-light px-3 py-2 text-sm"
            disabled={Boolean(projectStartDate)}
          />
        </label>
      </div>
      <div className="mt-4 grid grid-cols-2 gap-3 md:grid-cols-4">
        <div className="rounded-lg border border-border-light bg-white px-3 py-2">
          <p className="text-[10px] font-bold uppercase tracking-wider text-text-muted">Material</p>
          <p className="text-sm font-bold text-text-primary">{ff(preview.summary.totalMaterial)}</p>
        </div>
        <div className="rounded-lg border border-border-light bg-white px-3 py-2">
          <p className="text-[10px] font-bold uppercase tracking-wider text-text-muted">Labour</p>
          <p className="text-sm font-bold text-text-primary">{ff(preview.summary.totalLabor)}</p>
        </div>
        <div className="rounded-lg border border-border-light bg-white px-3 py-2">
          <p className="text-[10px] font-bold uppercase tracking-wider text-text-muted">Total</p>
          <p className="text-sm font-bold text-primary">{ff(preview.summary.totalCost)}</p>
        </div>
        <div className="rounded-lg border border-border-light bg-white px-3 py-2">
          <p className="text-[10px] font-bold uppercase tracking-wider text-text-muted">Duration</p>
          <p className="text-sm font-bold text-text-primary">{preview.summary.totalDays} days</p>
        </div>
      </div>
      <details className="mt-4 rounded-lg border border-border-light bg-white/80 px-4 py-3">
        <summary className="cursor-pointer text-xs font-bold uppercase tracking-wider text-text-muted">Material summary (approx.)</summary>
        <ul className="mt-3 grid grid-cols-1 gap-2 text-sm md:grid-cols-2">
          {preview.materialSummary.map((row) => (
            <li key={row.name} className="flex justify-between gap-2 border-b border-border-light/60 pb-1">
              <span className="text-text-muted">{row.name}</span>
              <span className="font-semibold text-text-primary">{row.quantity}{row.unit ? ` ${row.unit}` : ""}</span>
            </li>
          ))}
        </ul>
      </details>
    </div>
  );
}

function WorkTypeChipPicker({
  presets,
  selected,
  onChange,
  onOpenPresetModal,
  onClearAll,
  customType,
  setCustomType,
  onAddCustom,
  defaultTargetDays = 30,
  projectStartDate = "",
  projectEndDate = ""
}) {
  const removeType = (type) => {
    onChange(selected.filter((t) => t !== type));
  };

  const openAddPreset = (workType) => {
    const breakdown = emptyTaskBreakdown(defaultTargetDays);
    const dates = defaultTaskDates({
      projectStart: projectStartDate,
      projectEnd: projectEndDate,
      targetDays: defaultTargetDays
    });
    onOpenPresetModal?.({
      mode: "add",
      workType,
      targetDays: defaultTargetDays,
      budgetPlanned: 0,
      hasBreakdown: true,
      labor: breakdown.labor,
      materials: breakdown.materials,
      startDate: dates.startDate,
      dueDate: dates.dueDate
    });
  };

  return (
    <fieldset className="rounded-xl border border-border-light bg-white p-5">
      <legend className="sr-only">Types of work</legend>
      <div className="mb-5">
        <h3 className="text-xs font-bold uppercase tracking-wider text-text-muted">Types of Work</h3>
        <p className="mt-1 text-sm text-text-muted">Tap a preset to set days, labor, and materials — each type seeds a task under every block.</p>
      </div>
      {presets.filter((p) => !selected.includes(p)).length > 0 ? (
        <div className="mb-4 rounded-xl border border-border-light bg-surface-container-low p-4">
          <div className="mb-3 flex flex-wrap items-center justify-between gap-2">
            <span className="text-xs font-bold uppercase tracking-wider text-text-muted">Add from presets</span>
            <button
              type="button"
              onClick={() => onChange([...new Set([...selected, ...presets])])}
              className="text-xs font-semibold text-primary hover:underline"
              title="Adds all presets with 30-day / ₹0 defaults — edit each after"
            >
              Add all (defaults)
            </button>
          </div>
          <div className="work-type-chip-row">
            {presets.filter((p) => !selected.includes(p)).map((preset) => (
              <button key={preset} type="button" onClick={() => openAddPreset(preset)} className="work-type-chip work-type-chip--add">
                <span aria-hidden="true">+</span>
                <span className="work-type-chip__label">{preset}</span>
              </button>
            ))}
          </div>
        </div>
      ) : null}
      <div className="mb-4 rounded-xl border border-dashed border-border-light p-4">
        <label htmlFor="work-type-custom" className="mb-2 block text-xs font-bold uppercase tracking-wider text-text-muted">Custom type</label>
        <div className="flex gap-2">
          <input id="work-type-custom" type="text" placeholder="e.g. HVAC Installation" value={customType} onChange={(e) => setCustomType(e.target.value)} onKeyDown={(e) => e.key === "Enter" && onAddCustom(e)} className="flex-1 rounded-lg border border-border-light px-3 py-2 text-sm" />
          <Button type="button" variant="secondary" onClick={() => onAddCustom()} disabled={!customType?.trim()}>Add</Button>
        </div>
      </div>
      <div className={`work-type-selected-panel ${selected.length > 0 ? "work-type-selected-panel--filled" : ""}`}>
        <div className="mb-3 flex items-center justify-between">
          <span className="text-xs font-bold uppercase tracking-wider text-primary">Selected ({selected.length})</span>
          {selected.length > 0 ? <button type="button" onClick={onClearAll} className="text-xs text-text-muted hover:text-danger">Clear all</button> : null}
        </div>
        {selected.length > 0 ? (
          <div className="work-type-chip-row">
            {selected.map((type) => (
              <span key={type} className="work-type-chip work-type-chip--selected">
                <span className="work-type-chip__label">{type}</span>
                <button
                  type="button"
                  onClick={() => removeType(type)}
                  className="work-type-chip__remove"
                  aria-label={`Remove ${type}`}
                >
                  <span aria-hidden="true">&times;</span>
                </button>
              </span>
            ))}
          </div>
        ) : (
          <p className="text-center text-sm text-text-muted">No work types yet — pick a preset above.</p>
        )}
      </div>
    </fieldset>
  );
}

Object.assign(window, {
  WorkPlanEditor: {
    createDraftTask,
    createDraftTaskId,
    parseIndianCostInput,
    addCalendarDays,
    calendarDaysBetween,
    defaultTaskDates,
    getTaskDateBounds,
    validateTaskSchedule,
    emptyTaskBreakdown,
    applyDirectBudget,
    isQcType,
    stripQcScheduling,
    emptyQcBreakdown,
    calculateEstimateBudget,
    recalcTaskBudget,
    deriveSimpleCostSplit,
    applySimpleCostSplit,
    calculateBlockBudget,
    calculateProjectBudget,
    syncPresetTasksForBlock,
    syncPresetTasksForAllBlocks,
    normalizePresetDefaults,
    inferPresetDefaultsFromBlocks,
    flattenBlockTasks,
    mergeImportedTasksIntoBlocks,
    applyFoundationPlanToBlock,
    applyFoundationPlanToAllBlocks,
    foundationPresetDefaultsFromPlan,
    syncBlockDependencies,
    syncProjectDependencies,
    syncParentTaskDatesFromSubtasks,
    buildProjectTaskIndex,
    collectProjectDependencyLinks,
    collectProjectDependencySummary,
    orderBlockTasksByDependency,
    workItemToDraft,
    getBlockTasksFromTree,
    getBlockLinkableItemsFromTree,
    resolveDependencyPatches,
    isLinkableDependencyType,
    supportsDependencies,
    formatDependencyOptionLabel,
    getWorkItemTypeMeta,
    formatWorkStatus
  },
  WorkPlanFilterBar,
  TaskEditorModal,
  PresetWorkTypeModal,
  SimpleCostEstimateFields,
  CurrencyQuickInput,
  WorkItemDraftRow,
  WorkItemDraftForm,
  WorkItemLiveForm,
  BlockWorkPlanPanel,
  WizardBudgetSummary,
  ProjectDependencyOverview,
  WorkTypeChipPicker,
  FoundationPlanPanel
});
