import React, { useState, useEffect, useRef, useMemo } from 'react';
import {
  AlarmClock,
  Plus,
  X,
  MoreHorizontal,
  ChevronDown,
  ChevronUp,
  Check,
} from 'lucide-react';
import type { CronJob } from '@/types/api';
import {
  getCronJobs,
  addCronJob,
  deleteCronJob,
  patchCronJob,
  runCronJobNow,
  pauseCronJob,
  resumeCronJob,
  getStatus,
  getModels,
} from '@/lib/api';
import { assetBasePath } from '@/lib/basePath';
import { t } from '@/lib/i18n';

function formatDate(iso: string | null): string {
  if (!iso) return '-';
  const d = new Date(iso);
  return `${d.getFullYear()}/${d.getMonth() + 1}/${d.getDate()} ${String(d.getHours()).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')}:${String(d.getSeconds()).padStart(2, '0')}`;
}

// ---------- Toggle Switch ----------
function Toggle({ checked, onChange, disabled }: { checked: boolean; onChange: () => void; disabled?: boolean }) {
  return (
    <button
      onClick={onChange}
      disabled={disabled}
      style={{
        position: 'relative',
        display: 'inline-flex',
        alignItems: 'center',
        width: 40,
        height: 22,
        borderRadius: 11,
        background: checked ? '#1890ff' : '#d9d9d9',
        border: 'none',
        cursor: disabled ? 'not-allowed' : 'pointer',
        transition: 'background 0.25s',
        flexShrink: 0,
        opacity: disabled ? 0.5 : 1,
        padding: 0,
      }}
    >
      <span style={{
        position: 'absolute',
        left: checked ? 20 : 2,
        width: 18,
        height: 18,
        borderRadius: '50%',
        background: '#fff',
        boxShadow: '0 1px 4px rgba(0,0,0,0.2)',
        transition: 'left 0.25s',
      }} />
    </button>
  );
}

// ---------- Status Badge ----------
function StatusBadge({ status }: { status: string | null }) {
  if (!status) return <span style={{ color: '#bbb', fontSize: 13 }}>-</span>;
  const s = status.toLowerCase();
  const ok = s === 'ok' || s === 'success';
  const color = ok ? '#00cc66' : '#ff4d4f';
  const bgColor = ok ? 'rgba(0, 204, 102, 0.1)' : 'rgba(255, 77, 79, 0.1)';
  const label = ok ? t('cron.status_success') : t('cron.status_failed');

  return (
    <span style={{
      display: 'inline-flex',
      alignItems: 'center',
      gap: 4,
      color,
      background: bgColor,
      padding: '3px 8px',
      borderRadius: 12,
      fontSize: 12,
      fontWeight: 500
    }}>
      <svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg" style={{ flexShrink: 0 }}>
        <circle cx="7" cy="7" r="7" fill={color} />
        {ok ? (
          <path d="M4.5 7.5L6 9L9.5 5" stroke="#fff" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
        ) : (
          <path d="M4.5 4.5L9.5 9.5M9.5 4.5L4.5 9.5" stroke="#fff" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
        )}
      </svg>
      {label}
    </span>
  );
}

// ---------- Row Dropdown Menu ----------
function RowMenu({ onEdit, onDelete, onRunNow }: {
  onEdit: () => void;
  onDelete: () => void;
  onRunNow: () => void;
}) {
  const [open, setOpen] = useState(false);
  const ref = useRef<HTMLDivElement>(null);

  useEffect(() => {
    if (!open) return;
    const handler = (e: MouseEvent) => {
      if (ref.current && !ref.current.contains(e.target as Node)) setOpen(false);
    };
    document.addEventListener('mousedown', handler);
    return () => document.removeEventListener('mousedown', handler);
  }, [open]);

  const item = (label: string, action: () => void) => (
    <button
      onClick={() => { action(); setOpen(false); }}
      style={{
        display: 'flex', alignItems: 'center',
        width: 164, height: 36, padding: '0 12px', margin: '0 8px',
        background: 'none', border: 'none', cursor: 'pointer',
        fontSize: 13, color: 'rgba(0, 0, 0, 0.85)',
        textAlign: 'left', borderRadius: 6,
        transition: 'background 0.15s',
      }}
      onMouseEnter={e => (e.currentTarget.style.background = '#DCDCDC')}
      onMouseLeave={e => (e.currentTarget.style.background = 'none')}
    >
      {label}
    </button>
  );

  return (
    <div ref={ref} style={{ position: 'relative', display: 'inline-block' }}>
      <button
        onClick={() => setOpen(v => !v)}
        style={{
          background: 'none', border: 'none', cursor: 'pointer',
          padding: '4px 6px', borderRadius: 6, color: '#999',
          display: 'inline-flex', alignItems: 'center',
        }}
      >
        <MoreHorizontal size={18} />
      </button>
      {open && (
        <div style={{
          position: 'absolute', right: 0, top: '100%', zIndex: 100,
          background: '#fff', border: '1px solid #e8e8e8',
          borderRadius: 8, boxShadow: '0 4px 16px rgba(0,0,0,0.12)',
          width: 180, height: 128, padding: '10px 0',
          boxSizing: 'border-box',
        }}>
          {item(t('cron.run_now'), onRunNow)}
          {item(t('cron.edit'), onEdit)}
          {item(t('cron.delete'), onDelete)}
        </div>
      )}
    </div>
  );
}

// ---------- Schedule helpers ----------
type CronScheduleInput = Parameters<typeof addCronJob>[0]['schedule'];

function getCronCurrentTimeHHMM(): string {
  const now = new Date();
  return `${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}`;
}

function formatLocalYmd(d: Date): string {
  const y = d.getFullYear();
  const m = String(d.getMonth() + 1).padStart(2, '0');
  const day = String(d.getDate()).padStart(2, '0');
  return `${y}-${m}-${day}`;
}

/** Display `YYYY-MM-DD` as `YYYY/MM/DD` (matches once-schedule UI spec). */
function formatYmdSlashes(ymd: string): string {
  const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(ymd.trim());
  if (!m) return ymd;
  const [, y, mo, da] = m;
  if (y === undefined || mo === undefined || da === undefined) return ymd;
  return `${y}/${mo}/${da}`;
}

function bumpYmdByDays(ymd: string, deltaDays: number): string {
  const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(ymd.trim());
  if (!m) return formatLocalYmd(new Date());
  const [, ys, mos, ds] = m;
  if (ys === undefined || mos === undefined || ds === undefined) return formatLocalYmd(new Date());
  const y = parseInt(ys, 10);
  const mo = parseInt(mos, 10) - 1;
  const d = parseInt(ds, 10);
  const dt = new Date(y, mo, d);
  dt.setDate(dt.getDate() + deltaDays);
  return formatLocalYmd(dt);
}

