import { useCallback, useEffect, useRef, useState } from 'react';
import { ArrowLeft, Check, FolderPlus, Plus, Trash2, TriangleAlert } from 'lucide-react';
import { navigateToPath } from '../routing';
import { Badge, Button, Card, ConfirmDialog, Dialog, IconButton, Input, Select, Spinner } from '../components';
import {
  ApiError,
  createDecoTemplate,
  createDecoTemplateGroup,
  deleteDecoTemplate,
  deleteDecoTemplateGroup,
  getDecoGroups,
  getDecoTemplate,
  getDecoTemplateGroups,
  getDecoTemplateItemsWithMeta,
  getDecoTemplates,
  getDecos,
  messageFromDecoTemplateError,
  replaceDecoTemplate,
  type DecoGroup,
  type DecoListItem,
  type DecoTemplate,
  type DecoTemplateGroup,
  type DecoTemplateItem
} from '../api';

const BASE_PATH = '/app/deco-templates';
const MINUTES_PER_DAY = 24 * 60;

// Every start time is a five-minute increment across a full day (mirrors Blazor DecoTemplateEditor.razor's picker).
const START_TIME_OPTIONS = Array.from({ length: 288 }, (_, index) => {
  const totalMinutes = index * 5;
  const hours = Math.floor(totalMinutes / 60);
  const minutes = totalMinutes % 60;
  const value = `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}:00`;
  return { label: value.slice(0, 5), value };
});

const DURATION_MINUTE_OPTIONS = [0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55].map((minutes) => ({
  label: String(minutes),
  value: String(minutes)
}));

function timeToMinutes(time: string): number {
  const [hours, minutes] = time.split(':').map(Number);
  return hours * 60 + minutes;
}

// An end time of 00:00:00 means "end of day" (24:00), not midnight-of-the-same-day; this mirrors
// ReplaceDecoTemplateItemsHandler.DecoTemplateItemsMustBeValid's EffectiveEndTime treatment.
function effectiveEndMinutes(endTime: string): number {
  const minutes = timeToMinutes(endTime);
  return minutes === 0 ? MINUTES_PER_DAY : minutes;
}

function minutesToClock(totalMinutes: number): string {
  if (totalMinutes >= MINUTES_PER_DAY) {
    return '24:00';
  }
  const hours = Math.floor(totalMinutes / 60);
  const minutes = totalMinutes % 60;
  return `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}`;
}

interface DraftItem {
  key: string;
  decoId: number;
  decoName: string;
  startTime: string;
  endTime: string;
}

interface Draft {
  name: string;
  items: DraftItem[];
}

let keySeq = 0;
function nextKey(): string {
  keySeq += 1;
  return `item-${keySeq}`;
}

function itemFromResponse(item: DecoTemplateItem): DraftItem {
  return {
    key: nextKey(),
    decoId: item.decoId,
    decoName: item.decoName,
    startTime: item.startTime,
    endTime: item.endTime
  };
}

// Mirrors DecoTemplateItemsMustBeValid: an item must start before its (effective) end time, and no
// two items may overlap.
function findOrderingError(items: DraftItem[]): null | string {
  for (const item of items) {
    if (timeToMinutes(item.startTime) >= effectiveEndMinutes(item.endTime)) {
      return `"${item.decoName}" (${minutesToClock(timeToMinutes(item.startTime))}-${minutesToClock(effectiveEndMinutes(item.endTime))}) must start before it ends`;
    }
  }

  return null;
}

function findOverlap(items: DraftItem[]): null | string {
  const spans = items.map((item) => ({
    item,
    start: timeToMinutes(item.startTime),
    end: effectiveEndMinutes(item.endTime)
  }));

  for (let i = 0; i < spans.length; i += 1) {
    for (let j = i + 1; j < spans.length; j += 1) {
      const a = spans[i];
      const b = spans[j];
      if (a.start < b.end && b.start < a.end) {
        return `"${a.item.decoName}" (${minutesToClock(a.start)}-${minutesToClock(a.end)}) overlaps "${b.item.decoName}" (${minutesToClock(b.start)}-${minutesToClock(b.end)})`;
      }
    }
  }

  return null;
}

function validate(draft: Draft): null | string {
  if (!draft.name.trim()) {
    return 'Deco template name is required';
  }

  return findOrderingError(draft.items) ?? findOverlap(draft.items);
}

