import { CopyOutlined } from '@ant-design/icons';
import {
  Alert,
  Button,
  Card,
  Checkbox,
  Empty,
  Flex,
  Input,
  InputNumber,
  List,
  Segmented,
  Select,
  Space,
  Tabs,
  Tag,
  Typography,
  message,
} from 'antd';
import { memo, useEffect, useMemo, useState, type Dispatch, type SetStateAction } from 'react';
import {
  buildAutomationTypeOptions,
  useAutomationTypeRegistry,
} from '../../app/main/automationTypeAccess';
import { useTokenAccess } from '../../app/main/tokenAccess';
import './planBoard.css';
import './planSchedule.css';
import { maskNotePreviewByWord } from './noteMasking';
import { PlanListDetailLayout } from './PlanListDetailLayout';
import {
  createPlanScheduledTask,
  deletePlanScheduledTask,
  fetchPlanScheduledTasks,
  setupPlanBoard,
  updatePlanScheduledTask,
  type PlanScheduleMode,
  type PlanScheduleRepeatUnit,
  type PlanScheduledTask,
  type PlanScheduledTaskDraft,
} from './api';

const { Paragraph, Text, Title } = Typography;
const { TextArea } = Input;
const FUNCTION_CHECK_OPTIONS = ['완료', '오동작'];
const SCHEDULE_MODE_TAB_ITEMS: { key: PlanScheduleMode; label: string }[] = [
  { key: 'interval', label: '반복 주기' },
  { key: 'daily', label: '매일 시간' },
];
const REPEAT_UNIT_OPTIONS: { label: string; value: PlanScheduleRepeatUnit }[] = [
  { label: '분', value: 'minute' },
  { label: '시간', value: 'hour' },
  { label: '일', value: 'day' },
  { label: '주', value: 'week' },
  { label: '월', value: 'month' },
];
const REPEAT_UNIT_LABELS: Record<PlanScheduleRepeatUnit, string> = {
  minute: '분',
  hour: '시간',
  day: '일',
  week: '주',
  month: '개월',
};
const REPEAT_PRESET_OPTIONS: { label: string; value: number; unit: PlanScheduleRepeatUnit }[] = [
  { label: '10분', value: 10, unit: 'minute' },
  { label: '30분', value: 30, unit: 'minute' },
  { label: '1시간', value: 1, unit: 'hour' },
  { label: '6시간', value: 6, unit: 'hour' },
  { label: '매일 1회', value: 1, unit: 'day' },
  { label: '매주 1회', value: 1, unit: 'week' },
  { label: '매월 1회', value: 1, unit: 'month' },
];
const HOUR_OPTIONS = Array.from({ length: 24 }, (_, hour) => ({
  label: `${String(hour).padStart(2, '0')}시`,
  value: String(hour).padStart(2, '0'),
}));
const MINUTE_OPTIONS = Array.from({ length: 60 }, (_, minute) => ({
  label: `${String(minute).padStart(2, '0')}분`,
  value: String(minute).padStart(2, '0'),
}));
const DEFAULT_DAILY_RUN_TIME = '09:00';
const KST_TIME_ZONE = 'Asia/Seoul';
const DAY_MS = 24 * 60 * 60 * 1000;
function getRepeatIntervalMinutes(value: number, unit: PlanScheduleRepeatUnit) {
  const normalizedValue = Math.max(1, Math.round(Number(value) || 1));

  if (unit === 'day') {
    return normalizedValue * 24 * 60;
  }

  if (unit === 'week') {
    return normalizedValue * 7 * 24 * 60;
  }

  if (unit === 'month') {
    return normalizedValue * 30 * 24 * 60;
  }

  if (unit === 'hour') {
    return normalizedValue * 60;
  }

  return normalizedValue;
}