function parseCronExprToUI(expr: string): { time: string; weekdays: number[] } {
  const parts = (expr ?? '').trim().split(/\s+/);
  if (parts.length >= 5) {
    const minutePart = parts[0] ?? '0';
    const hourPart = parts[1] ?? '0';
    const wdStr = parts[4] ?? '*';
    const time = `${hourPart.padStart(2, '0')}:${minutePart.padStart(2, '0')}`;
    const weekdays =
      wdStr === '*'
        ? [1, 2, 3, 4, 5, 6, 7]
        : wdStr
          .split(',')
          .map(n => {
            const d = parseInt(n, 10);
            return d === 0 ? 7 : d;
          })
          .filter(d => d >= 1 && d <= 7);
    return { time, weekdays };
  }
  return { time: getCronCurrentTimeHHMM(), weekdays: [1, 2, 3, 4, 5, 6, 7] };
}

// ---------- Weekday Chips ----------
function WeekdayChips({
  weekdays,
  onChange,
}: {
  weekdays: number[];
  onChange: (next: number[]) => void;
}) {
  const days = [1, 2, 3, 4, 5, 6, 7];
  const keys: Record<number, string> = {
    1: 'cron.weekday_1',
    2: 'cron.weekday_2',
    3: 'cron.weekday_3',
    4: 'cron.weekday_4',
    5: 'cron.weekday_5',
    6: 'cron.weekday_6',
    7: 'cron.weekday_7',
  };
  return (
    <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap', marginTop: 8 }}>
      {days.map(day => {
        const selected = weekdays.includes(day);
        const dayKey = keys[day] ?? 'cron.weekday_1';
        return (
          <button
            key={day}
            type="button"
            onClick={() => {
              const next = selected
                ? weekdays.filter(d => d !== day)
                : [...weekdays, day].sort((a, b) => a - b);
              onChange(next);
            }}
            style={{
              boxSizing: 'border-box',
              display: 'inline-flex',
              alignItems: 'center',
              justifyContent: 'center',
              height: 36,
              padding: '0 8px',
              width: 'auto',
              borderRadius: 6,
              border: 'none',
              background: selected ? 'rgba(55, 144, 250, 0.1)' : 'rgb(230, 230, 230)',
              color: selected ? '#3790FA' : 'rgba(0, 0, 0, 0.85)',
              fontSize: 14,
              fontWeight: 400,
              cursor: 'pointer',
              transition: 'background 0.15s',
            }}
          >
            {t(dayKey)}
          </button>
        );
      })}
    </div>
  );
}

// ---------- Execution-time schedule kind (custom dropdown per design spec) ----------
const SCHEDULE_KIND_TEXT = 'rgba(0, 0, 0, 0.85)';
const SCHEDULE_KIND_ROW_SELECTED_BG = 'rgba(0, 0, 0, 0.15)';

/** String list picker: same visuals as execution-time schedule kind dropdown. */
function CronModalListSelect({
  value,
  onChange,
  options,
  triggerStyle,
  containerStyle,
}: {
  value: string;
  onChange: (next: string) => void;
  options: ReadonlyArray<{ value: string; label: string }>;
  triggerStyle: React.CSSProperties;
  containerStyle?: React.CSSProperties;
}) {
  const [open, setOpen] = useState(false);
  const wrapRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    if (!open) return;
    const handler = (e: MouseEvent) => {
      if (wrapRef.current && !wrapRef.current.contains(e.target as Node)) setOpen(false);
    };
    document.addEventListener('mousedown', handler);
    return () => document.removeEventListener('mousedown', handler);
  }, [open]);

  const activeLabel = options.find(o => o.value === value)?.label ?? (value !== '' ? value : '');

  const triggerMerged: React.CSSProperties = {
    ...triggerStyle,
    padding: '0 12px',
    display: 'flex',
    alignItems: 'center',
    gap: 8,
    width: '100%',
    justifyContent: 'space-between',
    textAlign: 'left',
    cursor: 'pointer',
    fontFamily: 'inherit',
  };

  const wrapOuter: React.CSSProperties = {
    position: 'relative',
    width: '100%',
    ...containerStyle,
  };

  return (
    <div ref={wrapRef} style={wrapOuter}>
      <button
        type="button"
        aria-expanded={open}
        aria-haspopup="listbox"
        onClick={() => setOpen(o => !o)}
        style={triggerMerged}
      >
        <span
          style={{
            overflow: 'hidden',
            textOverflow: 'ellipsis',
            whiteSpace: 'nowrap',
            flex: 1,
            minWidth: 0,
            color: SCHEDULE_KIND_TEXT,
          }}
        >
          {activeLabel}
        </span>
        <ChevronDown size={16} style={{ flexShrink: 0, color: SCHEDULE_KIND_TEXT }} aria-hidden />
      </button>

      {open && (
        <div
          role="listbox"
          style={{
            position: 'absolute',
            left: 0,
            right: 0,
            top: '100%',
            marginTop: 4,
            background: '#fff',
            borderRadius: 8,
            border: '1px solid rgba(0, 0, 0, 0.06)',
            boxShadow: '0 4px 16px rgba(0, 0, 0, 0.12)',
            zIndex: 400,
            padding: '6px 8px',
          }}
        >
          {options.map((opt, i) => {
            const selected = value === opt.value;
            return (
              <button
                key={`${opt.value}:${i}:${opt.label}`}
                role="option"
                type="button"
                aria-selected={selected}
                onClick={() => {
                  onChange(opt.value);
                  setOpen(false);
                }}
                style={{
                  width: '100%',
                  height: 36,
                  padding: '0 8px',
                  border: 'none',
                  cursor: 'pointer',
                  fontSize: 14,
                  fontWeight: 400,
                  fontFamily: 'inherit',
                  color: SCHEDULE_KIND_TEXT,
                  background: selected ? SCHEDULE_KIND_ROW_SELECTED_BG : 'transparent',
                  display: 'flex',
                  alignItems: 'center',
                  gap: 4,
                  boxSizing: 'border-box',
                  borderRadius: 6,
                  marginBottom: i < options.length - 1 ? 2 : 0,
                }}
              >
                <span
                  style={{
                    width: 20,
                    flexShrink: 0,
                    display: 'flex',
                    alignItems: 'center',
                    justifyContent: 'center',
                  }}
                >
                  {selected ? (
                    <Check
                      size={14}
                      strokeWidth={2.5}
                      color={SCHEDULE_KIND_TEXT}
                      aria-hidden
                    />
                  ) : null}
                </span>
                <span style={{ flex: 1, textAlign: 'left' }}>{opt.label}</span>
              </button>
            );
          })}
        </div>
      )}
    </div>
  );
}