type Mode = { id: number; kind: 'edit' } | { kind: 'list' };

function modeFromPath(pathname: string): Mode {
  const normalized = pathname.replace(/\/+$/, '');
  if (normalized === BASE_PATH || !normalized.startsWith(`${BASE_PATH}/`)) {
    return { kind: 'list' };
  }
  const suffix = normalized.slice(BASE_PATH.length + 1).split('/')[0];
  const id = Number(suffix);
  return Number.isInteger(id) && id > 0 ? { id, kind: 'edit' } : { kind: 'list' };
}

export function DecoTemplatesScreen() {
  const [pathname, setPathname] = useState(() => window.location.pathname);

  useEffect(() => {
    const onPop = () => setPathname(window.location.pathname);
    window.addEventListener('popstate', onPop);
    return () => window.removeEventListener('popstate', onPop);
  }, []);

  const mode = modeFromPath(pathname);

  if (mode.kind === 'list') {
    return <DecoTemplateList key={pathname} />;
  }

  return <DecoTemplateEditor decoTemplateId={mode.id} key={pathname} />;
}

function DecoTemplateList() {
  const [groups, setGroups] = useState<DecoTemplateGroup[] | null>(null);
  const [decoTemplates, setDecoTemplates] = useState<DecoTemplate[] | null>(null);
  const [error, setError] = useState<null | string>(null);
  const [groupDialogOpen, setGroupDialogOpen] = useState(false);
  const [newGroupName, setNewGroupName] = useState('');
  const [decoTemplateDialogGroup, setDecoTemplateDialogGroup] = useState<DecoTemplateGroup | null>(null);
  const [newDecoTemplateName, setNewDecoTemplateName] = useState('');
  const [deleteGroupTarget, setDeleteGroupTarget] = useState<DecoTemplateGroup | null>(null);
  const [deleteDecoTemplateTarget, setDeleteDecoTemplateTarget] = useState<DecoTemplate | null>(null);
  const [busy, setBusy] = useState(false);
  const activeRef = useRef(true);

  const load = useCallback(() => {
    Promise.all([getDecoTemplateGroups(), getDecoTemplates()])
      .then(([groupData, decoTemplateData]) => {
        if (activeRef.current) {
          setGroups(groupData);
          setDecoTemplates(decoTemplateData);
          setError(null);
        }
      })
      .catch((loadError: unknown) => {
        if (activeRef.current) {
          setError(messageFromDecoTemplateError(loadError));
        }
      });
  }, []);

  useEffect(() => {
    activeRef.current = true;
    load();
    return () => {
      activeRef.current = false;
    };
  }, [load]);

  const createGroup = async () => {
    if (!newGroupName.trim() || busy) {
      return;
    }
    setBusy(true);
    try {
      await createDecoTemplateGroup({ name: newGroupName.trim() });
      setGroupDialogOpen(false);
      setNewGroupName('');
      load();
    } catch (createError) {
      setError(messageFromDecoTemplateError(createError, 'Unable to create deco template group'));
    } finally {
      setBusy(false);
    }
  };

  const createDecoTemplateInGroup = async () => {
    if (!decoTemplateDialogGroup || !newDecoTemplateName.trim() || busy) {
      return;
    }
    setBusy(true);
    try {
      const decoTemplate = await createDecoTemplate({
        decoTemplateGroupId: decoTemplateDialogGroup.id,
        name: newDecoTemplateName.trim()
      });
      setDecoTemplateDialogGroup(null);
      setNewDecoTemplateName('');
      navigateToPath(`${BASE_PATH}/${decoTemplate.id}`);
    } catch (createError) {
      setError(messageFromDecoTemplateError(createError, 'Unable to create deco template'));
    } finally {
      setBusy(false);
    }
  };

  const confirmDeleteGroup = async () => {
    if (!deleteGroupTarget) {
      return;
    }
    setBusy(true);
    try {
      await deleteDecoTemplateGroup(deleteGroupTarget.id);
      setDeleteGroupTarget(null);
      load();
    } catch (deleteError) {
      setError(messageFromDecoTemplateError(deleteError, 'Unable to delete deco template group'));
    } finally {
      setBusy(false);
    }
  };

  const confirmDeleteDecoTemplate = async () => {
    if (!deleteDecoTemplateTarget) {
      return;
    }
    setBusy(true);
    try {
      await deleteDecoTemplate(deleteDecoTemplateTarget.id);
      setDeleteDecoTemplateTarget(null);
      load();
    } catch (deleteError) {
      setError(messageFromDecoTemplateError(deleteError, 'Unable to delete deco template'));
    } finally {
      setBusy(false);
    }
  };

  if (error && (!groups || !decoTemplates)) {
    return (
      <div className="ctv-collections">
        <div className="ctv-channels-error" role="alert">
          <TriangleAlert aria-hidden="true" size={15} />
          <span>{error}</span>
          <span className="ctv-channels-spacer" />
          <Button onClick={load} size="sm" variant="secondary">
            Retry
          </Button>
        </div>
      </div>
    );
  }

  if (!groups || !decoTemplates) {
    return (
      <div className="ctv-collections-loading" role="status">
        <Spinner size={18} />
        <span>Loading deco templates…</span>
      </div>
    );
  }

  const sortedGroups = [...groups].sort((a, b) => a.name.localeCompare(b.name));

  return (
    <div className="ctv-collections">
      <div className="ctv-channels-actionbar">
        <Badge tone="neutral">{decoTemplates.length} deco templates</Badge>
        <span className="ctv-channels-spacer" />
        <Button
          onClick={() => {
            setNewGroupName('');
            setGroupDialogOpen(true);
          }}
          size="sm"
          startIcon={<FolderPlus aria-hidden="true" size={14} />}
        >
          New group
        </Button>
      </div>

      {error && (
        <div className="ctv-channels-error" role="alert">
          <TriangleAlert aria-hidden="true" size={15} />
          <span>{error}</span>
        </div>
      )}

      {sortedGroups.length === 0 ? (
        <Card>
          <div className="ctv-collections-empty">No deco template groups yet. Create one to get started.</div>
        </Card>
      ) : (
        sortedGroups.map((group) => {
          const groupDecoTemplates = decoTemplates
            .filter((t) => t.decoTemplateGroupId === group.id && t.id > 0)
            .sort((a, b) => a.name.localeCompare(b.name));
          return (
            <Card
              key={group.id}
              padded={false}
              title={group.name}
              actions={
                <div style={{ display: 'flex', gap: 8 }}>
                  <Button
                    onClick={() => {
                      setNewDecoTemplateName('');
                      setDecoTemplateDialogGroup(group);
                    }}
                    size="sm"
                    startIcon={<Plus aria-hidden="true" size={14} />}
                    variant="secondary"
                  >
                    New deco template
                  </Button>
                  <IconButton
                    onClick={() => setDeleteGroupTarget(group)}
                    size="sm"
                    title="Delete group"
                    variant="ghost"
                  >
                    <Trash2 aria-hidden="true" size={14} />
                  </IconButton>
                </div>
              }
            >
              {groupDecoTemplates.length === 0 ? (
                <div className="ctv-collections-empty">No deco templates in this group.</div>
              ) : (
                groupDecoTemplates.map((decoTemplate, index) => (
                  <div
                    className="ctv-settings-flush-row"
                    key={decoTemplate.id}
                    style={index === 0 ? { borderTop: 'none' } : undefined}
                  >
                    <button
                      className="ctv-settings-flush-row-main ctv-settings-flush-row-title ctv-collections-linkbtn"
                      onClick={() => navigateToPath(`${BASE_PATH}/${decoTemplate.id}`)}
                      type="button"
                    >
                      {decoTemplate.name}
                    </button>
                    <IconButton
                      onClick={() => setDeleteDecoTemplateTarget(decoTemplate)}
                      size="sm"
                      title="Delete deco template"
                      variant="ghost"
                    >
                      <Trash2 aria-hidden="true" size={14} />
                    </IconButton>
                  </div>
                ))
              )}
            </Card>
          );
        })
      )}

      <Dialog
        footer={
          <>
            <Button onClick={() => setGroupDialogOpen(false)} size="sm" variant="ghost">
              Cancel
            </Button>
            <Button disabled={!newGroupName.trim() || busy} loading={busy} onClick={() => void createGroup()} size="sm">
              Create
            </Button>
          </>
        }
        onClose={() => setGroupDialogOpen(false)}
        open={groupDialogOpen}
        title="New deco template group"
      >
        <Input
          label="Name"
          onChange={(event) => setNewGroupName(event.target.value)}
          placeholder="Deco template group name"
          value={newGroupName}
        />
      </Dialog>

      <Dialog
        footer={
          <>
            <Button onClick={() => setDecoTemplateDialogGroup(null)} size="sm" variant="ghost">
              Cancel
            </Button>
            <Button
              disabled={!newDecoTemplateName.trim() || busy}
              loading={busy}
              onClick={() => void createDecoTemplateInGroup()}
              size="sm"
            >
              Create
            </Button>
          </>
        }
        onClose={() => setDecoTemplateDialogGroup(null)}
        open={decoTemplateDialogGroup !== null}
        title={decoTemplateDialogGroup ? `New deco template in ${decoTemplateDialogGroup.name}` : 'New deco template'}
      >
        <Input
          label="Name"
          onChange={(event) => setNewDecoTemplateName(event.target.value)}
          placeholder="Deco template name"
          value={newDecoTemplateName}
        />
      </Dialog>

      <ConfirmDialog
        busy={busy}
        confirmLabel="Delete"
        message={
          deleteGroupTarget
            ? `Delete group "${deleteGroupTarget.name}"? Every deco template in it will also be deleted. This cannot be undone.`
            : ''
        }
        onCancel={() => setDeleteGroupTarget(null)}
        onConfirm={() => void confirmDeleteGroup()}
        open={deleteGroupTarget !== null}
        title="Delete deco template group"
        tone="danger"
      />

      <ConfirmDialog
        busy={busy}
        confirmLabel="Delete"
        message={
          deleteDecoTemplateTarget
            ? `Delete deco template "${deleteDecoTemplateTarget.name}"? This cannot be undone.`
            : ''
        }
        onCancel={() => setDeleteDecoTemplateTarget(null)}
        onConfirm={() => void confirmDeleteDecoTemplate()}
        open={deleteDecoTemplateTarget !== null}
        title="Delete deco template"
        tone="danger"
      />
    </div>
  );
}