function getRepeatIntervalValueMax(unit: PlanScheduleRepeatUnit) {
  if (unit === 'month') {
    return 12;
  }

  if (unit === 'week') {
    return 52;
  }

  if (unit === 'day') {
    return 365;
  }

  if (unit === 'hour') {
    return 8760;
  }

  return 525600;
}

function normalizeRepeatIntervalValue(value: number, unit: PlanScheduleRepeatUnit) {
  const roundedValue = Math.max(1, Math.round(Number(value) || 1));
  return Math.min(getRepeatIntervalValueMax(unit), roundedValue);
}

function formatRepeatInterval(value: number, unit: PlanScheduleRepeatUnit, fallbackMinutes: number) {
  if (!value || !unit) {
    return `${fallbackMinutes}분마다`;
  }

  return `${value}${REPEAT_UNIT_LABELS[unit]}마다`;
}

function normalizeScheduleMode(value: PlanScheduleMode | null | undefined): PlanScheduleMode {
  return value === 'daily' ? 'daily' : 'interval';
}

function normalizeDailyRunTime(value: string | null | undefined) {
  return typeof value === 'string' && /^([01]\d|2[0-3]):[0-5]\d$/.test(value) ? value : DEFAULT_DAILY_RUN_TIME;
}

function updateDailyRunTime(value: string, part: 'hour' | 'minute', nextPartValue: string) {
  const [hour, minute] = normalizeDailyRunTime(value).split(':');
  return part === 'hour' ? `${nextPartValue}:${minute}` : `${hour}:${nextPartValue}`;
}

function formatScheduleCycle(item: PlanScheduledTask) {
  const scheduleMode = normalizeScheduleMode(item.scheduleMode);

  if (scheduleMode === 'daily') {
    return `매일 ${normalizeDailyRunTime(item.dailyRunTime)} 실행`;
  }

  return formatRepeatInterval(item.repeatIntervalValue, item.repeatIntervalUnit, item.repeatIntervalMinutes);
}

function getValidDate(value: string | null | undefined) {
  if (!value) {
    return null;
  }

  const date = new Date(value);
  return Number.isNaN(date.getTime()) ? null : date;
}

function getKstDateTimeParts(value: Date) {
  const parts = new Intl.DateTimeFormat('en-US', {
    timeZone: KST_TIME_ZONE,
    year: 'numeric',
    month: '2-digit',
    day: '2-digit',
    hour: '2-digit',
    minute: '2-digit',
    hour12: false,
    hourCycle: 'h23',
  }).formatToParts(value);
  const partMap = Object.fromEntries(parts.map((part) => [part.type, part.value]));

  return {
    year: Number(partMap.year),
    month: Number(partMap.month),
    day: Number(partMap.day),
    hour: Number(partMap.hour),
    minute: Number(partMap.minute),
  };
}

function getKstDateKey(value: Date) {
  const parts = getKstDateTimeParts(value);
  return `${parts.year}-${String(parts.month).padStart(2, '0')}-${String(parts.day).padStart(2, '0')}`;
}

function createDateFromKstParts(year: number, month: number, day: number, hour: number, minute: number) {
  return new Date(Date.UTC(year, month - 1, day, hour - 9, minute, 0, 0));
}

function resolveNextPlanScheduleRunAt(item: PlanScheduledTask, now = new Date()) {
  if (!item.enabled) {
    return null;
  }

  const lastRegisteredAt = getValidDate(item.lastRegisteredAt);

  if (!lastRegisteredAt && item.immediateRunEnabled) {
    return now;
  }

  if (normalizeScheduleMode(item.scheduleMode) === 'daily') {
    const [hour, minute] = normalizeDailyRunTime(item.dailyRunTime).split(':').map((value) => Number(value));
    const nowParts = getKstDateTimeParts(now);
    const scheduledToday = createDateFromKstParts(nowParts.year, nowParts.month, nowParts.day, hour, minute);
    const lastRegisteredToday = lastRegisteredAt ? getKstDateKey(lastRegisteredAt) === getKstDateKey(now) : false;

    if (!lastRegisteredToday && scheduledToday.getTime() <= now.getTime()) {
      return now;
    }

    if (!lastRegisteredToday) {
      return scheduledToday;
    }

    return new Date(scheduledToday.getTime() + DAY_MS);
  }

  const baseAt = lastRegisteredAt ?? getValidDate(item.createdAt) ?? now;
  const nextRunAt = new Date(baseAt.getTime() + item.repeatIntervalMinutes * 60 * 1000);

  return nextRunAt.getTime() <= now.getTime() ? now : nextRunAt;
}