function ScheduleKindSelect({
  value,
  onChange,
  triggerStyle,
}: {
  value: 'daily' | 'interval' | 'once';
  onChange: (next: 'daily' | 'interval' | 'once') => void;
  triggerStyle: React.CSSProperties;
}) {
  return (
    <CronModalListSelect
      value={value}
      onChange={next =>
        next === 'daily' || next === 'interval' || next === 'once' ? onChange(next) : undefined
      }
      triggerStyle={triggerStyle}
      options={[
        { value: 'daily', label: t('cron.schedule_daily_label') },
        { value: 'interval', label: t('cron.schedule_interval_label') },
        { value: 'once', label: t('cron.schedule_once_label') },
      ]}
    />
  );
}

/** Minute / hour picker for interval mode — execution-time dropdown visuals */
function IntervalUnitSelect({
  value,
  onChange,
  triggerStyle,
}: {
  value: 'minute' | 'hour';
  onChange: (next: 'minute' | 'hour') => void;
  triggerStyle: React.CSSProperties;
}) {
  return (
    <CronModalListSelect
      value={value}
      containerStyle={{ flex: 1, minWidth: 0 }}
      onChange={next => (next === 'minute' || next === 'hour' ? onChange(next) : undefined)}
      triggerStyle={triggerStyle}
      options={[
        { value: 'minute', label: t('cron.interval_unit_minute') },
        { value: 'hour', label: t('cron.interval_unit_hour') },
      ]}
    />
  );
}