function DecoTemplateEditor({ decoTemplateId }: { decoTemplateId: number }) {
  const [decoTemplate, setDecoTemplate] = useState<DecoTemplate | null>(null);
  const [draft, setDraft] = useState<Draft | null>(null);
  const [decoGroups, setDecoGroups] = useState<DecoGroup[]>([]);
  const [decos, setDecos] = useState<DecoListItem[]>([]);
  const [loadError, setLoadError] = useState<null | string>(null);
  const [saveError, setSaveError] = useState<null | string>(null);
  const [saving, setSaving] = useState(false);
  const [conflictOpen, setConflictOpen] = useState(false);
  const [reloadKey, setReloadKey] = useState(0);
  // Concurrency ETag (issue #253): captured from the items GET, sent as If-Match on save, and
  // replaced from the PUT response on every successful save.
  const etagRef = useRef<string | null>(null);

  const [addGroupId, setAddGroupId] = useState('');
  const [addDecoId, setAddDecoId] = useState('');
  const [addStartTime, setAddStartTime] = useState('00:00:00');
  const [addDurationHours, setAddDurationHours] = useState('0');
  const [addDurationMinutes, setAddDurationMinutes] = useState('15');

  useEffect(() => {
    let active = true;

    // Read items + ETag FIRST, then the root metadata, mirroring BlockEditor (issue #253): the ETag
    // must be no newer than every piece of versioned data the draft is built from.
    void (async () => {
      try {
        const itemsMeta = await getDecoTemplateItemsWithMeta(decoTemplateId);
        const [decoTemplateData, groupData, decoData] = await Promise.all([
          getDecoTemplate(decoTemplateId),
          getDecoGroups(),
          getDecos()
        ]);
        if (!active) {
          return;
        }
        etagRef.current = itemsMeta.etag;
        setDecoTemplate(decoTemplateData);
        setDecoGroups(groupData);
        setDecos(decoData);
        setDraft({
          name: decoTemplateData.name,
          items: itemsMeta.data.map(itemFromResponse)
        });
      } catch (error) {
        if (active) {
          setLoadError(messageFromDecoTemplateError(error, 'Unable to load deco template'));
        }
      }
    })();

    return () => {
      active = false;
    };
  }, [decoTemplateId, reloadKey]);

  if (loadError) {
    return (
      <div className="ctv-settings-error" role="alert">
        <TriangleAlert aria-hidden="true" size={16} />
        <span>{loadError}</span>
        <Button onClick={() => navigateToPath(BASE_PATH)} size="sm" variant="secondary">
          Back to deco templates
        </Button>
      </div>
    );
  }

  if (!draft || !decoTemplate) {
    return (
      <div className="ctv-settings-loading">
        <Spinner size={18} />
        <span>Loading deco template…</span>
      </div>
    );
  }

  const setDraftValue = (patch: Partial<Draft>) => {
    setDraft((current) => (current ? { ...current, ...patch } : current));
  };

  const removeItem = (key: string) => {
    setDraft((current) => (current ? { ...current, items: current.items.filter((item) => item.key !== key) } : current));
  };

  // Decos belong to a group by id; the group picker narrows the deco picker's options (derived
  // during render, not via an effect, since it's a pure function of addGroupId + decos).
  const groupOptions = [{ label: 'Select a deco group…', value: '' }].concat(
    [...decoGroups].sort((a, b) => a.name.localeCompare(b.name)).map((g) => ({ label: g.name, value: String(g.id) }))
  );
  const decosInGroup = addGroupId
    ? decos.filter((d) => d.decoGroupId === Number(addGroupId) && d.id > 0).sort((a, b) => a.name.localeCompare(b.name))
    : [];
  const decoOptions = [{ label: 'Select a deco…', value: '' }].concat(
    decosInGroup.map((d) => ({ label: d.name, value: String(d.id) }))
  );

  const addDurationMinutesTotal = Number(addDurationHours) * 60 + Number(addDurationMinutes);
  const addEndMinutes = timeToMinutes(addStartTime) + addDurationMinutesTotal;
  const addCrossesMidnight = addDurationMinutesTotal <= 0 || addEndMinutes > MINUTES_PER_DAY;

  const addItem = () => {
    if (!addDecoId || addCrossesMidnight) {
      return;
    }
    const deco = decos.find((d) => d.id === Number(addDecoId));
    if (!deco) {
      return;
    }
    const endTime = addEndMinutes >= MINUTES_PER_DAY ? '00:00:00' : `${minutesToClock(addEndMinutes)}:00`;
    const item: DraftItem = {
      key: nextKey(),
      decoId: deco.id,
      decoName: deco.name,
      startTime: addStartTime,
      endTime
    };
    setDraft((current) => (current ? { ...current, items: [...current.items, item] } : current));
  };

  const sortedItems = [...draft.items].sort((a, b) => timeToMinutes(a.startTime) - timeToMinutes(b.startTime));
  const validationError = validate(draft);

  const save = async () => {
    if (validationError || saving) {
      return;
    }
    setSaving(true);
    setSaveError(null);
    try {
      const { etag } = await replaceDecoTemplate(
        decoTemplateId,
        {
          name: draft.name.trim(),
          items: draft.items.map((item) => ({
            decoId: item.decoId,
            startTime: item.startTime,
            endTime: item.endTime
          }))
        },
        etagRef.current
      );
      etagRef.current = etag;
      navigateToPath(BASE_PATH);
    } catch (error) {
      if (error instanceof ApiError && error.status === 412) {
        // Another edit landed since we loaded — force a reload rather than overwriting it (#253).
        setConflictOpen(true);
      } else {
        setSaveError(messageFromDecoTemplateError(error, 'Unable to save deco template'));
      }
    } finally {
      setSaving(false);
    }
  };

  const reloadAfterConflict = () => {
    setConflictOpen(false);
    setSaveError(null);
    setDraft(null);
    setDecoTemplate(null);
    setReloadKey((key) => key + 1);
  };

  return (
    <div className="ctv-collections">
      <div className="ctv-channels-actionbar">
        <Button
          onClick={() => navigateToPath(BASE_PATH)}
          size="sm"
          startIcon={<ArrowLeft aria-hidden="true" size={14} />}
          variant="ghost"
        >
          All deco templates
        </Button>
        <span className="ctv-collections-detail-title">{decoTemplate.groupName} / Edit deco template</span>
        <span className="ctv-channels-spacer" />
        {saveError && <span className="ctv-settings-savebar-error">{saveError}</span>}
        <Button
          disabled={validationError !== null || saving}
          loading={saving}
          onClick={() => void save()}
          size="sm"
          startIcon={<Check aria-hidden="true" size={14} />}
        >
          Save deco template
        </Button>
      </div>

      {validationError && (
        <div className="ctv-channels-error" role="alert">
          <TriangleAlert aria-hidden="true" size={15} />
          <span>{validationError}</span>
        </div>
      )}

      <Card padded={false} title="Deco template">
        <div className="ctv-settings-row" style={{ borderTop: 'none' }}>
          <div className="ctv-settings-row-main">
            <div className="ctv-settings-row-label">Deco template group</div>
          </div>
          <div className="ctv-settings-row-control" style={{ flex: '0 0 360px' }}>
            <Input disabled size="sm" value={decoTemplate.groupName} />
          </div>
        </div>
        <div className="ctv-settings-row">
          <div className="ctv-settings-row-main">
            <div className="ctv-settings-row-label">Deco template name</div>
          </div>
          <div className="ctv-settings-row-control" style={{ flex: '0 0 360px' }}>
            <Input
              error={draft.name.trim() ? null : 'Deco template name is required'}
              onChange={(event) => setDraftValue({ name: event.target.value })}
              size="sm"
              value={draft.name}
            />
          </div>
        </div>
      </Card>

      <Card padded={false} title="Add content">
        <div style={{ display: 'flex', gap: 12, padding: 16, flexWrap: 'wrap', alignItems: 'flex-end' }}>
          <div style={{ minWidth: 200 }}>
            <Select
              label="Deco group"
              onChange={(event) => {
                setAddGroupId(event.target.value);
                setAddDecoId('');
              }}
              options={groupOptions}
              value={addGroupId}
            />
          </div>
          <div style={{ minWidth: 200 }}>
            <Select
              label="Deco"
              onChange={(event) => setAddDecoId(event.target.value)}
              options={decoOptions}
              value={addDecoId}
            />
          </div>
          <div style={{ minWidth: 140 }}>
            <Select
              label="Start time"
              onChange={(event) => setAddStartTime(event.target.value)}
              options={START_TIME_OPTIONS}
              value={addStartTime}
            />
          </div>
          <div style={{ width: 90 }}>
            <Input
              label="Hours"
              onChange={(event) => setAddDurationHours(event.target.value)}
              type="number"
              value={addDurationHours}
            />
          </div>
          <div style={{ width: 110 }}>
            <Select
              label="Minutes"
              onChange={(event) => setAddDurationMinutes(event.target.value)}
              options={DURATION_MINUTE_OPTIONS}
              value={addDurationMinutes}
            />
          </div>
          <Button
            disabled={!addDecoId || addCrossesMidnight}
            onClick={addItem}
            size="sm"
            startIcon={<Plus aria-hidden="true" size={14} />}
          >
            Add
          </Button>
        </div>
        {addCrossesMidnight && addDecoId && (
          <div className="ctv-channels-error" role="alert" style={{ margin: '0 16px 16px' }}>
            <TriangleAlert aria-hidden="true" size={15} />
            <span>Duration must be greater than zero and must not cross midnight.</span>
          </div>
        )}
      </Card>

      <Card padded={false} title="Deco template items">
        {sortedItems.length === 0 ? (
          <div className="ctv-collections-empty">No items yet. Add one above to build the deco template.</div>
        ) : (
          <table className="ctv-channels-table">
            <thead>
              <tr>
                <th>Time</th>
                <th>Deco</th>
                <th />
              </tr>
            </thead>
            <tbody>
              {sortedItems.map((item) => {
                const start = timeToMinutes(item.startTime);
                const end = effectiveEndMinutes(item.endTime);
                return (
                  <tr key={item.key}>
                    <td>
                      {minutesToClock(start)}-{minutesToClock(end)}
                    </td>
                    <td>{item.decoName}</td>
                    <td>
                      <IconButton onClick={() => removeItem(item.key)} size="sm" title="Remove" variant="ghost">
                        <Trash2 aria-hidden="true" size={14} />
                      </IconButton>
                    </td>
                  </tr>
                );
              })}
            </tbody>
          </table>
        )}
      </Card>

      <ConfirmDialog
        cancelLabel="Keep editing"
        confirmLabel="Reload"
        message="This deco template was changed elsewhere since you opened it. Reload to get the latest version — your unsaved changes will be discarded."
        onCancel={() => setConflictOpen(false)}
        onConfirm={reloadAfterConflict}
        open={conflictOpen}
        title="Deco template changed elsewhere"
        tone="danger"
      />
    </div>
  );
}