function createEmptyScheduleDraft(defaultReleaseTarget = 'release'): PlanScheduledTaskDraft {
  return {
    id: null,
    workId: '',
    note: '',
    automationType: 'none',
    releaseTarget: defaultReleaseTarget,
    jangsingProcessingRequired: true,
    autoDeployToMain: true,
    enabled: true,
    immediateRunEnabled: true,
    scheduleMode: 'interval',
    repeatIntervalValue: 60,
    repeatIntervalUnit: 'minute',
    repeatIntervalMinutes: 60,
    dailyRunTime: DEFAULT_DAILY_RUN_TIME,
  };
}

function toDraft(item: PlanScheduledTask): PlanScheduledTaskDraft {
  const repeatIntervalUnit = item.repeatIntervalUnit ?? 'minute';
  const repeatIntervalValue = normalizeRepeatIntervalValue(
    item.repeatIntervalValue ?? item.repeatIntervalMinutes,
    repeatIntervalUnit,
  );

  return {
    id: item.id,
    workId: item.workId,
    note: item.note,
    automationType: item.automationType,
    releaseTarget: item.releaseTarget,
    jangsingProcessingRequired: item.jangsingProcessingRequired,
    autoDeployToMain: item.autoDeployToMain,
    enabled: item.enabled,
    immediateRunEnabled: item.immediateRunEnabled,
    scheduleMode: normalizeScheduleMode(item.scheduleMode),
    repeatIntervalValue,
    repeatIntervalUnit,
    repeatIntervalMinutes: item.repeatIntervalMinutes ?? getRepeatIntervalMinutes(repeatIntervalValue, repeatIntervalUnit),
    dailyRunTime: normalizeDailyRunTime(item.dailyRunTime),
  };
}

function formatPlanScheduleDateTime(value: string | Date | null | undefined) {
  if (!value) {
    return '미등록';
  }

  return new Date(value).toLocaleString('ko-KR', {
    timeZone: KST_TIME_ZONE,
  });
}

function formatNextPlanScheduleRunAt(item: PlanScheduledTask) {
  if (!item.enabled) {
    return '중지';
  }

  return formatPlanScheduleDateTime(resolveNextPlanScheduleRunAt(item));
}

function validateScheduleDraft(draft: PlanScheduledTaskDraft, items: PlanScheduledTask[]) {
  const messages: string[] = [];
  const workId = draft.workId.trim();
  const note = draft.note.trim();

  if (!workId) {
    messages.push('작업 ID를 입력하세요.');
  } else if (items.some((item) => item.id !== draft.id && item.workId.trim() === workId)) {
    messages.push('같은 작업 ID의 스케줄이 이미 있습니다.');
  }

  if (!note) {
    messages.push('반복 등록할 작업 메모를 입력하세요.');
  }

  if (draft.scheduleMode === 'interval' && getRepeatIntervalMinutes(draft.repeatIntervalValue, draft.repeatIntervalUnit) < 10) {
    messages.push('반복 주기는 최소 10분 이상으로 설정하세요.');
  }

  if (!draft.enabled) {
    messages.push('비활성 스케줄은 자동 등록되지 않습니다.');
  }

  return messages;
}