// ---------- Add/Edit Modal ----------
function CronModal({
  job,
  onClose,
  onSaved,
}: {
  job: CronJob | 'add';
  onClose: () => void;
  onSaved: (updated: CronJob) => void;
}) {
  const isEdit = job !== 'add';
  const existing = isEdit ? (job as CronJob) : null;

  // Derive initial values from existing job
  const initKind = (): 'daily' | 'interval' | 'once' => {
    if (!existing) return 'daily';
    if (existing.schedule.kind === 'cron') return 'daily';
    if (existing.schedule.kind === 'every') return 'interval';
    return 'once';
  };

  const cronParsed =
    existing?.schedule.kind === 'cron' && existing.schedule.expr
      ? parseCronExprToUI(existing.schedule.expr)
      : { time: getCronCurrentTimeHHMM(), weekdays: [1, 2, 3, 4, 5, 6, 7] };

  const initIntervalValue = (): string => {
    if (existing?.schedule.kind === 'every' && existing.schedule.every_ms) {
      const ms = existing.schedule.every_ms;
      if (ms % 3600000 === 0) return String(ms / 3600000);
      if (ms % 60000 === 0) return String(ms / 60000);
      return String(Math.max(1, Math.round(ms / 60000)));
    }
    return '5';
  };

  const initIntervalUnit = (): 'minute' | 'hour' => {
    if (existing?.schedule.kind === 'every' && existing.schedule.every_ms) {
      const ms = existing.schedule.every_ms;
      if (ms % 3600000 === 0) return 'hour';
      return 'minute';
    }
    return 'minute';
  };

  // Form state
  const [formName, setFormName] = useState(existing?.name ?? '');
  const [formPrompt, setFormPrompt] = useState(existing?.prompt ?? existing?.command ?? '');
  const [formScheduleKind, setFormScheduleKind] = useState<'daily' | 'interval' | 'once'>(
    initKind(),
  );
  // Daily
  const [formDailyTime, setFormDailyTime] = useState(cronParsed.time);
  const [formDailyWeekdays, setFormDailyWeekdays] = useState<number[]>(cronParsed.weekdays);
  // Interval
  const [formIntervalValue, setFormIntervalValue] = useState(initIntervalValue());
  const [formIntervalUnit, setFormIntervalUnit] = useState<'minute' | 'hour'>(initIntervalUnit());
  const [formIntervalWeekdays, setFormIntervalWeekdays] = useState<number[]>([
    1, 2, 3, 4, 5, 6, 7,
  ]);
  const initOnceTime = (): string => {
    if (existing?.schedule.kind === 'at' && existing.schedule.at) {
      const d = new Date(existing.schedule.at);
      return `${String(d.getHours()).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')}`;
    }
    return getCronCurrentTimeHHMM();
  };

  const initOnceDate = (): string => {
    if (existing?.schedule.kind === 'at' && existing.schedule.at) {
      return formatLocalYmd(new Date(existing.schedule.at));
    }
    return formatLocalYmd(new Date());
  };

  // Once — separate time + date (defaults: current local time / date)
  const [formOnceTime, setFormOnceTime] = useState(initOnceTime());
  const [formOnceDate, setFormOnceDate] = useState(initOnceDate());
  // Channel & model
  const [formChannel, setFormChannel] = useState<string>(existing?.session_target ?? 'main');
  const [formModel, setFormModel] = useState<string>(existing?.model ?? '');
  const [availableChannels, setAvailableChannels] = useState<string[]>([]);
  const [availableModels, setAvailableModels] = useState<string[]>([]);
  const [formError, setFormError] = useState<string | null>(null);
  const [submitting, setSubmitting] = useState(false);

  // Dirty detection: capture initial snapshot for edit mode
  const initialSnapshot = useRef({
    name: existing?.name ?? '',
    prompt: existing?.prompt ?? existing?.command ?? '',
    scheduleKind: initKind(),
    dailyTime: cronParsed.time,
    dailyWeekdays: [...cronParsed.weekdays],
    intervalValue: initIntervalValue(),
    intervalUnit: initIntervalUnit(),
    intervalWeekdays: [1, 2, 3, 4, 5, 6, 7],
    onceTime: initOnceTime(),
    onceDate: initOnceDate(),
    channel: existing?.session_target ?? 'main',
    model: existing?.model ?? '',
  });

  const canSubmit = useMemo(() => {
    if (!isEdit) {
      // Add mode: enabled only when name and prompt are both non-empty
      return formName.trim().length > 0 && formPrompt.trim().length > 0;
    }
    const s = initialSnapshot.current;
    return (
      formName !== s.name ||
      formPrompt !== s.prompt ||
      formScheduleKind !== s.scheduleKind ||
      formDailyTime !== s.dailyTime ||
      formDailyWeekdays.length !== s.dailyWeekdays.length ||
      formDailyWeekdays.some((v, i) => v !== s.dailyWeekdays[i]) ||
      formIntervalValue !== s.intervalValue ||
      formIntervalUnit !== s.intervalUnit ||
      formIntervalWeekdays.length !== s.intervalWeekdays.length ||
      formIntervalWeekdays.some((v, i) => v !== s.intervalWeekdays[i]) ||
      formOnceTime !== s.onceTime ||
      formOnceDate !== s.onceDate ||
      formChannel !== s.channel ||
      formModel !== s.model
    );
  }, [
    isEdit,
    formName,
    formPrompt,
    formScheduleKind,
    formDailyTime,
    formDailyWeekdays,
    formIntervalValue,
    formIntervalUnit,
    formIntervalWeekdays,
    formOnceTime,
    formOnceDate,
    formChannel,
    formModel,
  ]);

  useEffect(() => {
    getStatus()
      .then(status => {
        const enabled = Object.entries(status.channels)
          .filter(([, on]) => on)
          .map(([name]) => name);
        setAvailableChannels(enabled);
        if (!isEdit && status.model) setFormModel(status.model);
      })
      .catch(() => { });
    getModels()
      .then(resp => setAvailableModels(resp.models.map(m => m.model)))
      .catch(() => { });
  }, []); // eslint-disable-line react-hooks/exhaustive-deps

  const handleSubmit = async () => {
    if (!formPrompt.trim()) {
      setFormError(t('cron.validation_error'));
      return;
    }
    let schedule: CronScheduleInput;
    if (formScheduleKind === 'daily') {
      const [hStr = '0', mStr = '0'] = formDailyTime.split(':');
      const h = parseInt(hStr, 10);
      const m = parseInt(mStr, 10);
      const days =
        formDailyWeekdays.length === 7
          ? '*'
          : formDailyWeekdays.map(d => (d === 7 ? 0 : d)).join(',');
      schedule = { kind: 'cron', expr: `${m} ${h} * * ${days}` };
    } else if (formScheduleKind === 'interval') {
      const unitMs = formIntervalUnit === 'minute' ? 60000 : 3600000;
      const val = parseInt(formIntervalValue, 10);
      if (!Number.isFinite(val) || val <= 0) {
        setFormError(t('cron.validation_error'));
        return;
      }
      schedule = { kind: 'every', every_ms: val * unitMs };
    } else {
      const ymd = formOnceDate.trim();
      const hhmm = formOnceTime.trim();
      if (!/^\d{4}-\d{2}-\d{2}$/.test(ymd) || !/^\d{2}:\d{2}$/.test(hhmm)) {
        setFormError(t('cron.validation_error'));
        return;
      }
      const atLocal = new Date(`${ymd}T${hhmm}:00`);
      if (Number.isNaN(atLocal.getTime())) {
        setFormError(t('cron.validation_error'));
        return;
      }
      schedule = { kind: 'at', at: atLocal.toISOString() };
    }

    setSubmitting(true);
    setFormError(null);
    try {
      const payload = {
        name: formName.trim() || undefined,
        schedule,
        prompt: formPrompt.trim(),
        job_type: 'agent' as const,
        session_target: (formChannel === 'main' ? 'main' : 'isolated') as 'main' | 'isolated',
        model: formModel.trim() || undefined,
        enabled: true,
      };
      let result: CronJob;
      if (isEdit) {
        result = await patchCronJob((existing as CronJob).id, payload);
      } else {
        result = await addCronJob(payload);
      }
      onSaved(result);
    } catch (err) {
      setFormError(
        err instanceof Error ? err.message : t(isEdit ? 'cron.edit_error' : 'cron.add_error'),
      );
    } finally {
      setSubmitting(false);
    }
  };

  // Shared styles
  const fieldLabel: React.CSSProperties = {
    display: 'block',
    fontSize: 14,
    color: 'rgba(0,0,0,0.85)',
    marginBottom: 6,
    fontWeight: 400,
  };
  const inputBase: React.CSSProperties = {
    width: '100%',
    boxSizing: 'border-box',
    height: 36,
    padding: '0 12px',
    border: 'none',
    borderRadius: 6,
    fontSize: 13,
    color: 'rgba(0,0,0,0.85)',
    background: '#E6E6E6',
    outline: 'none',
  };
  const selStyle: React.CSSProperties = {
    ...inputBase,
    padding: '0 36px 0 12px',
    cursor: 'pointer',
    appearance: 'none',
  };

  const bumpDailyMinutes = (delta: number) => {
    const [hs = '0', ms = '0'] = formDailyTime.split(':');
    let total = parseInt(hs, 10) * 60 + parseInt(ms, 10) + delta;
    total = ((total % 1440) + 1440) % 1440;
    const nh = Math.floor(total / 60);
    const nm = total % 60;
    setFormDailyTime(`${String(nh).padStart(2, '0')}:${String(nm).padStart(2, '0')}`);
  };

  const bumpOnceMinutes = (delta: number) => {
    const [hs = '0', ms = '0'] = formOnceTime.split(':');
    let total = parseInt(hs, 10) * 60 + parseInt(ms, 10) + delta;
    total = ((total % 1440) + 1440) % 1440;
    const nh = Math.floor(total / 60);
    const nm = total % 60;
    setFormOnceTime(`${String(nh).padStart(2, '0')}:${String(nm).padStart(2, '0')}`);
  };

  const bumpOnceDateByDays = (delta: number) => {
    setFormOnceDate(prev => bumpYmdByDays(prev, delta));
  };

  const bumpIntervalValue = (delta: number) => {
    const raw = parseInt(formIntervalValue, 10);
    const base = Number.isFinite(raw) && raw >= 1 ? raw : 1;
    setFormIntervalValue(String(Math.max(1, base + delta)));
  };

  return (
    <div
      style={{
        position: 'fixed',
        inset: 0,
        background: 'rgba(0,0,0,0.45)',
        display: 'flex',
        alignItems: 'center',
        justifyContent: 'center',
        zIndex: 200,
      }}
    >
      <style>{`
        .cron-add-task-modal input::-webkit-input-placeholder,
        .cron-add-task-modal textarea::-webkit-input-placeholder {
          color: rgba(0, 0, 0, 0.35) !important;
          -webkit-text-fill-color: rgba(0, 0, 0, 0.35) !important;
          opacity: 1 !important;
        }
        .cron-add-task-modal input::-moz-placeholder,
        .cron-add-task-modal textarea::-moz-placeholder {
          color: rgba(0, 0, 0, 0.35) !important;
          opacity: 1 !important;
        }
        .cron-add-task-modal input:-ms-input-placeholder,
        .cron-add-task-modal textarea:-ms-input-placeholder {
          color: rgba(0, 0, 0, 0.35) !important;
        }
        .cron-add-task-modal input::placeholder,
        .cron-add-task-modal textarea::placeholder {
          color: rgba(0, 0, 0, 0.35) !important;
          opacity: 1 !important;
        }
        .cron-add-task-modal input.cron-daily-time-input::-webkit-calendar-picker-indicator,
        .cron-add-task-modal input.cron-once-time-input::-webkit-calendar-picker-indicator {
          display: none;
        }
        .cron-add-task-modal input.cron-daily-time-input::-webkit-clear-button,
        .cron-add-task-modal input.cron-once-time-input::-webkit-clear-button {
          display: none;
        }
        .cron-add-task-modal input.cron-interval-number-input::-webkit-outer-spin-button,
        .cron-add-task-modal input.cron-interval-number-input::-webkit-inner-spin-button {
          -webkit-appearance: none;
          margin: 0;
        }
        .cron-add-task-modal input.cron-interval-number-input {
          appearance: none;
          -moz-appearance: textfield;
        }
      `}</style>
      <div
        className="cron-add-task-modal"
        style={{
          background: '#fff',
          borderRadius: 14,
          width: 424,
          maxHeight: 656,
          display: 'flex',
          flexDirection: 'column',
          boxShadow: '0 8px 32px rgba(0,0,0,0.18)',
        }}
      >
        {/* Header */}
        <div
          style={{
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'space-between',
            padding: '16px 20px 12px 8px',
          }}
        >
          <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
            <img
              src={`${assetBasePath}/kylin-bot.svg`}
              width={24}
              height={24}
              style={{ borderRadius: 6, flexShrink: 0 }}
              alt=""
            />
            <span style={{ fontSize: 14, fontWeight: 600, color: 'rgba(0,0,0,0.85)' }}>
              {isEdit ? t('cron.edit_modal_title') : t('cron.add_modal_title')}
            </span>
          </div>
          <button
            onClick={onClose}
            style={{
              background: 'none',
              border: 'none',
              cursor: 'pointer',
              color: 'rgba(0, 0, 0, 0.85)',
              padding: 4,
              display: 'flex',
              alignItems: 'center',
            }}
          >
            <X size={18} />
          </button>
        </div>

        {/* Body */}
        <div style={{ padding: '0 24px 4px', overflowY: 'auto', flex: 1 }}>
          {formError && (
            <div
              style={{
                background: '#fff2f0',
                border: '1px solid #ffa39e',
                borderRadius: 6,
                padding: '8px 12px',
                marginBottom: 12,
                fontSize: 13,
                color: '#cf1322',
              }}
            >
              {formError}
            </div>
          )}
          <div style={{ display: 'flex', flexDirection: 'column', gap: 16, paddingBottom: 8 }}>
            {/* 任务名称 */}
            <div>
              <label style={fieldLabel}>{t('cron.task_name')}</label>
              <input
                type="text"
                value={formName}
                onChange={e => setFormName(e.target.value)}
                placeholder={t('cron.task_name_placeholder')}
                maxLength={32}
                style={inputBase}
              />
            </div>

            {/* 任务指令 */}
            <div>
              <label style={fieldLabel}>{t('cron.task_prompt_label')}</label>
              <textarea
                value={formPrompt}
                onChange={e => setFormPrompt(e.target.value)}
                placeholder={t('cron.task_prompt_placeholder')}
                maxLength={1000}
                style={{
                  ...inputBase,
                  height: 120,
                  padding: '8px 12px',
                  resize: 'none',
                  lineHeight: '1.5',
                }}
              />
            </div>

            {/* 执行时间 */}
            <div>
              <label style={fieldLabel}>{t('cron.exec_time')}</label>
              <ScheduleKindSelect value={formScheduleKind} onChange={setFormScheduleKind} triggerStyle={selStyle} />

              {/* Daily: time + weekdays */}
              {formScheduleKind === 'daily' && (
                <div style={{ marginTop: 8 }}>
                  <div style={{ position: 'relative', width: '100%' }}>
                    <input
                      type="time"
                      className="cron-daily-time-input"
                      value={formDailyTime}
                      onChange={e => setFormDailyTime(e.target.value)}
                      style={{
                        ...inputBase,
                        paddingRight: 36,
                      }}
                    />
                    <div
                      style={{
                        position: 'absolute',
                        right: 8,
                        top: 0,
                        bottom: 0,
                        width: 16,
                        display: 'flex',
                        flexDirection: 'column',
                        alignItems: 'center',
                        justifyContent: 'center',
                        gap: 0,
                      }}
                    >
                      <button
                        type="button"
                        onClick={e => {
                          e.preventDefault();
                          bumpDailyMinutes(1);
                        }}
                        aria-label={t('cron.daily_time_step_up')}
                        style={{
                          padding: 0,
                          margin: 0,
                          border: 'none',
                          background: 'transparent',
                          cursor: 'pointer',
                          height: 18,
                          width: 16,
                          display: 'flex',
                          alignItems: 'center',
                          justifyContent: 'center',
                          flexShrink: 0,
                          color: SCHEDULE_KIND_TEXT,
                        }}
                      >
                        <ChevronUp size={16} strokeWidth={2} aria-hidden />
                      </button>
                      <button
                        type="button"
                        onClick={e => {
                          e.preventDefault();
                          bumpDailyMinutes(-1);
                        }}
                        aria-label={t('cron.daily_time_step_down')}
                        style={{
                          padding: 0,
                          margin: 0,
                          border: 'none',
                          background: 'transparent',
                          cursor: 'pointer',
                          height: 18,
                          width: 16,
                          display: 'flex',
                          alignItems: 'center',
                          justifyContent: 'center',
                          flexShrink: 0,
                          color: SCHEDULE_KIND_TEXT,
                        }}
                      >
                        <ChevronDown size={16} strokeWidth={2} aria-hidden />
                      </button>
                    </div>
                  </div>
                  <WeekdayChips weekdays={formDailyWeekdays} onChange={setFormDailyWeekdays} />
                </div>
              )}

              {/* Interval: 每间隔 + stepped number + unit dropdown + weekdays */}
              {formScheduleKind === 'interval' && (
                <div style={{ marginTop: 8, display: 'flex', flexDirection: 'column', gap: 8 }}>
                  <div
                    style={{
                      display: 'flex',
                      alignItems: 'center',
                      gap: 8,
                      width: '100%',
                      flexWrap: 'nowrap',
                    }}
                  >
                    <span
                      style={{
                        fontSize: 14,
                        color: 'rgba(0,0,0,0.85)',
                        whiteSpace: 'nowrap',
                        flexShrink: 0,
                      }}
                    >
                      {t('cron.interval_row_label')}
                    </span>
                    <div style={{ flex: 1, minWidth: 0, position: 'relative' }}>
                      <input
                        type="number"
                        inputMode="numeric"
                        min={1}
                        className="cron-interval-number-input"
                        value={formIntervalValue}
                        onChange={e => {
                          const v = e.target.value;
                          if (v === '') {
                            setFormIntervalValue('');
                            return;
                          }
                          const n = parseInt(v, 10);
                          if (!Number.isFinite(n)) return;
                          setFormIntervalValue(String(n));
                        }}
                        style={{
                          ...inputBase,
                          width: '100%',
                          paddingLeft: 12,
                          paddingRight: 36,
                          textAlign: 'left',
                        }}
                      />
                      <div
                        style={{
                          position: 'absolute',
                          right: 8,
                          top: 0,
                          bottom: 0,
                          width: 16,
                          display: 'flex',
                          flexDirection: 'column',
                          alignItems: 'center',
                          justifyContent: 'center',
                          gap: 0,
                        }}
                      >
                        <button
                          type="button"
                          onClick={e => {
                            e.preventDefault();
                            bumpIntervalValue(1);
                          }}
                          aria-label={t('cron.interval_step_up')}
                          style={{
                            padding: 0,
                            margin: 0,
                            border: 'none',
                            background: 'transparent',
                            cursor: 'pointer',
                            height: 18,
                            width: 16,
                            display: 'flex',
                            alignItems: 'center',
                            justifyContent: 'center',
                            flexShrink: 0,
                            color: SCHEDULE_KIND_TEXT,
                          }}
                        >
                          <ChevronUp size={16} strokeWidth={2} aria-hidden />
                        </button>
                        <button
                          type="button"
                          onClick={e => {
                            e.preventDefault();
                            bumpIntervalValue(-1);
                          }}
                          aria-label={t('cron.interval_step_down')}
                          style={{
                            padding: 0,
                            margin: 0,
                            border: 'none',
                            background: 'transparent',
                            cursor: 'pointer',
                            height: 18,
                            width: 16,
                            display: 'flex',
                            alignItems: 'center',
                            justifyContent: 'center',
                            flexShrink: 0,
                            color: SCHEDULE_KIND_TEXT,
                          }}
                        >
                          <ChevronDown size={16} strokeWidth={2} aria-hidden />
                        </button>
                      </div>
                    </div>
                    <IntervalUnitSelect
                      value={formIntervalUnit}
                      onChange={setFormIntervalUnit}
                      triggerStyle={selStyle}
                    />
                  </div>
                  <WeekdayChips
                    weekdays={formIntervalWeekdays}
                    onChange={setFormIntervalWeekdays}
                  />
                </div>
              )}

              {/* Once: time + date row (defaults: current local time / date) */}
              {formScheduleKind === 'once' && (
                <div
                  style={{
                    marginTop: 8,
                    display: 'flex',
                    gap: 8,
                    alignItems: 'stretch',
                    width: '100%',
                  }}
                >
                  <div style={{ flex: 1, minWidth: 0, position: 'relative' }}>
                    <input
                      type="time"
                      className="cron-daily-time-input cron-once-time-input"
                      value={formOnceTime}
                      onChange={e => setFormOnceTime(e.target.value)}
                      style={{
                        ...inputBase,
                        paddingRight: 36,
                        width: '100%',
                      }}
                    />
                    <div
                      style={{
                        position: 'absolute',
                        right: 8,
                        top: 0,
                        bottom: 0,
                        width: 16,
                        display: 'flex',
                        flexDirection: 'column',
                        alignItems: 'center',
                        justifyContent: 'center',
                        gap: 0,
                      }}
                    >
                      <button
                        type="button"
                        onClick={e => {
                          e.preventDefault();
                          bumpOnceMinutes(1);
                        }}
                        aria-label={t('cron.daily_time_step_up')}
                        style={{
                          padding: 0,
                          margin: 0,
                          border: 'none',
                          background: 'transparent',
                          cursor: 'pointer',
                          height: 18,
                          width: 16,
                          display: 'flex',
                          alignItems: 'center',
                          justifyContent: 'center',
                          flexShrink: 0,
                          color: SCHEDULE_KIND_TEXT,
                        }}
                      >
                        <ChevronUp size={16} strokeWidth={2} aria-hidden />
                      </button>
                      <button
                        type="button"
                        onClick={e => {
                          e.preventDefault();
                          bumpOnceMinutes(-1);
                        }}
                        aria-label={t('cron.daily_time_step_down')}
                        style={{
                          padding: 0,
                          margin: 0,
                          border: 'none',
                          background: 'transparent',
                          cursor: 'pointer',
                          height: 18,
                          width: 16,
                          display: 'flex',
                          alignItems: 'center',
                          justifyContent: 'center',
                          flexShrink: 0,
                          color: SCHEDULE_KIND_TEXT,
                        }}
                      >
                        <ChevronDown size={16} strokeWidth={2} aria-hidden />
                      </button>
                    </div>
                  </div>
                  <div style={{ flex: 1, minWidth: 0, position: 'relative' }}>
                    <div
                      role="group"
                      aria-label={t('cron.once_run_date')}
                      style={{
                        ...inputBase,
                        paddingRight: 36,
                        width: '100%',
                        display: 'flex',
                        alignItems: 'center',
                      }}
                    >
                      <span
                        style={{
                          flex: 1,
                          overflow: 'hidden',
                          textOverflow: 'ellipsis',
                          whiteSpace: 'nowrap',
                        }}
                      >
                        {formatYmdSlashes(formOnceDate)}
                      </span>
                    </div>
                    <div
                      style={{
                        position: 'absolute',
                        right: 8,
                        top: 0,
                        bottom: 0,
                        width: 16,
                        display: 'flex',
                        flexDirection: 'column',
                        alignItems: 'center',
                        justifyContent: 'center',
                        gap: 0,
                      }}
                    >
                      <button
                        type="button"
                        onClick={e => {
                          e.preventDefault();
                          bumpOnceDateByDays(1);
                        }}
                        aria-label={t('cron.once_date_step_up')}
                        style={{
                          padding: 0,
                          margin: 0,
                          border: 'none',
                          background: 'transparent',
                          cursor: 'pointer',
                          height: 18,
                          width: 16,
                          display: 'flex',
                          alignItems: 'center',
                          justifyContent: 'center',
                          flexShrink: 0,
                          color: SCHEDULE_KIND_TEXT,
                        }}
                      >
                        <ChevronUp size={16} strokeWidth={2} aria-hidden />
                      </button>
                      <button
                        type="button"
                        onClick={e => {
                          e.preventDefault();
                          bumpOnceDateByDays(-1);
                        }}
                        aria-label={t('cron.once_date_step_down')}
                        style={{
                          padding: 0,
                          margin: 0,
                          border: 'none',
                          background: 'transparent',
                          cursor: 'pointer',
                          height: 18,
                          width: 16,
                          display: 'flex',
                          alignItems: 'center',
                          justifyContent: 'center',
                          flexShrink: 0,
                          color: SCHEDULE_KIND_TEXT,
                        }}
                      >
                        <ChevronDown size={16} strokeWidth={2} aria-hidden />
                      </button>
                    </div>
                  </div>
                </div>
              )}
            </div>

            {/* 远控通道 */}
            <div>
              <label style={fieldLabel}>{t('cron.remote_channel')}</label>
              <CronModalListSelect
                value={formChannel}
                onChange={setFormChannel}
                triggerStyle={selStyle}
                options={[
                  { value: 'main', label: t('cron.channel_main') },
                  ...availableChannels.map(ch => ({ value: ch, label: ch })),
                ]}
              />
            </div>

            {/* 执行模型 */}
            <div>
              <label style={fieldLabel}>{t('cron.exec_model')}</label>
              <CronModalListSelect
                value={formModel}
                onChange={setFormModel}
                triggerStyle={selStyle}
                options={[
                  { value: '', label: t('cron.model_default') },
                  ...availableModels.map(m => ({ value: m, label: m })),
                ]}
              />
            </div>
          </div>
        </div>

        {/* Footer */}
        <div
          style={{
            display: 'flex',
            justifyContent: 'flex-end',
            alignItems: 'center',
            gap: 8,
            padding: '16px 24px 24px',
          }}
        >
          <button
            type="button"
            onClick={onClose}
            style={{
              boxSizing: 'border-box',
              height: 36,
              padding: '0 16px',
              borderRadius: 6,
              border: 'none',
              background: 'rgba(0, 0, 0, 0.1)',
              color: 'rgba(0, 0, 0, 0.85)',
              fontSize: 14,
              fontWeight: 400,
              cursor: 'pointer',
              display: 'inline-flex',
              alignItems: 'center',
              justifyContent: 'center',
            }}
          >
            {t('cron.cancel')}
          </button>
          <button
            type="button"
            onClick={handleSubmit}
            disabled={submitting || !canSubmit}
            style={{
              boxSizing: 'border-box',
              height: 36,
              padding: '0 16px',
              borderRadius: 6,
              border: 'none',
              background: submitting || !canSubmit
                ? 'rgba(55, 144, 250, 0.45)'
                : '#3790FA',
              color: submitting || !canSubmit
                ? 'rgba(255, 255, 255, 0.75)'
                : '#FFFFFF',
              fontSize: 14,
              fontWeight: 400,
              cursor: submitting || !canSubmit ? 'not-allowed' : 'pointer',
              display: 'inline-flex',
              alignItems: 'center',
              justifyContent: 'center',
            }}
          >
            {submitting
              ? t(isEdit ? 'cron.saving' : 'cron.adding')
              : t(isEdit ? 'cron.confirm' : 'cron.modal_add')}
          </button>
        </div>
      </div>
    </div>
  );
}

