import { useEffect, useMemo, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import type { TFunction } from 'i18next';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { POLLING } from '../lib/constants.js';
import { EmptyState } from '../components/shared/EmptyState';
import { StatChip } from '../components/shared/StatChip';
import { usePieceList } from '../hooks/usePieces';
import { resolvePieceOptions } from '../lib/splitPieces';
import { ownerDisplayName } from '../lib/owner';
import { cronToFormState } from '../lib/cronForm';
import { fetchMyOrgs, listBrowserSessionProfiles, type Visibility } from '../api';
import { useAuthState } from '../App';

type TaskKind = 'agent' | 'script';

interface ScheduledTask {
  id: number;
  title: string | null;
  body: string;
  pieceName: string;
  profile: string;
  outputFormat: string;
  cronExpression: string;
  nextRunAt: string;
  lastRunAt: string | null;
  lastJobId: string | null;
  isActive: boolean;
  ownerId: string | null;
  ownerName: string | null;
  visibility: Visibility;
  visibilityScopeOrgId: string | null;
  visibilityScopeOrgName: string | null;
  browserSessionProfileId: number | null;
  taskKind: TaskKind;
  scriptName: string | null;
  scriptParams: string | null;
  createdAt: string;
}

interface ScheduleFormState {
  title: string;
  body: string;
  piece: string;
  scheduleType: string;
  hour: number;
  minute: number;
  dayOfWeek: number;
  dayOfMonth: number;
  cronExpression: string;
  scheduledAt: string;
  outputFormat: string;
  visibility: Visibility;
  visibilityScopeOrgId: string | null;
  browserSessionProfileId: number | null;
  taskKind: TaskKind;
  scriptName: string;
  scriptParams: string;
}

type ScheduleFilter = 'all' | 'active' | 'paused';
type DetailMode = 'view' | 'edit' | 'new';

const DAY_KEYS = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'] as const;

// label/hint are i18n keys (relative to the `schedules` namespace) resolved at
// the render site via t(...). `cron` has a fixed (non-prose) label.
const SCHEDULE_TYPE_OPTIONS: Array<{ value: string; labelKey: string | null; label?: string; hintKey: string }> = [
  { value: 'daily', labelKey: 'scheduleType.daily.label', hintKey: 'scheduleType.daily.hint' },
  { value: 'weekly', labelKey: 'scheduleType.weekly.label', hintKey: 'scheduleType.weekly.hint' },
  { value: 'monthly', labelKey: 'scheduleType.monthly.label', hintKey: 'scheduleType.monthly.hint' },
  { value: 'cron', labelKey: null, label: 'Cron', hintKey: 'scheduleType.cron.hint' },
  { value: 'once', labelKey: 'scheduleType.once.label', hintKey: 'scheduleType.once.hint' },
];

const OUTPUT_FORMAT_OPTIONS = [
  { value: 'markdown', label: 'markdown' },
  { value: 'plain', label: 'plain' },
  { value: 'json', label: 'json' },
];

function parseCronToDisplay(cron: string, t: TFunction): string {
  if (cron === 'once') return t('cronDisplay.onceOnly');
  const parts = cron.split(' ');
  if (parts.length !== 5) return cron;
  const [min, hour, dom, , dow] = parts;
  const hhmm = `${hour}:${min.padStart(2, '0')}`;

  if (dom !== '*' && dow === '*') return t('cronDisplay.monthly', { day: dom, time: hhmm });
  if (dow !== '*' && dom === '*') {
    const dayKey = DAY_KEYS[Number(dow)];
    const day = dayKey ? t(`days.${dayKey}`) : dow;
    return t('cronDisplay.weekly', { day, time: hhmm });
  }
  if (dom === '*' && dow === '*') return t('cronDisplay.daily', { time: hhmm });
  return cron;
}

// cronToFormState lives in ../lib/cronForm so it can be unit-tested in isolation.
// It only maps to daily/weekly/monthly presets when every field is a simple
// integer; custom expressions (ranges/steps/lists) stay as raw 'cron' so editing
// a saved schedule never mangles it to NaN or drops the expression.

function taskToFormState(task: ScheduledTask): ScheduleFormState {
  const cronState = cronToFormState(task.cronExpression);
  return {
    title: task.title ?? '',
    body: task.body,
    piece: task.pieceName || 'auto',
    outputFormat: task.outputFormat || 'markdown',
    scheduledAt: '',
    visibility: task.visibility,
    visibilityScopeOrgId: task.visibilityScopeOrgId,
    browserSessionProfileId: task.browserSessionProfileId ?? null,
    taskKind: task.taskKind,
    scriptName: task.scriptName ?? '',
    scriptParams: task.scriptParams ?? '',
    ...cronState,
  };
}

function blankFormState(visibility: Visibility = 'private', orgId: string | null = null): ScheduleFormState {
  return {
    title: '',
    body: '',
    piece: 'auto',
    scheduleType: 'daily',
    hour: 9,
    minute: 0,
    dayOfWeek: 1,
    dayOfMonth: 1,
    cronExpression: '',
    scheduledAt: '',
    outputFormat: 'markdown',
    visibility,
    visibilityScopeOrgId: orgId,
    browserSessionProfileId: null,
    taskKind: 'agent',
    scriptName: '',
    scriptParams: '',
  };
}

function formatDate(iso: string | null): string {
  if (!iso) return '—';
  return new Date(iso).toLocaleString('ja-JP', { timeZone: 'Asia/Tokyo', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' });
}

function relativeFromNow(iso: string | null, t: TFunction): string {
  if (!iso) return '—';
  const diff = new Date(iso).getTime() - Date.now();
  const abs = Math.abs(diff);
  const mins = Math.round(abs / 60000);
  const hrs = Math.round(mins / 60);
  const days = Math.round(hrs / 24);
  const unit = mins < 60
    ? t('relative.minutes', { count: mins })
    : hrs < 24
      ? t('relative.hours', { count: hrs })
      : t('relative.days', { count: days });
  return diff >= 0 ? t('relative.after', { unit }) : t('relative.before', { unit });
}

function buildCronPreview(form: ScheduleFormState, t: TFunction): string {
  const { scheduleType, hour, minute, dayOfWeek, dayOfMonth, cronExpression } = form;
  const hh = `${String(hour).padStart(2, '0')}:${String(minute).padStart(2, '0')}`;
  if (scheduleType === 'daily') return t('cronDisplay.daily', { time: hh });
  if (scheduleType === 'weekly') {
    const dayKey = DAY_KEYS[dayOfWeek];
    return t('cronDisplay.weekly', { day: dayKey ? t(`days.${dayKey}`) : '', time: hh });
  }
  if (scheduleType === 'monthly') return t('cronDisplay.monthly', { day: dayOfMonth, time: hh });
  if (scheduleType === 'cron') return parseCronToDisplay(cronExpression || '', t);
  if (scheduleType === 'once') return t('cronDisplay.onceOnly');
  return '';
}

async function fetchScheduledTasks(): Promise<ScheduledTask[]> {
  const res = await fetch('/api/scheduled-tasks');
  if (res.status === 401 || res.status === 403 || res.status === 404) return [];
  if (!res.ok) throw new Error('Failed to fetch scheduled tasks');
  const data = await res.json();
  return data.tasks ?? [];
}

interface SchedulesPageProps {
  showToast?: (message: string, variant?: 'success' | 'error') => void;
}

export function SchedulesPage({ showToast }: SchedulesPageProps = {}) {
  const { t } = useTranslation('schedules');
  const qc = useQueryClient();
  const { data: tasks = [], isLoading } = useQuery({
    queryKey: ['scheduledTasks'],
    queryFn: fetchScheduledTasks,
    refetchInterval: POLLING.VERY_SLOW,
  });

  const notifyError = (label: string, err: unknown) => {
    const msg = `${label}: ${err instanceof Error ? err.message : String(err)}`;
    if (showToast) showToast(msg, 'error');
    else console.error(msg);
  };

  const [filter, setFilter] = useState<ScheduleFilter>('all');
  const [search, setSearch] = useState('');
  const [activeId, setActiveId] = useState<number | null>(null);
  const [mode, setMode] = useState<DetailMode>('view');
  // Mobile single-column flow: list ↔ detail toggle. On sm+ both panes
  // are visible side-by-side and this flag is ignored. Defaults to list
  // (false) so the user lands on the overview.
  const [mobileShowDetail, setMobileShowDetail] = useState(false);

  const counts = useMemo(() => ({
    all: tasks.length,
    active: tasks.filter(t => t.isActive).length,
    paused: tasks.filter(t => !t.isActive).length,
  }), [tasks]);

  const filtered = useMemo(() => {
    return tasks.filter(t => {
      if (filter === 'active' && !t.isActive) return false;
      if (filter === 'paused' && t.isActive) return false;
      if (search) {
        const hay = `${t.title ?? ''} ${t.body}`.toLowerCase();
        if (!hay.includes(search.toLowerCase())) return false;
      }
      return true;
    });
  }, [tasks, filter, search]);

  const active = useMemo(() => {
    if (mode === 'new') return null;
    if (activeId != null) {
      const found = tasks.find(t => t.id === activeId);
      if (found) return found;
    }
    return filtered[0] ?? null;
  }, [tasks, filtered, activeId, mode]);

  useEffect(() => {
    if (mode === 'new') return;
    if (active && activeId !== active.id) setActiveId(active.id);
  }, [active, activeId, mode]);

  const toggleMutation = useMutation({
    mutationFn: async ({ id, isActive }: { id: number; isActive: boolean }) => {
      const res = await fetch(`/api/scheduled-tasks/${id}`, {
        method: 'PATCH',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ isActive }),
      });
      if (!res.ok) throw new Error(`HTTP ${res.status} ${await res.text().catch(() => '')}`.trim());
    },
    onSuccess: () => qc.invalidateQueries({ queryKey: ['scheduledTasks'] }),
    onError: (err) => notifyError(t('errors.toggleFailed'), err),
  });

  const deleteMutation = useMutation({
    mutationFn: async (id: number) => {
      const res = await fetch(`/api/scheduled-tasks/${id}`, { method: 'DELETE' });
      if (!res.ok) throw new Error(`HTTP ${res.status} ${await res.text().catch(() => '')}`.trim());
    },
    onSuccess: () => qc.invalidateQueries({ queryKey: ['scheduledTasks'] }),
    onError: (err) => notifyError(t('errors.deleteFailed'), err),
  });

  const triggerMutation = useMutation({
    mutationFn: async (id: number) => {
      const res = await fetch(`/api/scheduled-tasks/${id}/trigger`, { method: 'POST' });
      if (!res.ok) throw new Error(`HTTP ${res.status} ${await res.text().catch(() => '')}`.trim());
    },
    onSuccess: () => qc.invalidateQueries({ queryKey: ['scheduledTasks'] }),
    onError: (err) => notifyError(t('errors.triggerFailed'), err),
  });

  const clearFilters = () => {
    setSearch('');
    setFilter('all');
  };

  const handleNew = () => {
    setMode('new');
    setMobileShowDetail(true);
  };

  const handleEdit = () => {
    if (active) setMode('edit');
  };

  const handleCancelEdit = () => {
    // On mobile, cancelling from 'new' means user backs out of creation
    // entirely; they didn't want to see some random task's detail.
    if (mode === 'new') setMobileShowDetail(false);
    setMode('view');
  };

  const handleSaved = async (newId?: number) => {
    await qc.invalidateQueries({ queryKey: ['scheduledTasks'] });
    if (newId != null) setActiveId(newId);
    setMode('view');
  };

  const handleSelect = (id: number) => {
    setActiveId(id);
    setMode('view');
    setMobileShowDetail(true);
  };

  const handleMobileBack = () => {
    setMode('view');
    setMobileShowDetail(false);
  };

  return (
    <div className="flex h-full min-h-0">
      <div className={`${mobileShowDetail ? 'hidden sm:flex' : 'flex'} w-full sm:w-[320px] flex-shrink-0 sm:border-r sm:border-hairline bg-canvas p-3 flex-col min-h-0`}>
        <ScheduleListPane
          tasks={filtered}
          activeId={mode === 'new' ? null : active?.id ?? null}
          highlightNew={mode === 'new'}
          counts={counts}
          search={search}
          setSearch={setSearch}
          filter={filter}
          setFilter={setFilter}
          onSelect={handleSelect}
          onOpenCreate={handleNew}
          onClearFilters={clearFilters}
          isLoading={isLoading}
        />
      </div>
      <div className={`${mobileShowDetail ? 'flex' : 'hidden sm:flex'} flex-1 min-w-0 bg-canvas flex-col`}>
        <ScheduleDetailPane
          task={active}
          mode={mode}
          onNew={handleNew}
          onEdit={handleEdit}
          onCancelEdit={handleCancelEdit}
          onSaved={handleSaved}
          onMobileBack={handleMobileBack}
          onTrigger={(id) => triggerMutation.mutate(id)}
          onToggle={(id, isActive) => toggleMutation.mutate({ id, isActive })}
          triggeringId={triggerMutation.isPending ? (triggerMutation.variables ?? null) : null}
          togglingId={toggleMutation.isPending ? (toggleMutation.variables?.id ?? null) : null}
          onDelete={(id) => {
            if (confirm(t('confirm.delete'))) {
              deleteMutation.mutate(id);
              if (activeId === id) setActiveId(null);
              setMobileShowDetail(false);
            }
          }}
        />
      </div>
    </div>
  );
}

interface ScheduleListPaneProps {
  tasks: ScheduledTask[];
  activeId: number | null;
  highlightNew: boolean;
  counts: { all: number; active: number; paused: number };
  search: string;
  setSearch: (s: string) => void;
  filter: ScheduleFilter;
  setFilter: (f: ScheduleFilter) => void;
  onSelect: (id: number) => void;
  onOpenCreate: () => void;
  onClearFilters: () => void;
  isLoading: boolean;
}

function ScheduleListPane({
  tasks, activeId, highlightNew, counts, search, setSearch, filter, setFilter,
  onSelect, onOpenCreate, onClearFilters, isLoading,
}: ScheduleListPaneProps) {
  const { t } = useTranslation('schedules');
  const hasFilters = !!search || filter !== 'all';

  return (
    <div className="flex flex-col h-full overflow-hidden">
      <button
        type="button"
        onClick={onOpenCreate}
        aria-pressed={highlightNew}
        className={`w-full mb-3 px-3 py-2 rounded-md text-xs font-semibold inline-flex items-center justify-center gap-1.5 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent-ring ${
          highlightNew
            ? 'bg-accent-deep text-accent-fg ring-2 ring-accent-ring'
            : 'bg-accent hover:bg-accent-deep text-accent-fg'
        }`}
      >
        <svg
          width="13"
          height="13"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          strokeWidth={2.25}
          strokeLinecap="round"
          strokeLinejoin="round"
          aria-hidden="true"
        >
          <path d="M12 5v14M5 12h14" />
        </svg>
        {t('list.new')}
      </button>

      <div className="flex items-center gap-3 text-[10px] text-slate-500 px-0.5 pb-2.5 font-mono tabular-nums">
        <span><span className="font-semibold text-slate-700">{counts.all}</span> {t('list.countUnit')}</span>
        {(counts.active > 0 || counts.paused > 0) && (
          <>
            <span aria-hidden="true" className="text-slate-300">·</span>
            <span><span className="font-semibold text-emerald-600">{counts.active}</span> {t('list.active')}</span>
            {counts.paused > 0 && (
              <span><span className="font-semibold text-slate-600">{counts.paused}</span> {t('list.paused')}</span>
            )}
          </>
        )}
      </div>

      <div className="flex flex-col gap-2 pb-3 border-b border-hairline">
        <div className="flex items-center gap-1.5 bg-canvas border border-hairline rounded-md pl-2.5 pr-1 h-8">
          <svg aria-hidden="true" className="w-3.5 h-3.5 text-slate-400 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
            <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
          </svg>
          <input
            aria-label={t('list.searchAria')}
            value={search}
            onChange={e => setSearch(e.target.value)}
            placeholder={t('list.searchPlaceholder')}
            className="flex-1 bg-transparent border-0 outline-none text-[13px] text-slate-900 placeholder:text-slate-400 min-w-0"
          />
        </div>
        <div role="tablist" aria-label={t('list.filterAria')} className="flex gap-1 overflow-x-auto pb-1 scrollbar-none">
          {([
            ['all', t('list.filter.all'), counts.all],
            ['active', t('list.filter.active'), counts.active],
            ['paused', t('list.filter.paused'), counts.paused],
          ] as const).map(([key, label, n]) => (
            <button
              key={key}
              type="button"
              role="tab"
              aria-selected={filter === key}
              onClick={() => setFilter(key as ScheduleFilter)}
              className={`flex-shrink-0 px-2 h-7 rounded text-2xs font-medium border transition-colors whitespace-nowrap focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent-ring ${
                filter === key
                  ? 'border-accent/60 bg-accent-soft text-accent font-semibold'
                  : 'border-hairline bg-canvas text-slate-600 hover:bg-surface'
              }`}
            >
              {label} <span className="text-slate-400 ml-0.5 font-mono tabular-nums">{n}</span>
            </button>
          ))}
        </div>
      </div>

      <div className="flex flex-col gap-1.5 mt-2 overflow-y-auto flex-1 min-h-0 pr-0.5">
        {isLoading && <div className="text-[13px] text-slate-500 px-2 py-3">{t('list.loading')}</div>}
        {!isLoading && tasks.length === 0 && (
          hasFilters ? (
            <EmptyState
              compact
              title={t('list.empty.filteredTitle')}
              hint={t('list.empty.filteredHint')}
              action={
                <button
                  type="button"
                  onClick={onClearFilters}
                  className="px-3 py-1.5 rounded-md text-xs font-semibold bg-canvas border border-hairline text-slate-700 hover:border-hairline transition-colors"
                >
                  {t('list.empty.clearFilters')}
                </button>
              }
            />
          ) : (
            <EmptyState
              compact
              title={t('list.empty.title')}
              hint={t('list.empty.hint')}
            />
          )
        )}
        {!isLoading && tasks.map(task => (
          <ScheduleListItem
            key={task.id}
            task={task}
            active={activeId === task.id}
            onClick={() => onSelect(task.id)}
          />
        ))}
      </div>
    </div>
  );
}

function ScheduleListItem({ task, active, onClick }: { task: ScheduledTask; active: boolean; onClick: () => void }) {
  const { t } = useTranslation('schedules');
  const authState = useAuthState();
  const showOwnership = authState.mode === 'authenticated';
  return (
    <button
      type="button"
      onClick={onClick}
      aria-current={active ? 'true' : undefined}
      className={`w-full text-left px-3 py-2.5 rounded-md border transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent-ring ${
        active ? 'border-accent/60 bg-accent-soft' : 'border-hairline bg-canvas hover:bg-surface'
      }`}
    >
      <div className="flex items-center gap-2 min-w-0">
        <span aria-hidden="true" className={`inline-block w-1.5 h-1.5 rounded-full flex-shrink-0 ${task.isActive ? 'bg-emerald-500' : 'bg-slate-300'}`} />
        <span className={`flex-1 min-w-0 truncate text-[13px] font-semibold ${task.isActive ? 'text-slate-900' : 'text-slate-500'}`}>
          {task.title || t('item.untitled')}
        </span>
      </div>
      <div className="mt-1 text-2xs text-slate-600 truncate leading-snug">
        {parseCronToDisplay(task.cronExpression, t)}
      </div>
      <div className="mt-1 text-[10px] text-slate-400 font-mono tabular-nums">
        {task.isActive
          ? task.nextRunAt
            ? t('item.next', { date: formatDate(task.nextRunAt), relative: relativeFromNow(task.nextRunAt, t) })
            : t('item.nextUndecided')
          : t('item.paused')}
      </div>
      {showOwnership && (
        <div className="mt-1.5 flex items-center gap-1.5 text-[10px] flex-wrap">
          <span className="text-slate-600">{ownerDisplayName(task.ownerId, task.ownerName)}</span>
          {task.visibility === 'private' && (
            <span className="px-1 rounded text-[10px] font-medium bg-amber-50 dark:bg-amber-500/15 text-amber-700 dark:text-amber-300 border border-amber-100 dark:border-amber-500/30" title={t('item.private')}>🔒 {t('item.private')}</span>
          )}
          {task.visibility === 'org' && (
            <span className="px-1 rounded text-[10px] font-medium bg-blue-50 dark:bg-blue-500/15 text-blue-700 dark:text-blue-300 border border-blue-100 dark:border-blue-500/30" title={t('item.sharedWith', { org: task.visibilityScopeOrgName ?? 'org' })}>
              🏢 {task.visibilityScopeOrgName ?? 'org'}
            </span>
          )}
          {task.visibility === 'public' && (
            <span className="px-1 rounded text-[10px] font-medium bg-emerald-50 dark:bg-emerald-500/15 text-emerald-700 dark:text-emerald-300 border border-emerald-100 dark:border-emerald-500/30" title={t('item.public')}>🌐 {t('item.public')}</span>
          )}
        </div>
      )}
    </button>
  );
}

interface ScheduleDetailPaneProps {
  task: ScheduledTask | null;
  mode: DetailMode;
  onNew: () => void;
  onEdit: () => void;
  onCancelEdit: () => void;
  onSaved: (newId?: number) => void | Promise<void>;
  /** Mobile-only callback to return to the list pane. Hidden on sm+. */
  onMobileBack?: () => void;
  onTrigger: (id: number) => void;
  onToggle: (id: number, isActive: boolean) => void;
  onDelete: (id: number) => void;
  /** Task id currently being triggered (for optimistic "実行中..." state). */
  triggeringId?: number | null;
  togglingId?: number | null;
}

function ScheduleDetailPane({
  task, mode, onNew, onEdit, onCancelEdit, onSaved, onMobileBack, onTrigger, onToggle, onDelete,
  triggeringId, togglingId,
}: ScheduleDetailPaneProps) {
  const { t } = useTranslation('schedules');
  const authState = useAuthState();
  const showOwnership = authState.mode === 'authenticated';
  if (mode === 'new') {
    return (
      <ScheduleEditor
        mode="new"
        initialTask={null}
        onCancel={onCancelEdit}
        onSaved={onSaved}
      />
    );
  }

  if (mode === 'edit' && task) {
    return (
      <ScheduleEditor
        mode="edit"
        initialTask={task}
        onCancel={onCancelEdit}
        onSaved={onSaved}
      />
    );
  }

  if (!task) {
    return (
      <div className="h-full flex items-center justify-center p-10">
        <EmptyState
          title={t('detail.selectTitle')}
          hint={t('detail.selectHint')}
          action={
            <button
              type="button"
              onClick={onNew}
              className="px-3 py-1.5 rounded-md text-xs font-semibold bg-accent hover:bg-accent-deep text-accent-fg transition-colors"
            >
              {t('detail.newSchedule')}
            </button>
          }
        />
      </div>
    );
  }

  return (
    <div className="flex flex-col h-full overflow-hidden">
      <div className="flex-shrink-0 px-3 sm:px-5 py-3 sm:py-3.5 border-b border-hairline bg-canvas flex flex-col sm:flex-row sm:items-center sm:justify-between gap-2 sm:gap-3">
        <div className="flex items-center gap-2 sm:gap-2.5 min-w-0">
          {onMobileBack && (
            <button
              type="button"
              onClick={onMobileBack}
              aria-label={t('detail.backAria')}
              className="sm:hidden inline-flex items-center justify-center w-8 h-8 -ml-1 rounded-md text-slate-500 hover:text-slate-900 hover:bg-surface-2 transition-colors"
            >
              <svg className="w-4 h-4" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.75" strokeLinecap="round" strokeLinejoin="round">
                <path d="M10 4l-4 4 4 4" />
              </svg>
            </button>
          )}
          <span aria-hidden="true" className={`inline-block w-2 h-2 rounded-full flex-shrink-0 ${task.isActive ? 'bg-emerald-500' : 'bg-slate-300'}`} />
          <div className="section-label font-mono flex-shrink-0 hidden sm:block">
            SCHEDULE #{task.id}
          </div>
          <div className="text-sm sm:text-sm font-semibold text-slate-900 truncate">
            {task.title || t('detail.untitled')}
          </div>
        </div>
        <div className="flex items-center gap-1.5 flex-shrink-0 overflow-x-auto scrollbar-none -mx-1 px-1 sm:mx-0 sm:px-0">
          {(() => {
            const isTriggering = triggeringId === task.id;
            const isToggling = togglingId === task.id;
            return (
              <>
                <button
                  type="button"
                  onClick={() => onTrigger(task.id)}
                  disabled={isTriggering}
                  className="px-2.5 sm:px-3 h-7 sm:h-8 rounded-md text-xs font-semibold bg-canvas border border-accent text-accent hover:bg-accent-soft active:scale-[0.97] active:bg-accent-soft transition-[transform,background-color,color] duration-100 inline-flex items-center gap-1 whitespace-nowrap disabled:opacity-70 disabled:cursor-wait"
                >
                  {isTriggering ? (
                    <span className="inline-block w-3 h-3 border-2 border-accent border-t-transparent rounded-full animate-spin" aria-hidden="true" />
                  ) : (
                    <svg width="12" height="12" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
                      <path d="M8 5v14l11-7z" />
                    </svg>
                  )}
                  {isTriggering ? t('detail.running') : t('detail.runNow')}
                </button>
                <button
                  type="button"
                  onClick={() => onToggle(task.id, !task.isActive)}
                  disabled={isToggling}
                  className="px-2.5 sm:px-3 h-7 sm:h-8 rounded-md text-xs font-medium bg-canvas border border-hairline text-slate-700 hover:bg-surface active:scale-[0.97] active:bg-surface transition-[transform,background-color,color] duration-100 whitespace-nowrap disabled:opacity-70 disabled:cursor-wait"
                >
                  {isToggling ? t('detail.toggling') : (task.isActive ? t('detail.stop') : t('detail.resume'))}
                </button>
                <button
                  type="button"
                  onClick={onEdit}
                  className="px-2.5 sm:px-3 h-7 sm:h-8 rounded-md text-xs font-medium bg-canvas border border-hairline text-slate-700 hover:bg-surface active:scale-[0.97] active:bg-surface transition-[transform,background-color,color] duration-100 whitespace-nowrap"
                >
                  {t('detail.edit')}
                </button>
                <button
                  type="button"
                  onClick={() => onDelete(task.id)}
                  className="px-2.5 sm:px-3 h-7 sm:h-8 rounded-md text-xs font-medium bg-canvas border border-red-200 text-red-700 dark:text-red-300 hover:bg-red-50 dark:hover:bg-red-500/15 active:scale-[0.97] active:bg-red-50 dark:active:bg-red-500/15 transition-[transform,background-color,color] duration-100 whitespace-nowrap"
                >
                  {t('detail.delete')}
                </button>
              </>
            );
          })()}
        </div>
      </div>

      <div className="flex-1 overflow-y-auto px-4 sm:px-6 py-4 sm:py-5 bg-surface">
        <div className="max-w-[640px] mx-auto">
          <div className="flex gap-2.5 mb-5 flex-wrap">
            <StatChip label={t('detail.stat.kind')} value={task.taskKind === 'script' ? `Script (${task.scriptName ?? '?'})` : t('detail.stat.agent')} />
            <StatChip label={t('detail.stat.schedule')} value={parseCronToDisplay(task.cronExpression, t)} />
            {task.taskKind === 'agent' && <StatChip label={t('detail.stat.piece')} value={task.pieceName} />}
            <StatChip
              label={task.isActive ? t('detail.stat.nextRun') : t('detail.stat.status')}
              value={task.isActive ? (task.nextRunAt ? relativeFromNow(task.nextRunAt, t) : '—') : t('detail.stat.paused')}
            />
          </div>

          <div className="bg-canvas border border-hairline rounded-md p-5">
            <div className="section-label mb-3.5">
              {t('detail.basicInfo')}
            </div>
            <dl className="grid grid-cols-1 gap-3 text-[13px]">
              <div>
                <dt className="text-2xs font-medium text-slate-500 mb-1">{t('detail.title')}</dt>
                <dd className="text-slate-900">{task.title || <span className="text-slate-400">{t('detail.untitled')}</span>}</dd>
              </div>
              <div>
                <dt className="text-2xs font-medium text-slate-500 mb-1">{t('detail.prompt')}</dt>
                <dd className="text-slate-700 whitespace-pre-wrap break-words">{task.body}</dd>
              </div>
              <div className="grid grid-cols-2 gap-3">
                <div>
                  <dt className="text-2xs font-medium text-slate-500 mb-1">{t('detail.stat.piece')}</dt>
                  <dd className="text-slate-900 font-mono">{task.pieceName}</dd>
                </div>
                <div>
                  <dt className="text-2xs font-medium text-slate-500 mb-1">{t('detail.outputFormat')}</dt>
                  <dd className="text-slate-900 font-mono">{task.outputFormat || 'markdown'}</dd>
                </div>
              </div>
              {showOwnership && (
                <div className="grid grid-cols-2 gap-3">
                  <div>
                    <dt className="text-2xs font-medium text-slate-500 mb-1">{t('detail.owner')}</dt>
                    <dd className="text-slate-900">
                      {ownerDisplayName(task.ownerId, task.ownerName)}
                    </dd>
                  </div>
                  <div>
                    <dt className="text-2xs font-medium text-slate-500 mb-1">{t('detail.visibility')}</dt>
                    <dd className="text-slate-900">
                      {task.visibility === 'private' && t('detail.visPrivate')}
                      {task.visibility === 'org' && `🏢 ${task.visibilityScopeOrgName ?? 'org'}`}
                      {task.visibility === 'public' && t('detail.visPublic')}
                    </dd>
                  </div>
                </div>
              )}
            </dl>
          </div>

          <div className="bg-canvas border border-hairline rounded-md p-5 mt-4">
            <div className="section-label mb-3.5">
              {t('detail.scheduleSection')}
            </div>
            <dl className="grid grid-cols-1 gap-3 text-[13px]">
              <div>
                <dt className="text-2xs font-medium text-slate-500 mb-1">{t('detail.cronExpression')}</dt>
                <dd className="text-slate-900 font-mono">{task.cronExpression || '—'}</dd>
              </div>
              <div className="px-3 py-2.5 bg-surface border border-hairline rounded-md text-xs text-slate-600">
                <b className="font-semibold text-slate-900">{parseCronToDisplay(task.cronExpression, t)}</b>
                {task.nextRunAt && (
                  <>
                    <span aria-hidden="true" className="mx-1.5 text-slate-300">·</span>
                    {t('detail.next', { date: formatDate(task.nextRunAt), relative: relativeFromNow(task.nextRunAt, t) })}
                  </>
                )}
              </div>
              <div className="grid grid-cols-2 gap-3">
                <div>
                  <dt className="text-2xs font-medium text-slate-500 mb-1">{t('detail.lastRun')}</dt>
                  <dd className="text-slate-700">{formatDate(task.lastRunAt)}</dd>
                </div>
                <div>
                  <dt className="text-2xs font-medium text-slate-500 mb-1">{t('detail.createdAt')}</dt>
                  <dd className="text-slate-700">{formatDate(task.createdAt)}</dd>
                </div>
              </div>
            </dl>
          </div>

          <div className="h-10" />
        </div>
      </div>
    </div>
  );
}

function EditorFormRow({
  label, help, children,
}: {
  label: string;
  help?: string;
  children: React.ReactNode;
}) {
  return (
    <label className="block">
      <div className="text-2xs font-semibold text-slate-600 mb-1">{label}</div>
      {children}
      {help && <div className="text-[10px] text-slate-400 mt-1">{help}</div>}
    </label>
  );
}

const EDITOR_INPUT_CLASS =
  'w-full px-3 py-2 border border-hairline rounded-md text-[13px] outline-none focus:border-accent focus:ring-2 focus:ring-accent-ring transition-colors';

interface ScheduleEditorProps {
  mode: 'new' | 'edit';
  initialTask: ScheduledTask | null;
  onCancel: () => void;
  onSaved: (newId?: number) => void | Promise<void>;
}

function ScheduleEditor({ mode, initialTask, onCancel, onSaved }: ScheduleEditorProps) {
  const { t } = useTranslation('schedules');
  const isEdit = mode === 'edit';
  const authState = useAuthState();
  const defaultVis: Visibility =
    (authState.mode === 'authenticated' ? authState.user?.defaultVisibility : undefined) ?? 'private';
  const savedOrgId: string | null =
    (authState.mode === 'authenticated' ? authState.user?.defaultVisibilityOrgId : undefined) ?? null;
  const { data: orgs = [] } = useQuery({
    queryKey: ['my-orgs'],
    queryFn: fetchMyOrgs,
    staleTime: 5 * 60 * 1000,
  });
  const { data: sessionProfiles = [] } = useQuery({
    queryKey: ['browser-session-profiles'],
    queryFn: listBrowserSessionProfiles,
    staleTime: 60 * 1000,
  });
  const activeSessionProfiles = sessionProfiles.filter(p => p.status === 'active');
  const [form, setForm] = useState<ScheduleFormState>(
    initialTask ? taskToFormState(initialTask) : blankFormState(defaultVis, savedOrgId)
  );
  const [error, setError] = useState('');
  const [submitting, setSubmitting] = useState(false);
  const titleRef = useRef<HTMLInputElement | null>(null);
  const { data: pieces = [] } = usePieceList();

  useEffect(() => {
    setForm(initialTask ? taskToFormState(initialTask) : blankFormState(defaultVis, savedOrgId));
    setError('');
  }, [initialTask, defaultVis, savedOrgId]);

  // Backfill scope when orgs load asynchronously (mirrors CreateTaskDialog)
  useEffect(() => {
    if (form.visibilityScopeOrgId !== null) return;
    if (orgs.length === 0) return;
    setForm(p => ({ ...p, visibilityScopeOrgId: orgs[0].orgId }));
  }, [orgs, form.visibilityScopeOrgId]);

  useEffect(() => {
    titleRef.current?.focus();
  }, []);

  useEffect(() => {
    const handler = (e: KeyboardEvent) => {
      if (e.key === 'Escape') onCancel();
    };
    document.addEventListener('keydown', handler);
    return () => document.removeEventListener('keydown', handler);
  }, [onCancel]);

  const pieceOptions = useMemo(() => {
    const opts = [{ value: 'auto', label: 'auto', description: t('editor.pieceAutoDescription') }];
    for (const p of resolvePieceOptions(pieces)) {
      if (p.name === 'auto') continue;
      opts.push({ value: p.name, label: p.name, description: p.description ?? '' });
    }
    return opts;
  }, [pieces, t]);

  const preview = buildCronPreview(form, t);

  const handleSubmit = async () => {
    if (form.taskKind === 'agent' && !form.body.trim()) {
      setError(t('errors.promptRequired'));
      return;
    }
    if (form.taskKind === 'script' && !form.scriptName.trim()) {
      setError(t('errors.scriptNameRequired'));
      return;
    }
    if (form.taskKind === 'script' && form.scriptParams.trim()) {
      try {
        const parsed = JSON.parse(form.scriptParams);
        if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
          throw new Error('params must be a JSON object');
        }
      } catch (e) {
        setError(t('errors.scriptParamsInvalid', { message: e instanceof Error ? e.message : String(e) }));
        return;
      }
    }
    try {
      setSubmitting(true);
      setError('');
      const payload: Record<string, unknown> = {
        ...form,
        visibilityScopeOrgId: form.visibility === 'org' ? form.visibilityScopeOrgId : null,
        scriptParams: form.taskKind === 'script' && form.scriptParams.trim() ? form.scriptParams : null,
      };
      if (form.scheduleType === 'once' && form.scheduledAt) {
        payload.scheduledAt = new Date(form.scheduledAt).toISOString();
      }

      if (isEdit && initialTask) {
        const res = await fetch(`/api/scheduled-tasks/${initialTask.id}`, {
          method: 'PATCH',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify(payload),
        });
        if (!res.ok) { const data = await res.json().catch(() => ({})); throw new Error(data.error ?? t('errors.saveFailed')); }
        await onSaved(initialTask.id);
      } else {
        const res = await fetch('/api/scheduled-tasks', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify(payload),
        });
        if (!res.ok) { const data = await res.json().catch(() => ({})); throw new Error(data.error ?? t('errors.createFailed')); }
        const body = await res.json().catch(() => ({}));
        const newId = body?.task?.id ?? body?.id ?? undefined;
        await onSaved(typeof newId === 'number' ? newId : undefined);
      }
    } catch (e) {
      setError(e instanceof Error ? e.message : String(e));
    } finally {
      setSubmitting(false);
    }
  };

  return (
    <div className="flex flex-col h-full overflow-hidden">
      <div className="flex-shrink-0 px-5 py-3.5 border-b border-hairline bg-canvas flex items-center justify-between gap-3">
        <div className="min-w-0">
          <div className="flex items-center gap-2.5 min-w-0">
            <div className="section-label font-mono flex-shrink-0">
              {isEdit && initialTask ? `SCHEDULE #${initialTask.id}` : 'NEW'}
            </div>
            <div className="text-sm font-semibold text-slate-900 truncate">
              {isEdit ? t('editor.editTitle') : t('editor.newTitle')}
            </div>
          </div>
          <div className="text-2xs text-slate-500 mt-0.5">
            {t('editor.subtitle')}
          </div>
        </div>
        <button
          type="button"
          onClick={onCancel}
          aria-label={t('editor.closeAria')}
          className="w-8 h-8 inline-flex items-center justify-center rounded-md text-slate-400 hover:text-slate-600 hover:bg-surface-2 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent-ring flex-shrink-0"
        >
          <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
            <path d="M18 6L6 18M6 6l12 12" />
          </svg>
        </button>
      </div>

      <div className="flex-1 overflow-y-auto px-6 py-5 bg-surface">
        <div className="max-w-[640px] mx-auto space-y-4">
          <div className="bg-canvas border border-hairline rounded-md p-5">
            <div className="section-label mb-3.5">
              {t('editor.basicInfo')}
            </div>
            <div className="space-y-3">
              <EditorFormRow label={t('editor.kind')} help={t('editor.kindHelp')}>
                <div className="flex gap-1.5">
                  {(['agent', 'script'] as const).map(k => {
                    const selected = form.taskKind === k;
                    return (
                      <button
                        key={k}
                        type="button"
                        onClick={() => setForm(p => ({ ...p, taskKind: k }))}
                        aria-pressed={selected}
                        className={`px-3 py-1.5 rounded-full text-xs font-semibold border transition-colors ${
                          selected ? 'border-accent bg-accent-soft text-accent' : 'border-hairline bg-canvas text-slate-600'
                        }`}
                      >
                        {k === 'agent' ? t('editor.kindAgent') : t('editor.kindScript')}
                      </button>
                    );
                  })}
                </div>
              </EditorFormRow>

              <EditorFormRow label={t('editor.title')}>
                <input
                  ref={titleRef}
                  value={form.title}
                  onChange={e => setForm(p => ({ ...p, title: e.target.value }))}
                  className={EDITOR_INPUT_CLASS}
                  placeholder={form.taskKind === 'script' ? t('editor.titlePlaceholderScript') : t('editor.titlePlaceholderAgent')}
                />
              </EditorFormRow>

              {form.taskKind === 'agent' ? (
                <>
                  <EditorFormRow label={t('editor.prompt')} help={t('editor.promptHelp')}>
                    <textarea
                      value={form.body}
                      onChange={e => setForm(p => ({ ...p, body: e.target.value }))}
                      rows={5}
                      className={`${EDITOR_INPUT_CLASS} resize-y leading-relaxed`}
                      placeholder={t('editor.promptPlaceholder')}
                    />
                  </EditorFormRow>

                  <div className="grid grid-cols-2 gap-3">
                    <EditorFormRow
                      label="Piece"
                      help={pieceOptions.find(o => o.value === form.piece)?.description || undefined}
                    >
                      <select
                        value={form.piece}
                        onChange={e => setForm(p => ({ ...p, piece: e.target.value }))}
                        className={`${EDITOR_INPUT_CLASS} font-mono`}
                      >
                        {pieceOptions.map(o => (
                          <option key={o.value} value={o.value}>{o.label}</option>
                        ))}
                      </select>
                    </EditorFormRow>
                    <EditorFormRow label={t('editor.outputFormat')}>
                      <select
                        value={form.outputFormat}
                        onChange={e => setForm(p => ({ ...p, outputFormat: e.target.value }))}
                        className={`${EDITOR_INPUT_CLASS} font-mono`}
                      >
                        {OUTPUT_FORMAT_OPTIONS.map(o => (
                          <option key={o.value} value={o.value}>{o.label}</option>
                        ))}
                      </select>
                    </EditorFormRow>
                  </div>
                </>
              ) : (
                <>
                  <EditorFormRow
                    label={t('editor.scriptName')}
                    help={t('editor.scriptNameHelp')}
                  >
                    <input
                      value={form.scriptName}
                      onChange={e => setForm(p => ({ ...p, scriptName: e.target.value }))}
                      className={`${EDITOR_INPUT_CLASS} font-mono`}
                      placeholder="weekly-report"
                    />
                  </EditorFormRow>
                  <EditorFormRow
                    label="params (JSON)"
                    help={t('editor.scriptParamsHelp')}
                  >
                    <textarea
                      value={form.scriptParams}
                      onChange={e => setForm(p => ({ ...p, scriptParams: e.target.value }))}
                      rows={4}
                      className={`${EDITOR_INPUT_CLASS} font-mono resize-y leading-relaxed`}
                      placeholder='{"date":"2026-05-11"}'
                    />
                  </EditorFormRow>
                </>
              )}
            </div>
          </div>

          <div className="bg-canvas border border-hairline rounded-md p-5">
            <div className="section-label mb-3.5">
              {t('editor.scheduleSection')}
            </div>

            <EditorFormRow label={t('editor.type')}>
              <div className="flex flex-wrap gap-1.5">
                {SCHEDULE_TYPE_OPTIONS.map(opt => {
                  const selected = form.scheduleType === opt.value;
                  return (
                    <button
                      key={opt.value}
                      type="button"
                      onClick={() => setForm(p => ({ ...p, scheduleType: opt.value }))}
                      aria-pressed={selected}
                      title={t(opt.hintKey)}
                      className={`px-3 py-1.5 rounded-full text-xs font-semibold border transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent-ring ${
                        selected
                          ? 'border-accent bg-accent-soft text-accent'
                          : 'border-hairline bg-canvas text-slate-600 hover:border-hairline'
                      }`}
                    >
                      {opt.labelKey ? t(opt.labelKey) : opt.label}
                    </button>
                  );
                })}
              </div>
            </EditorFormRow>

            {form.scheduleType !== 'cron' && form.scheduleType !== 'once' && (
              <div className="grid grid-cols-2 gap-3 mt-3">
                <EditorFormRow label={t('editor.time')}>
                  <div className="flex items-center gap-1">
                    <input
                      type="number"
                      min={0}
                      max={23}
                      value={form.hour}
                      onChange={e => setForm(p => ({ ...p, hour: Number(e.target.value) }))}
                      className={`${EDITOR_INPUT_CLASS} w-16 text-center font-mono`}
                    />
                    <span className="text-slate-400">:</span>
                    <input
                      type="number"
                      min={0}
                      max={59}
                      value={form.minute}
                      onChange={e => setForm(p => ({ ...p, minute: Number(e.target.value) }))}
                      className={`${EDITOR_INPUT_CLASS} w-16 text-center font-mono`}
                    />
                  </div>
                </EditorFormRow>
                {form.scheduleType === 'weekly' && (
                  <EditorFormRow label={t('editor.dayOfWeek')}>
                    <select
                      value={form.dayOfWeek}
                      onChange={e => setForm(p => ({ ...p, dayOfWeek: Number(e.target.value) }))}
                      className={EDITOR_INPUT_CLASS}
                    >
                      {DAY_KEYS.map((d, i) => <option key={i} value={i}>{t(`dayOptions.${d}`)}</option>)}
                    </select>
                  </EditorFormRow>
                )}
                {form.scheduleType === 'monthly' && (
                  <EditorFormRow label={t('editor.dayOfMonth')}>
                    <input
                      type="number"
                      min={1}
                      max={31}
                      value={form.dayOfMonth}
                      onChange={e => setForm(p => ({ ...p, dayOfMonth: Number(e.target.value) }))}
                      className={EDITOR_INPUT_CLASS}
                    />
                  </EditorFormRow>
                )}
              </div>
            )}

            {form.scheduleType === 'cron' && (
              <div className="mt-3">
                <EditorFormRow label={t('editor.cronExpression')} help={t('editor.cronHelp')}>
                  <input
                    value={form.cronExpression}
                    onChange={e => setForm(p => ({ ...p, cronExpression: e.target.value }))}
                    className={`${EDITOR_INPUT_CLASS} font-mono`}
                    placeholder="0 9 * * 1"
                  />
                </EditorFormRow>
              </div>
            )}

            {form.scheduleType === 'once' && (
              <div className="mt-3">
                <EditorFormRow label={t('editor.runAt')}>
                  <input
                    type="datetime-local"
                    value={form.scheduledAt}
                    onChange={e => setForm(p => ({ ...p, scheduledAt: e.target.value }))}
                    className={EDITOR_INPUT_CLASS}
                  />
                </EditorFormRow>
              </div>
            )}

            {preview && form.scheduleType !== 'once' && (
              <div className="mt-3 px-3 py-2.5 bg-surface border border-hairline rounded-md text-xs text-slate-600">
                <span className="text-[10px] font-bold text-slate-500 uppercase tracking-wide mr-2">
                  {t('editor.preview')}
                </span>
                <b className="font-semibold text-slate-900">{preview}</b>
              </div>
            )}
          </div>

          {authState.mode === 'authenticated' && (
            <div className="bg-canvas border border-hairline rounded-md p-5">
              <div className="section-label mb-3.5">
                {t('editor.visibilitySection')}
              </div>
              <div className="flex flex-col gap-2 text-[13px]">
                <label className="inline-flex items-center gap-2">
                  <input
                    type="radio"
                    checked={form.visibility === 'private'}
                    onChange={() => setForm(p => ({ ...p, visibility: 'private' }))}
                  />
                  <span>{t('editor.visPrivate')}</span>
                </label>
                <label className="inline-flex items-center gap-2">
                  <input
                    type="radio"
                    checked={form.visibility === 'org'}
                    onChange={() => setForm(p => ({ ...p, visibility: 'org' }))}
                    disabled={orgs.length === 0}
                  />
                  <span>{t('editor.visOrg')}</span>
                </label>
                <label className="inline-flex items-center gap-2">
                  <input
                    type="radio"
                    checked={form.visibility === 'public'}
                    onChange={() => setForm(p => ({ ...p, visibility: 'public' }))}
                  />
                  <span>{t('editor.visPublic')}</span>
                </label>
                {form.visibility === 'org' && orgs.length > 1 && (
                  <select
                    className={`${EDITOR_INPUT_CLASS} mt-1`}
                    value={form.visibilityScopeOrgId ?? ''}
                    onChange={e => setForm(p => ({ ...p, visibilityScopeOrgId: e.target.value || null }))}
                  >
                    {orgs.map(o => (
                      <option key={o.orgId} value={o.orgId}>{o.orgName}</option>
                    ))}
                  </select>
                )}
                {form.visibility === 'org' && orgs.length === 1 && (
                  <div className="text-2xs text-slate-500 mt-1">→ {orgs[0].orgName}</div>
                )}
                {form.visibility === 'org' && orgs.length === 0 && (
                  <div className="text-2xs text-amber-700 dark:text-amber-300 mt-1">
                    {t('editor.noOrg')}
                  </div>
                )}
              </div>
            </div>
          )}

          {activeSessionProfiles.length > 0 && (
            <div className="bg-canvas border border-hairline rounded-md p-5">
              <div className="section-label mb-3.5">
                {t('editor.browserSession')}
              </div>
              <select
                value={form.browserSessionProfileId ?? ''}
                onChange={e =>
                  setForm(p => ({
                    ...p,
                    browserSessionProfileId: e.target.value ? Number(e.target.value) : null,
                  }))
                }
                className={EDITOR_INPUT_CLASS}
              >
                <option value="">{t('editor.none')}</option>
                {activeSessionProfiles.map(p => (
                  <option key={p.id} value={p.id}>{p.label}</option>
                ))}
              </select>
              <p className="text-2xs text-slate-500 mt-1">
                {t('editor.browserSessionHelp')}
              </p>
            </div>
          )}

          {error && (
            <div
              role="alert"
              className="px-3.5 py-2.5 bg-red-50 dark:bg-red-500/15 border border-red-200 dark:border-red-500/30 rounded-md text-xs text-red-700 dark:text-red-300"
            >
              ⚠ {error}
            </div>
          )}

          <div className="h-4" />
        </div>
      </div>

      <div className="flex-shrink-0 px-6 py-3.5 border-t border-hairline flex justify-end gap-2 bg-canvas">
        <button
          type="button"
          onClick={onCancel}
          className="px-4 py-2 text-[13px] font-semibold text-slate-600 rounded-md hover:bg-surface-2 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent-ring"
        >
          {t('editor.cancel')}
        </button>
        <button
          type="button"
          onClick={() => void handleSubmit()}
          disabled={submitting || !form.body.trim()}
          className="px-4 py-2 bg-accent hover:bg-accent-deep text-accent-fg text-[13px] font-semibold rounded-md disabled:opacity-50 disabled:cursor-not-allowed transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent-ring"
        >
          {submitting ? (isEdit ? t('editor.saving') : t('editor.creating')) : (isEdit ? t('editor.save') : t('editor.create'))}
        </button>
      </div>
    </div>
  );
}