export function PlanSchedulePage() {
  const { hasAccess } = useTokenAccess();
  const { automationTypes } = useAutomationTypeRegistry();
  const [messageApi, contextHolder] = message.useMessage();
  const [items, setItems] = useState<PlanScheduledTask[]>([]);
  const [draft, setDraft] = useState(() => createEmptyScheduleDraft());
  const [editorOpen, setEditorOpen] = useState(false);
  const [loading, setLoading] = useState(false);
  const [saving, setSaving] = useState(false);
  const [errorMessage, setErrorMessage] = useState<string | null>(null);
  const selectedItem = useMemo(
    () => items.find((item) => item.id === draft.id) ?? null,
    [draft.id, items],
  );
  const validationMessages = useMemo(() => validateScheduleDraft(draft, items), [draft, items]);

  async function loadItems() {
    setLoading(true);
    setErrorMessage(null);

    try {
      setItems(await fetchPlanScheduledTasks());
    } catch (error) {
      setErrorMessage(error instanceof Error ? error.message : '스케줄 목록을 불러오지 못했습니다.');
    } finally {
      setLoading(false);
    }
  }

  useEffect(() => {
    void loadItems();
  }, [hasAccess]);

  async function handleSetup() {
    if (!hasAccess) {
      messageApi.error('설정 > 토큰 관리에서 권한 토큰을 등록한 사용자만 사용할 수 있습니다.');
      return;
    }

    try {
      await setupPlanBoard();
      messageApi.success('작업 스케줄 테이블을 준비했습니다.');
      await loadItems();
    } catch (error) {
      messageApi.error(error instanceof Error ? error.message : '테이블 생성에 실패했습니다.');
    }
  }

  async function handleSave() {
    if (!hasAccess) {
      messageApi.error('설정 > 토큰 관리에서 권한 토큰을 등록한 사용자만 사용할 수 있습니다.');
      return;
    }

    const blockingMessages = validationMessages.filter((message) => message !== '비활성 스케줄은 자동 등록되지 않습니다.');

    if (blockingMessages.length) {
      messageApi.warning(blockingMessages[0]);
      return;
    }

    setSaving(true);

    try {
      const repeatIntervalValue = normalizeRepeatIntervalValue(draft.repeatIntervalValue, draft.repeatIntervalUnit);
      const draftToSave = {
        ...draft,
        repeatIntervalValue,
        repeatIntervalMinutes: getRepeatIntervalMinutes(repeatIntervalValue, draft.repeatIntervalUnit),
      };
      const savedItem = draft.id ? await updatePlanScheduledTask(draftToSave) : await createPlanScheduledTask(draftToSave);
      messageApi.success(draft.id ? '스케줄을 수정했습니다.' : '스케줄을 등록했습니다.');
      setDraft(toDraft(savedItem));
      await loadItems();
    } catch (error) {
      messageApi.error(error instanceof Error ? error.message : '스케줄 저장에 실패했습니다.');
    } finally {
      setSaving(false);
    }
  }

  async function handleDelete() {
    if (!draft.id) {
      return;
    }

    if (!hasAccess) {
      messageApi.error('설정 > 토큰 관리에서 권한 토큰을 등록한 사용자만 사용할 수 있습니다.');
      return;
    }

    if (!window.confirm('선택한 스케줄을 삭제할까요?')) {
      return;
    }

    setSaving(true);

    try {
      await deletePlanScheduledTask(draft.id);
      messageApi.success('스케줄을 삭제했습니다.');
      setDraft(createEmptyScheduleDraft(draft.releaseTarget));
      setEditorOpen(false);
      await loadItems();
    } catch (error) {
      messageApi.error(error instanceof Error ? error.message : '스케줄 삭제에 실패했습니다.');
    } finally {
      setSaving(false);
    }
  }

  async function handleCopyText(text: string) {
    try {
      await navigator.clipboard.writeText(text);
      messageApi.success('복사했습니다.');
    } catch {
      messageApi.error('복사에 실패했습니다.');
    }
  }

  function handleCreateNew() {
    setDraft(createEmptyScheduleDraft(draft.releaseTarget));
    setEditorOpen(true);
  }

  function handleSelectItem(item: PlanScheduledTask) {
    setDraft(toDraft(item));
    setEditorOpen(true);
  }

  function closeEditor() {
    setEditorOpen(false);
  }

  return (
    <div className="plan-schedule-page">
      {contextHolder}
      <Card className="plan-schedule-page__overview" bordered={false}>
        <Flex justify="space-between" align="center" gap={12} wrap>
          <div>
            <Title level={4}>자동화 Schedule</Title>
            <Paragraph className="plan-schedule-page__intro">
              반복 작업 내용을 스케줄로 등록하고 활성 상태와 반복 주기를 관리합니다.
            </Paragraph>
          </div>
          <Space wrap>
            <Button onClick={() => void loadItems()} loading={loading}>
              조회
            </Button>
            <Button onClick={handleCreateNew} disabled={!hasAccess}>
              새 스케줄
            </Button>
            <Button onClick={() => void handleSetup()} disabled={!hasAccess}>
              테이블 생성
            </Button>
          </Space>
        </Flex>
      </Card>

      {!hasAccess ? (
        <Alert
          showIcon
          type="warning"
          className="plan-schedule-page__alert"
          message="권한 토큰이 없어 작업 스케줄은 조회만 사용할 수 있습니다."
          description="설정 > 토큰 관리에서 권한 토큰을 등록하기 전에는 조회 외 버튼과 입력을 사용할 수 없고, 작업 메모는 단어별로 마스킹됩니다."
        />
      ) : null}

      {errorMessage ? (
        <Alert
          showIcon
          type="error"
          className="plan-schedule-page__alert"
          message="스케줄을 사용할 수 없습니다."
          description={errorMessage}
        />
      ) : null}

      <PlanListDetailLayout
        classNamePrefix="plan-schedule-page"
        listTitle="스케줄 목록"
        listExtra={<Text code>{items.length} items</Text>}
        listContent={
          <PlanScheduleList
            activeDraftId={draft.id}
            editorOpen={editorOpen}
            hasAccess={hasAccess}
            items={items}
            loading={loading}
            onSelectItem={handleSelectItem}
          />
        }
        desktopDetailOpen={editorOpen}
        mobileDetailOpen={editorOpen}
        detailTitle={draft.id ? `스케줄 #${draft.id}` : '새 스케줄'}
        detailActions={
          <>
            {draft.id ? (
              <Button danger onClick={() => void handleDelete()} loading={saving} disabled={!hasAccess}>
                삭제
              </Button>
            ) : null}
            <Button type="primary" onClick={() => void handleSave()} loading={saving} disabled={!hasAccess}>
              저장
            </Button>
          </>
        }
        onCloseDetail={closeEditor}
        showDesktopClose
        emptyDetailTitle="스케줄 상세"
        detailContent={
          <PlanScheduleDetail
            automationTypeOptions={buildAutomationTypeOptions(automationTypes, draft.automationType)}
            draft={draft}
            hasAccess={hasAccess}
            selectedItem={selectedItem}
            validationMessages={validationMessages}
            onChangeDraft={setDraft}
            onCopyText={handleCopyText}
          />
        }
      />
    </div>
  );
}