// ---------- Confirm Delete Dialog ----------
function ConfirmDeleteModal({ onConfirm, onCancel }: { onConfirm: () => void; onCancel: () => void }) {
  return (
    <div style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.45)', display: 'flex', alignItems: 'center', justifyContent: 'center', zIndex: 300 }}>
      <div style={{ background: '#fff', borderRadius: 10, padding: 28, width: 320, boxShadow: '0 8px 32px rgba(0,0,0,0.18)' }}>
        <p style={{ margin: '0 0 20px', fontSize: 14, color: '#333' }}>{t('cron.confirm_delete')}</p>
        <div style={{ display: 'flex', justifyContent: 'flex-end', gap: 10 }}>
          <button onClick={onCancel} style={{ padding: '7px 16px', borderRadius: 6, border: '1px solid #d9d9d9', background: '#fff', fontSize: 13, color: '#555', cursor: 'pointer' }}>{t('cron.no')}</button>
          <button onClick={onConfirm} style={{ padding: '7px 16px', borderRadius: 6, border: 'none', background: '#ff4d4f', color: '#fff', fontSize: 13, cursor: 'pointer' }}>{t('cron.yes')}</button>
        </div>
      </div>
    </div>
  );
}

// ---------- Main Page ----------
export default function Cron() {
  const [jobs, setJobs] = useState<CronJob[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
  const [modalJob, setModalJob] = useState<CronJob | 'add' | null>(null);
  const [confirmDeleteId, setConfirmDeleteId] = useState<string | null>(null);
  const [togglingId, setTogglingId] = useState<string | null>(null);
  const [runningId, setRunningId] = useState<string | null>(null);

  const fetchJobs = () => {
    setLoading(true);
    getCronJobs()
      .then(setJobs)
      .catch(err => setError(err.message))
      .finally(() => setLoading(false));
  };

  useEffect(() => { fetchJobs(); }, []);

  const handleToggle = async (job: CronJob) => {
    setTogglingId(job.id);
    try {
      const updated = job.enabled ? await pauseCronJob(job.id) : await resumeCronJob(job.id);
      setJobs(prev => prev.map(j => j.id === updated.id ? updated : j));
    } catch (err) {
      setError(err instanceof Error ? err.message : t('cron.toggle_error'));
    } finally {
      setTogglingId(null);
    }
  };

  const handleDelete = async (id: string) => {
    try {
      await deleteCronJob(id);
      setJobs(prev => prev.filter(j => j.id !== id));
    } catch (err) {
      setError(err instanceof Error ? err.message : t('cron.delete_error'));
    } finally {
      setConfirmDeleteId(null);
    }
  };

  const handleRunNow = async (job: CronJob) => {
    setRunningId(job.id);
    try {
      await runCronJobNow(job.id);
      fetchJobs();
    } catch (err) {
      setError(err instanceof Error ? err.message : t('cron.run_error'));
    } finally {
      setRunningId(null);
    }
  };

  const handleSaved = (updated: CronJob) => {
    setJobs(prev => {
      const idx = prev.findIndex(j => j.id === updated.id);
      if (idx >= 0) {
        const next = [...prev];
        next[idx] = updated;
        return next;
      }
      return [...prev, updated];
    });
    setModalJob(null);
  };

  // Shared page wrapper style
  const page: React.CSSProperties = {
    padding: '16px 20px',
    fontFamily: 'var(--pc-font-ui)',
    color: '#333',
    height: '100%',
    boxSizing: 'border-box',
    display: 'flex',
    flexDirection: 'column',
    gap: 8,
  };

  const thStyle: React.CSSProperties = {
    padding: '0 14px',
    height: 36,
    boxSizing: 'border-box',
    textAlign: 'left',
    fontSize: 13,
    fontWeight: 500,
    color: 'rgba(0, 0, 0, 0.6)',
    whiteSpace: 'nowrap',
    background: '#f5f5f5',
    borderBottom: '2px solid #fff',
  };

  const tdStyle: React.CSSProperties = {
    padding: '0 14px',
    height: 60,
    boxSizing: 'border-box',
    fontSize: 13,
    color: '#333',
    verticalAlign: 'middle',
    background: '#f5f5f5',
    borderBottom: '2px solid #fff',
  };

  if (error) {
    return (
      <div style={page}>
        <div style={{ background: '#fff2f0', border: '1px solid #ffa39e', borderRadius: 8, padding: '12px 16px', color: '#cf1322', fontSize: 13 }}>
          {t('cron.load_error')}: {error}
        </div>
      </div>
    );
  }

  if (loading) {
    return (
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', height: 200 }}>
        <div style={{ width: 28, height: 28, border: '2px solid #e0e0e0', borderTopColor: '#1890ff', borderRadius: '50%', animation: 'spin 0.8s linear infinite' }} />
        <style>{`@keyframes spin { to { transform: rotate(360deg); } }`}</style>
      </div>
    );
  }

  return (
    <div style={page}>
      {/* Header */}
      <div style={{ padding: '0 14px' }}>
        <span style={{ fontSize: 14, color: 'rgba(0, 0, 0, 0.85)', fontWeight: 500 }}>
          {t('cron.task_list')}（{jobs.length}）
        </span>
      </div>

      {/* Content */}
      {jobs.length === 0 ? (
        /* Empty state */
        <div
          style={{
            border: '1px solid #f0f0f0', borderRadius: 8,
            background: '#fafafa',
          }}
        >
          <button
            onClick={() => setModalJob('add')}
            style={{
              display: 'flex', alignItems: 'center', justifyContent: 'center',
              gap: 6, width: '100%', padding: '16px',
              background: 'none', border: 'none', cursor: 'pointer',
              fontSize: 14, color: '#666',
            }}
          >
            <Plus size={16} />{t('cron.add_job')}
          </button>
        </div>
      ) : (
        /* Table */
        <div style={{ borderRadius: 8, background: '#fff', overflow: 'hidden', flex: 1 }}>
          <table style={{ width: '100%', borderCollapse: 'separate', borderSpacing: 0 }}>
            <thead>
              <tr>
                <th style={thStyle}>{t('cron.name')}</th>
                <th style={{ ...thStyle, width: '38%' }}>{t('cron.prompt_col')}</th>
                <th style={{ ...thStyle, whiteSpace: 'nowrap' }}>{t('cron.next_run')}</th>
                <th style={{ ...thStyle, whiteSpace: 'nowrap' }}>{t('cron.last_status')}</th>
                <th style={{ ...thStyle, whiteSpace: 'nowrap' }}>{t('cron.actions')}</th>
              </tr>
            </thead>
            <tbody>
              {jobs.map(job => (
                <tr key={job.id}>
                  {/* Name */}
                  <td style={tdStyle}>
                    <span style={{ display: 'inline-flex', alignItems: 'center', gap: 6, color: 'rgba(0, 0, 0, 0.85)', fontWeight: 400 }}>
                      <AlarmClock size={14} style={{ color: '#262626', flexShrink: 0 }} />
                      {job.name ?? job.id.slice(0, 8)}
                    </span>
                  </td>
                  {/* Prompt / command */}
                  <td style={{ ...tdStyle, color: 'rgba(0, 0, 0, 0.6)', maxWidth: 0 }}>
                    <span style={{ display: 'block', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
                      {job.prompt ?? job.command}
                    </span>
                  </td>
                  {/* Next run */}
                  <td style={{ ...tdStyle, color: 'rgba(0, 0, 0, 0.6)', whiteSpace: 'nowrap' }}>
                    {formatDate(job.next_run)}
                  </td>
                  {/* Last status */}
                  <td style={tdStyle}>
                    <StatusBadge status={job.last_status} />
                  </td>
                  {/* Actions */}
                  <td style={tdStyle}>
                    <div style={{ display: 'inline-flex', alignItems: 'center', gap: 8 }}>
                      <RowMenu
                        onEdit={() => setModalJob(job)}
                        onDelete={() => setConfirmDeleteId(job.id)}
                        onRunNow={() => handleRunNow(job)}
                      />
                      <Toggle
                        checked={job.enabled}
                        onChange={() => handleToggle(job)}
                        disabled={togglingId === job.id || runningId === job.id}
                      />
                    </div>
                  </td>
                </tr>
              ))}
              {/* Add task row */}
              <tr>
                <td
                  colSpan={5}
                  onClick={() => setModalJob('add')}
                  style={{
                    ...tdStyle,
                    textAlign: 'center',
                    borderBottom: 'none',
                    borderRadius: '0 0 6px 6px',
                    cursor: 'pointer',
                  }}
                >
                  <span style={{
                    display: 'inline-flex',
                    alignItems: 'center',
                    gap: 6,
                    fontSize: 14,
                    color: 'rgba(0, 0, 0, 0.85)',
                  }}>
                    <Plus size={14} />{t('cron.add_job')}
                  </span>
                </td>
              </tr>
            </tbody>
          </table>
        </div>
      )}

      {/* Add / Edit Modal */}
      {modalJob !== null && (
        <CronModal
          job={modalJob}
          onClose={() => setModalJob(null)}
          onSaved={handleSaved}
        />
      )}

      {/* Confirm Delete */}
      {confirmDeleteId !== null && (
        <ConfirmDeleteModal
          onConfirm={() => handleDelete(confirmDeleteId)}
          onCancel={() => setConfirmDeleteId(null)}
        />
      )}
    </div>
  );
}