const PlanScheduleList = memo(function PlanScheduleList({
  activeDraftId,
  editorOpen,
  hasAccess,
  items,
  loading,
  onSelectItem,
}: {
  activeDraftId: number | null;
  editorOpen: boolean;
  hasAccess: boolean;
  items: PlanScheduledTask[];
  loading: boolean;
  onSelectItem: (item: PlanScheduledTask) => void;
}) {
  return (
    <List
      className="plan-schedule-page__list"
      loading={loading}
      dataSource={items}
      locale={{ emptyText: <Empty description="등록된 스케줄이 없습니다." /> }}
      renderItem={(item) => (
        <List.Item
          key={item.id}
          className={`plan-schedule-page__list-item${
            editorOpen && activeDraftId === item.id ? ' plan-schedule-page__list-item--active' : ''
          }`}
          onClick={() => onSelectItem(item)}
        >
          <div className="plan-schedule-page__list-body">
            <Flex justify="space-between" align="start" gap={8}>
              <Text strong>{item.workId || `스케줄 #${item.id}`}</Text>
              <Tag color={item.enabled ? 'green' : 'default'}>{item.enabled ? '적용' : '중지'}</Tag>
            </Flex>
            <Paragraph ellipsis={{ rows: 2 }} className="plan-schedule-page__list-note">
              {item.note ? (hasAccess ? item.note : maskNotePreviewByWord(item.note)) : '작업 내용이 없습니다.'}
            </Paragraph>
            <Space wrap size={8}>
              <Tag>{formatScheduleCycle(item)}</Tag>
              <Tag color="blue">다음 실행 {formatNextPlanScheduleRunAt(item)}</Tag>
              <Tag>{item.immediateRunEnabled ? '즉시실행' : '주기 후 실행'}</Tag>
              <Tag>{item.autoDeployToMain ? 'main 자동등록' : 'release만'}</Tag>
              <Tag>기능동작확인 {item.jangsingProcessingRequired ? '완료' : '오동작'}</Tag>
            </Space>
            <Flex justify="space-between" align="center" gap={8} wrap style={{ marginTop: 10 }}>
              <Text type="secondary">최근 등록 {formatPlanScheduleDateTime(item.lastRegisteredAt)}</Text>
              <Text type="secondary">수정 {formatPlanScheduleDateTime(item.updatedAt)}</Text>
            </Flex>
          </div>
        </List.Item>
      )}
    />
  );
});

function PlanScheduleDetail({
  automationTypeOptions,
  draft,
  hasAccess,
  selectedItem,
  validationMessages,
  onChangeDraft,
  onCopyText,
}: {
  automationTypeOptions: Array<{ label: string; value: string }>;
  draft: PlanScheduledTaskDraft;
  hasAccess: boolean;
  selectedItem: PlanScheduledTask | null;
  validationMessages: string[];
  onChangeDraft: Dispatch<SetStateAction<PlanScheduledTaskDraft>>;
  onCopyText: (text: string) => Promise<void>;
}) {
  return (
    <Space direction="vertical" size={14} style={{ width: '100%' }}>
      {selectedItem ? (
        <Alert
          showIcon
          type="info"
          className="plan-schedule-page__alert"
          message="등록 정보"
          description={
            <Space direction="vertical" size={4}>
              <Text>다음 실행: {formatNextPlanScheduleRunAt(selectedItem)}</Text>
              <Text>최근 작업 등록: {formatPlanScheduleDateTime(selectedItem.lastRegisteredAt)}</Text>
              <Text>생성: {formatPlanScheduleDateTime(selectedItem.createdAt)}</Text>
              <Text>수정: {formatPlanScheduleDateTime(selectedItem.updatedAt)}</Text>
            </Space>
          }
        />
      ) : null}

      {validationMessages.length ? (
        <Alert
          showIcon
          type={validationMessages.some((message) => message !== '비활성 스케줄은 자동 등록되지 않습니다.') ? 'warning' : 'info'}
          className="plan-schedule-page__alert"
          message="스케줄 등록 검증"
          description={
            <Space direction="vertical" size={2}>
              {validationMessages.map((message) => (
                <Text key={message}>{message}</Text>
              ))}
            </Space>
          }
        />
      ) : null}

      <div className="plan-schedule-page__form">
        <div>
          <Text strong>작업 ID</Text>
          <Input
            value={draft.workId}
            placeholder="예: 반복-정리"
            disabled={!hasAccess}
            onChange={(event) => onChangeDraft((previous) => ({ ...previous, workId: event.target.value }))}
          />
        </div>
        <div>
          <Flex justify="space-between" align="center" gap={8} wrap>
            <Text strong>작업 메모</Text>
            <Space size={8}>
              <Button size="small" icon={<CopyOutlined />} onClick={() => void onCopyText(draft.note)}>
                복사
              </Button>
            </Space>
          </Flex>
          <div className="plan-schedule-page__notepad-frame">
            <TextArea
              value={hasAccess ? draft.note : maskNotePreviewByWord(draft.note)}
              rows={10}
              placeholder={hasAccess ? '반복 등록할 작업 내용을 입력하세요.' : '권한 토큰 등록 후 편집할 수 있습니다.'}
              className="plan-schedule-page__notepad"
              disabled={!hasAccess}
              onChange={(event) => onChangeDraft((previous) => ({ ...previous, note: event.target.value }))}
            />
          </div>
          {!hasAccess ? <Text type="secondary">조회 화면에서는 작업 메모를 단어별 마스킹으로 표시합니다.</Text> : null}
        </div>
        <div>
          <Text strong>release 브랜치</Text>
          <Input
            value={draft.releaseTarget}
            placeholder="release"
            disabled={!hasAccess}
            onChange={(event) =>
              onChangeDraft((previous) => ({ ...previous, releaseTarget: event.target.value || 'release' }))
            }
          />
        </div>
        <div>
          <Text strong>자동화 처리</Text>
          <Select
            className="plan-schedule-page__select plan-schedule-page__select--automation"
            value={draft.automationType}
            options={automationTypeOptions}
            popupClassName="plan-schedule-page__select-popup"
            getPopupContainer={(triggerNode) => triggerNode.parentElement ?? document.body}
            disabled={!hasAccess}
            onChange={(automationType) => onChangeDraft((previous) => ({ ...previous, automationType }))}
          />
        </div>
        <div>
          <Checkbox
            checked={draft.enabled}
            disabled={!hasAccess}
            onChange={(event) => onChangeDraft((previous) => ({ ...previous, enabled: event.target.checked }))}
          >
            적용
          </Checkbox>
        </div>
        <div>
          <Text strong>반복 주기</Text>
          <Tabs
            size="small"
            items={SCHEDULE_MODE_TAB_ITEMS.map((item) => ({
              ...item,
              disabled: !hasAccess,
            }))}
            activeKey={draft.scheduleMode}
            onChange={(key) => onChangeDraft((previous) => ({ ...previous, scheduleMode: key as PlanScheduleMode }))}
          />
          {draft.scheduleMode === 'daily' ? (
            <Space align="center" wrap>
              <Text>매일</Text>
              <Select
                style={{ width: 96 }}
                options={HOUR_OPTIONS}
                value={normalizeDailyRunTime(draft.dailyRunTime).split(':')[0]}
                popupClassName="plan-schedule-page__select-popup"
                getPopupContainer={(triggerNode) => triggerNode.parentElement ?? document.body}
                disabled={!hasAccess}
                onChange={(value) =>
                  onChangeDraft((previous) => ({
                    ...previous,
                    dailyRunTime: updateDailyRunTime(previous.dailyRunTime, 'hour', value),
                    repeatIntervalValue: 1,
                    repeatIntervalUnit: 'day',
                    repeatIntervalMinutes: getRepeatIntervalMinutes(1, 'day'),
                  }))
                }
              />
              <Select
                style={{ width: 96 }}
                options={MINUTE_OPTIONS}
                value={normalizeDailyRunTime(draft.dailyRunTime).split(':')[1]}
                popupClassName="plan-schedule-page__select-popup"
                getPopupContainer={(triggerNode) => triggerNode.parentElement ?? document.body}
                disabled={!hasAccess}
                onChange={(value) =>
                  onChangeDraft((previous) => ({
                    ...previous,
                    dailyRunTime: updateDailyRunTime(previous.dailyRunTime, 'minute', value),
                    repeatIntervalValue: 1,
                    repeatIntervalUnit: 'day',
                    repeatIntervalMinutes: getRepeatIntervalMinutes(1, 'day'),
                  }))
                }
              />
              <Text type="secondary">에 작업 메모로 등록</Text>
            </Space>
          ) : (
            <>
              <Space align="center" wrap>
                <InputNumber
                  min={1}
                  max={getRepeatIntervalValueMax(draft.repeatIntervalUnit)}
                  value={draft.repeatIntervalValue}
                  disabled={!hasAccess}
                  onChange={(value) => {
                    const repeatIntervalValue = normalizeRepeatIntervalValue(
                      Number(value) || 1,
                      draft.repeatIntervalUnit,
                    );
                    onChangeDraft((previous) => ({
                      ...previous,
                      repeatIntervalValue,
                      repeatIntervalMinutes: getRepeatIntervalMinutes(repeatIntervalValue, previous.repeatIntervalUnit),
                    }));
                  }}
                />
                <Select
                  style={{ width: 96 }}
                  options={REPEAT_UNIT_OPTIONS}
                  value={draft.repeatIntervalUnit}
                  popupClassName="plan-schedule-page__select-popup"
                  getPopupContainer={(triggerNode) => triggerNode.parentElement ?? document.body}
                  disabled={!hasAccess}
                  onChange={(value) =>
                    onChangeDraft((previous) => ({
                      ...previous,
                      repeatIntervalValue: normalizeRepeatIntervalValue(previous.repeatIntervalValue, value),
                      repeatIntervalUnit: value,
                      repeatIntervalMinutes: getRepeatIntervalMinutes(
                        normalizeRepeatIntervalValue(previous.repeatIntervalValue, value),
                        value,
                      ),
                    }))
                  }
                />
                <Text type="secondary">마다 작업 메모로 등록</Text>
              </Space>
              <Space size={6} wrap style={{ marginTop: 8 }}>
                {REPEAT_PRESET_OPTIONS.map((option) => (
                  <Button
                    key={`${option.value}-${option.unit}`}
                    size="small"
                    disabled={!hasAccess}
                    onClick={() =>
                      onChangeDraft((previous) => ({
                        ...previous,
                        scheduleMode: option.unit === 'day' && option.value === 1 ? 'daily' : 'interval',
                        repeatIntervalValue: option.value,
                        repeatIntervalUnit: option.unit,
                        repeatIntervalMinutes: getRepeatIntervalMinutes(option.value, option.unit),
                      }))
                    }
                  >
                    {option.label}
                  </Button>
                ))}
              </Space>
            </>
          )}
        </div>
        <div>
          <Checkbox
            checked={draft.immediateRunEnabled}
            disabled={!hasAccess}
            onChange={(event) =>
              onChangeDraft((previous) => ({ ...previous, immediateRunEnabled: event.target.checked }))
            }
          >
            즉시실행 여부
          </Checkbox>
        </div>
        <div>
          <Checkbox
            checked={draft.autoDeployToMain}
            disabled={!hasAccess}
            onChange={(event) => onChangeDraft((previous) => ({ ...previous, autoDeployToMain: event.target.checked }))}
          >
            메인까지 자동등록
          </Checkbox>
        </div>
        <div>
          <Text strong>기능동작확인</Text>
          <Segmented
            options={FUNCTION_CHECK_OPTIONS}
            value={draft.jangsingProcessingRequired ? '완료' : '오동작'}
            disabled={!hasAccess}
            onChange={(value) =>
              onChangeDraft((previous) => ({
                ...previous,
                jangsingProcessingRequired: value === '완료',
              }))
            }
          />
        </div>
      </div>
    </Space>
  );
}
