import { appendClientIdHeader } from '../../app/main/clientIdentity';
import { getRegisteredAccessToken, isAllowedRegistrationToken } from '../../app/main/tokenAccess';
import type {
  RestartReservationWorkloadSummary,
  ServerCommandActionResult,
  ServerCommandItem,
  ServerCommandKey,
  ServerRestartReservationAutoFix,
  ServerRestartReservationExecutionPhase,
  ServerRestartReservation,
  ServerRestartReservationStatus,
  ServerRestartReservationWorkItem,
  TestServerDeploymentPhase,
  TestServerDeploymentState,
  TestServerDeploymentStep,
  TestServerDeploymentStepKey,
  WorkServerDeploymentPhase,
  WorkServerDeploymentState,
  WorkServerDeploymentStep,
  WorkServerDeploymentStepKey,
} from './types';

export class ServerCommandApiError extends Error {
  status: number;
  workloadSummary: RestartReservationWorkloadSummary | null;

  constructor(message: string, status: number, workloadSummary: RestartReservationWorkloadSummary | null = null) {
    super(message);
    this.name = 'ServerCommandApiError';
    this.status = status;
    this.workloadSummary = workloadSummary;
  }
}

function resolveServerCommandApiBaseUrl() {
  if (import.meta.env.VITE_WORK_SERVER_URL) {
    return import.meta.env.VITE_WORK_SERVER_URL;
  }

  return '/api';
}

function resolveServerCommandFallbackBaseUrl() {
  if (typeof window === 'undefined') {
    return null;
  }

  const hostname = window.location.hostname;
  const isLocalWorkServerHost =
    hostname === 'localhost' || hostname === '127.0.0.1' || hostname === '0.0.0.0';

  if (!isLocalWorkServerHost) {
    return null;
  }

  const fallbackUrl = new URL(window.location.origin);
  fallbackUrl.port = '3100';
  fallbackUrl.pathname = '/api';
  fallbackUrl.search = '';
  fallbackUrl.hash = '';
  return fallbackUrl.toString().replace(/\/+$/, '');
}

function getCurrentAppOrigin() {
  if (typeof window === 'undefined') {
    return '';
  }

  return window.location.origin;
}

const SERVER_COMMAND_API_BASE_URL = resolveServerCommandApiBaseUrl();
const SERVER_COMMAND_API_FALLBACK_BASE_URL =
  !import.meta.env.VITE_WORK_SERVER_URL && SERVER_COMMAND_API_BASE_URL === '/api'
    ? resolveServerCommandFallbackBaseUrl()
    : null;

type ServerCommandRequestOptions = {
  shareToken?: string | null;
};

async function requestOnce<T>(baseUrl: string, path: string, init?: RequestInit, options?: ServerCommandRequestOptions) {
  const headers = appendClientIdHeader(init?.headers);
  const hasBody = init?.body !== undefined && init?.body !== null;
  const method = init?.method?.toUpperCase() ?? 'GET';
  const controller = new AbortController();
  const abortFromExternalSignal = () => controller.abort(init?.signal?.reason);
  const hasExternalSignal = Boolean(init?.signal);
  const extendedInit = init as (RequestInit & { timeoutMs?: number }) | undefined;
  const timeoutMs = typeof init?.keepalive === 'boolean'
    ? 8000
    : typeof extendedInit?.timeoutMs === 'number'
      ? Math.max(1000, extendedInit.timeoutMs ?? 8000)
      : 8000;
  const timeoutId = globalThis.setTimeout(() => controller.abort(), timeoutMs);

  if (init?.signal) {
    if (init.signal.aborted) {
      abortFromExternalSignal();
    } else {
      init.signal.addEventListener('abort', abortFromExternalSignal, { once: true });
    }
  }

  if (hasBody && !headers.has('Content-Type')) {
    headers.set('Content-Type', 'application/json');
  }

  const appOrigin = getCurrentAppOrigin();
  if (appOrigin && !headers.has('X-App-Origin')) {
    headers.set('X-App-Origin', appOrigin);
  }

  const token = getRegisteredAccessToken();
  const shareToken = options?.shareToken?.trim() ?? '';

  if (!shareToken && !isAllowedRegistrationToken(token)) {
    throw new ServerCommandApiError('권한 토큰 등록 또는 허용된 공유채팅 링크에서만 Work Server API를 호출할 수 있습니다.', 403);
  }

  if (token && !headers.has('X-Access-Token')) {
    headers.set('X-Access-Token', token);
  }

  if (shareToken && !headers.has('X-Chat-Share-Token')) {
    headers.set('X-Chat-Share-Token', shareToken);
  }

  let response: Response;

  try {
    response = await fetch(`${baseUrl}${path}`, {
      ...init,
      headers,
      signal: controller.signal,
      cache: init?.cache ?? (method === 'GET' ? 'no-store' : undefined),
    });
  } catch (error) {
    globalThis.clearTimeout(timeoutId);

    if (hasExternalSignal) {
      init?.signal?.removeEventListener('abort', abortFromExternalSignal);
    }

    if (init?.signal?.aborted) {
      throw error;
    }

    if (error instanceof DOMException && error.name === 'AbortError') {
      throw new ServerCommandApiError('서버 명령 응답이 지연됩니다.', 408);
    }

    throw error;
  }

  globalThis.clearTimeout(timeoutId);

  if (hasExternalSignal) {
    init?.signal?.removeEventListener('abort', abortFromExternalSignal);
  }

  if (!response.ok) {
    const text = await response.text();
    let payload: { message?: string; workloadSummary?: Partial<RestartReservationWorkloadSummary> } | null = null;

    try {
      payload = JSON.parse(text) as { message?: string; workloadSummary?: Partial<RestartReservationWorkloadSummary> };
    } catch {}

    if (payload) {
      const workloadSummary =
        payload.workloadSummary && typeof payload.workloadSummary === 'object'
          ? {
              codexRunningCount: Number(payload.workloadSummary.codexRunningCount ?? 0),
              codexQueuedCount: Number(payload.workloadSummary.codexQueuedCount ?? 0),
              automationRunningCount: Number(payload.workloadSummary.automationRunningCount ?? 0),
              automationQueuedCount: Number(payload.workloadSummary.automationQueuedCount ?? 0),
            }
          : null;
      throw new ServerCommandApiError(
        payload.message || '서버 명령 요청에 실패했습니다.',
        response.status,
        workloadSummary,
      );
    }

    throw new ServerCommandApiError(text || '서버 명령 요청에 실패했습니다.', response.status);
  }

  const responseText = await response.text();

  if (!responseText.trim()) {
    throw new ServerCommandApiError('서버 명령 응답이 비어 있습니다.', 502);
  }

  try {
    return JSON.parse(responseText) as T;
  } catch {
    throw new ServerCommandApiError('서버 명령 응답을 해석하지 못했습니다.', 502);
  }
}

async function request<T>(path: string, init?: RequestInit, options?: ServerCommandRequestOptions): Promise<T> {
  try {
    return await requestOnce<T>(SERVER_COMMAND_API_BASE_URL, path, init, options);
  } catch (error) {
    const shouldRetryWithFallback =
      SERVER_COMMAND_API_FALLBACK_BASE_URL &&
      SERVER_COMMAND_API_FALLBACK_BASE_URL !== SERVER_COMMAND_API_BASE_URL &&
      (error instanceof ServerCommandApiError
        ? error.status === 404 || error.status === 408 || error.status === 502
        : error instanceof Error && /404|Failed to fetch|NetworkError/i.test(error.message));

    if (!shouldRetryWithFallback) {
      throw error;
    }

    return requestOnce<T>(SERVER_COMMAND_API_FALLBACK_BASE_URL, path, init, options);
  }
}

function normalizeServerCommandItem(value: unknown): ServerCommandItem {
  if (!value || typeof value !== 'object') {
    throw new Error('서버 명령 항목 형식이 올바르지 않습니다.');
  }

  const item = value as Partial<Record<keyof ServerCommandItem, unknown>>;
  const key = typeof item.key === 'string' ? item.key : '';

  if (key !== 'test' && key !== 'rel' && key !== 'prod' && key !== 'work-server' && key !== 'command-runner') {
    throw new Error('지원하지 않는 서버 키입니다.');
  }

  const availability =
    item.availability === 'online' || item.availability === 'degraded' || item.availability === 'offline'
      ? item.availability
      : 'offline';

  return {
    key,
    label: typeof item.label === 'string' ? item.label : key.toUpperCase(),
    summary: typeof item.summary === 'string' ? item.summary : '',
    environment: typeof item.environment === 'string' ? item.environment : '-',
    publicUrl: typeof item.publicUrl === 'string' ? item.publicUrl : null,
    checkUrl: typeof item.checkUrl === 'string' ? item.checkUrl : '-',
    composeFile: typeof item.composeFile === 'string' ? item.composeFile : '-',
    serviceName: typeof item.serviceName === 'string' ? item.serviceName : '-',
    availability,
    httpStatus: typeof item.httpStatus === 'number' ? item.httpStatus : null,
    contentType: typeof item.contentType === 'string' ? item.contentType : null,
    responsePreview: typeof item.responsePreview === 'string' ? item.responsePreview : null,
    checkedAt: typeof item.checkedAt === 'string' ? item.checkedAt : new Date(0).toISOString(),
    startedAt: typeof item.startedAt === 'string' ? item.startedAt : null,
    runningVersion: typeof item.runningVersion === 'string' ? item.runningVersion : null,
    runningBuiltAt: typeof item.runningBuiltAt === 'string' ? item.runningBuiltAt : null,
    latestVersion: typeof item.latestVersion === 'string' ? item.latestVersion : null,
    latestBuiltAt: typeof item.latestBuiltAt === 'string' ? item.latestBuiltAt : null,
    latestSourceChangeAt: typeof item.latestSourceChangeAt === 'string' ? item.latestSourceChangeAt : null,
    latestSourceChangePath: typeof item.latestSourceChangePath === 'string' ? item.latestSourceChangePath : null,
    buildRequired: typeof item.buildRequired === 'boolean' ? item.buildRequired : false,
    updateAvailable: typeof item.updateAvailable === 'boolean' ? item.updateAvailable : false,
    updateSummary: typeof item.updateSummary === 'string' ? item.updateSummary : null,
    responseTimeMs: typeof item.responseTimeMs === 'number' ? item.responseTimeMs : null,
    composeStatus: typeof item.composeStatus === 'string' ? item.composeStatus : null,
    composeDetails: typeof item.composeDetails === 'string' ? item.composeDetails : null,
    lastCommand: typeof item.lastCommand === 'string' ? item.lastCommand : '-',
    commandScript: typeof item.commandScript === 'string' ? item.commandScript : '-',
    commandWorkingDirectory: typeof item.commandWorkingDirectory === 'string' ? item.commandWorkingDirectory : '-',
    errorMessage: typeof item.errorMessage === 'string' ? item.errorMessage : null,
    deployment: normalizeWorkServerDeploymentState(item.deployment),
  };
}

function extractServerCommandItems(response: unknown) {
  if (Array.isArray(response)) {
    const normalizedItems = response.flatMap((item) => {
      try {
        return [normalizeServerCommandItem(item)];
      } catch {
        return [];
      }
    });

    if (normalizedItems.length === 0) {
      throw new Error('서버 명령 목록을 읽지 못했습니다.');
    }

    return normalizedItems;
  }

  if (!response || typeof response !== 'object') {
    throw new Error('서버 명령 응답 형식이 올바르지 않습니다.');
  }

  const payload = response as {
    items?: unknown;
    data?: { items?: unknown; data?: { items?: unknown } | unknown[] } | unknown[];
    result?: { items?: unknown; data?: { items?: unknown } | unknown[] } | unknown[];
  };
  const payloadData =
    payload.data && typeof payload.data === 'object' && !Array.isArray(payload.data) ? payload.data : null;
  const payloadResult =
    payload.result && typeof payload.result === 'object' && !Array.isArray(payload.result) ? payload.result : null;
  const nestedPayloadData =
    payloadData?.data && typeof payloadData.data === 'object' && !Array.isArray(payloadData.data) ? payloadData.data : null;
  const nestedPayloadResult =
    payloadResult?.data && typeof payloadResult.data === 'object' && !Array.isArray(payloadResult.data) ? payloadResult.data : null;

  const items = Array.isArray(payload.items)
    ? payload.items
    : Array.isArray(payloadData?.items)
      ? payloadData.items
      : Array.isArray(payloadResult?.items)
        ? payloadResult.items
        : Array.isArray(nestedPayloadData?.items)
          ? nestedPayloadData.items
          : Array.isArray(nestedPayloadResult?.items)
            ? nestedPayloadResult.items
            : Array.isArray(payload.data)
              ? payload.data
              : Array.isArray(payload.result)
                ? payload.result
                : null;

  if (!items) {
    throw new Error('서버 명령 목록을 읽지 못했습니다.');
  }

  const normalizedItems = items.flatMap((item) => {
    try {
      return [normalizeServerCommandItem(item)];
    } catch {
      return [];
    }
  });

  if (normalizedItems.length === 0) {
    throw new Error('서버 명령 목록을 읽지 못했습니다.');
  }

  return normalizedItems;
}

function extractServerCommandActionResult(response: unknown): ServerCommandActionResult {
  if (!response || typeof response !== 'object') {
    throw new Error('서버 재기동 응답 형식이 올바르지 않습니다.');
  }

  const payload = response as {
    item?: unknown;
    server?: unknown;
    commandOutput?: unknown;
    output?: unknown;
    restartState?: unknown;
    testDeployment?: unknown;
    data?: {
      item?: unknown;
      server?: unknown;
      commandOutput?: unknown;
      output?: unknown;
      restartState?: unknown;
      testDeployment?: unknown;
    };
  };
  const nestedData = payload.data && typeof payload.data === 'object' && !Array.isArray(payload.data) ? payload.data : null;
  const item = payload.item ?? payload.server ?? nestedData?.item ?? nestedData?.server;

  if (!item) {
    throw new Error('재기동 결과를 읽지 못했습니다.');
  }

  const commandOutput = payload.commandOutput ?? payload.output ?? nestedData?.commandOutput ?? nestedData?.output;
  const restartState = payload.restartState ?? nestedData?.restartState;

  return {
    item: normalizeServerCommandItem(item),
    commandOutput: typeof commandOutput === 'string' ? commandOutput : null,
    restartState: restartState === 'accepted' ? 'accepted' : 'completed',
    deployment: normalizeWorkServerDeploymentState(payload.deployment ?? nestedData?.deployment),
    testDeployment: normalizeTestServerDeploymentState(payload.testDeployment ?? nestedData?.testDeployment),
  };
}

const WORK_SERVER_DEPLOYMENT_STEP_KEYS = [
  'build-target-slot',
  'verify-target-health',
  'switch-proxy',
  'drain-previous-slot',
  'rebuild-previous-slot',
  'recover-interrupted-chat',
] as const;

function normalizeWorkServerDeploymentStepKey(value: unknown): WorkServerDeploymentStepKey | null {
  return WORK_SERVER_DEPLOYMENT_STEP_KEYS.includes(value as WorkServerDeploymentStepKey)
    ? (value as WorkServerDeploymentStepKey)
    : null;
}

function normalizeWorkServerDeploymentPhase(value: unknown): WorkServerDeploymentPhase {
  return value === 'build-target-slot'
    || value === 'verify-target-health'
    || value === 'switch-proxy'
    || value === 'drain-previous-slot'
    || value === 'rebuild-previous-slot'
    || value === 'recover-interrupted-chat'
    || value === 'completed'
    || value === 'failed'
    ? value
    : 'idle';
}

function normalizeWorkServerDeploymentSteps(value: unknown): WorkServerDeploymentStep[] {
  if (!Array.isArray(value)) {
    return WORK_SERVER_DEPLOYMENT_STEP_KEYS.map((key) => ({
      key,
      status: 'pending',
      detail: null,
      updatedAt: null,
    }));
  }

  const normalizedByKey = new Map<WorkServerDeploymentStepKey, WorkServerDeploymentStep>();

  value.forEach((item) => {
    if (!item || typeof item !== 'object') {
      return;
    }

    const candidate = item as Record<string, unknown>;
    const key = normalizeWorkServerDeploymentStepKey(candidate.key);

    if (!key) {
      return;
    }

    normalizedByKey.set(key, {
      key,
      status:
        candidate.status === 'running'
        || candidate.status === 'completed'
        || candidate.status === 'failed'
        || candidate.status === 'pending'
          ? candidate.status
          : 'pending',
      detail: typeof candidate.detail === 'string' ? candidate.detail : null,
      updatedAt: typeof candidate.updatedAt === 'string' ? candidate.updatedAt : null,
    });
  });

  return WORK_SERVER_DEPLOYMENT_STEP_KEYS.map((key) => normalizedByKey.get(key) ?? {
    key,
    status: 'pending',
    detail: null,
    updatedAt: null,
  });
}

function normalizeWorkServerDeploymentState(value: unknown): WorkServerDeploymentState | null {
  if (!value || typeof value !== 'object') {
    return null;
  }

  const item = value as Record<string, unknown>;

  return {
    status: item.status === 'running' || item.status === 'completed' || item.status === 'failed' ? item.status : 'idle',
    phase: normalizeWorkServerDeploymentPhase(item.phase),
    summary: typeof item.summary === 'string' ? item.summary : null,
    startedAt: typeof item.startedAt === 'string' ? item.startedAt : null,
    updatedAt: typeof item.updatedAt === 'string' ? item.updatedAt : null,
    completedAt: typeof item.completedAt === 'string' ? item.completedAt : null,
    activeSlot: item.activeSlot === 'blue' || item.activeSlot === 'green' ? item.activeSlot : null,
    targetSlot: item.targetSlot === 'blue' || item.targetSlot === 'green' ? item.targetSlot : null,
    previousSlot: item.previousSlot === 'blue' || item.previousSlot === 'green' ? item.previousSlot : null,
    targetContainer: typeof item.targetContainer === 'string' ? item.targetContainer : null,
    previousContainer: typeof item.previousContainer === 'string' ? item.previousContainer : null,
    previousSlotActiveChatRequestCount: typeof item.previousSlotActiveChatRequestCount === 'number' ? item.previousSlotActiveChatRequestCount : null,
    previousSlotQueuedChatRequestCount: typeof item.previousSlotQueuedChatRequestCount === 'number' ? item.previousSlotQueuedChatRequestCount : null,
    recoveredSessionCount: typeof item.recoveredSessionCount === 'number' ? item.recoveredSessionCount : null,
    recoveredRestartedCount: typeof item.recoveredRestartedCount === 'number' ? item.recoveredRestartedCount : null,
    recoveredRequeuedCount: typeof item.recoveredRequeuedCount === 'number' ? item.recoveredRequeuedCount : null,
    lastError: typeof item.lastError === 'string' ? item.lastError : null,
    logExcerpt: typeof item.logExcerpt === 'string' ? item.logExcerpt : null,
    steps: normalizeWorkServerDeploymentSteps(item.steps),
  };
}

function extractWorkServerDeploymentState(response: unknown): WorkServerDeploymentState | null {
  if (!response || typeof response !== 'object') {
    return null;
  }

  const payload = response as {
    item?: unknown;
    data?: {
      item?: unknown;
    };
  };
  const nestedData = payload.data && typeof payload.data === 'object' && !Array.isArray(payload.data) ? payload.data : null;
  return normalizeWorkServerDeploymentState(payload.item ?? nestedData?.item ?? null);
}

const TEST_SERVER_DEPLOYMENT_STEP_KEYS = [
  'commit-main-worktree',
  'push-origin-main',
  'build-test-app',
  'deploy-test-server',
] as const;

function normalizeTestServerDeploymentStepKey(value: unknown): TestServerDeploymentStepKey | null {
  return TEST_SERVER_DEPLOYMENT_STEP_KEYS.includes(value as TestServerDeploymentStepKey)
    ? (value as TestServerDeploymentStepKey)
    : null;
}

function normalizeTestServerDeploymentPhase(value: unknown): TestServerDeploymentPhase {
  return value === 'commit-main-worktree'
    || value === 'push-origin-main'
    || value === 'build-test-app'
    || value === 'deploy-test-server'
    || value === 'completed'
    || value === 'failed'
    ? value
    : 'idle';
}

function normalizeTestServerDeploymentSteps(value: unknown): TestServerDeploymentStep[] {
  if (!Array.isArray(value)) {
    return TEST_SERVER_DEPLOYMENT_STEP_KEYS.map((key) => ({
      key,
      status: 'pending',
      detail: null,
      updatedAt: null,
    }));
  }

  const normalizedByKey = new Map<TestServerDeploymentStepKey, TestServerDeploymentStep>();

  value.forEach((item) => {
    if (!item || typeof item !== 'object') {
      return;
    }

    const candidate = item as Record<string, unknown>;
    const key = normalizeTestServerDeploymentStepKey(candidate.key);

    if (!key) {
      return;
    }

    normalizedByKey.set(key, {
      key,
      status:
        candidate.status === 'running'
        || candidate.status === 'completed'
        || candidate.status === 'failed'
        || candidate.status === 'pending'
          ? candidate.status
          : 'pending',
      detail: typeof candidate.detail === 'string' ? candidate.detail : null,
      updatedAt: typeof candidate.updatedAt === 'string' ? candidate.updatedAt : null,
    });
  });

  return TEST_SERVER_DEPLOYMENT_STEP_KEYS.map((key) => normalizedByKey.get(key) ?? {
    key,
    status: 'pending',
    detail: null,
    updatedAt: null,
  });
}

function normalizeTestServerDeploymentState(value: unknown): TestServerDeploymentState | null {
  if (!value || typeof value !== 'object') {
    return null;
  }

  const item = value as Record<string, unknown>;

  return {
    status: item.status === 'running' || item.status === 'completed' || item.status === 'failed' ? item.status : 'idle',
    phase: normalizeTestServerDeploymentPhase(item.phase),
    summary: typeof item.summary === 'string' ? item.summary : null,
    startedAt: typeof item.startedAt === 'string' ? item.startedAt : null,
    updatedAt: typeof item.updatedAt === 'string' ? item.updatedAt : null,
    completedAt: typeof item.completedAt === 'string' ? item.completedAt : null,
    lastError: typeof item.lastError === 'string' ? item.lastError : null,
    logExcerpt: typeof item.logExcerpt === 'string' ? item.logExcerpt : null,
    steps: normalizeTestServerDeploymentSteps(item.steps),
  };
}

function extractTestServerDeploymentState(response: unknown): TestServerDeploymentState | null {
  if (!response || typeof response !== 'object') {
    return null;
  }

  const payload = response as {
    item?: unknown;
    data?: {
      item?: unknown;
    };
  };
  const nestedData = payload.data && typeof payload.data === 'object' && !Array.isArray(payload.data) ? payload.data : null;
  return normalizeTestServerDeploymentState(payload.item ?? nestedData?.item ?? null);
}

function normalizeServerRestartReservationStatus(value: unknown): ServerRestartReservationStatus {
  return value === 'waiting'
    || value === 'ready'
    || value === 'executing'
    || value === 'recovering'
    || value === 'completed'
    || value === 'cancelled'
    || value === 'failed'
    ? value
    : 'idle';
}

function normalizeServerRestartReservationTarget(value: unknown): ServerRestartReservation['target'] {
  return value === 'test' || value === 'work-server' ? value : 'all';
}

function normalizeServerRestartReservationExecutionPhase(value: unknown): ServerRestartReservationExecutionPhase {
  return value === 'commit-main-worktree'
    || value === 'restart-test'
    || value === 'restart-work-server'
    || value === 'verify-runtime'
    ? value
    : 'idle';
}

function normalizeServerRestartReservationWorkItems(value: unknown): ServerRestartReservationWorkItem[] {
  if (!Array.isArray(value)) {
    return [];
  }

  return value.flatMap((item) => {
    if (!item || typeof item !== 'object') {
      return [];
    }

    const candidate = item as Partial<ServerRestartReservationWorkItem>;
    const kind = candidate.kind === 'automation' ? 'automation' : candidate.kind === 'codex' ? 'codex' : null;
    const status =
      candidate.status === 'running' || candidate.status === 'queued' || candidate.status === 'waiting'
        ? candidate.status
        : null;
    const title = typeof candidate.title === 'string' ? candidate.title.trim() : '';

    if (!kind || !status || !title) {
      return [];
    }

    return [{
      kind,
      status,
      title,
      detail: typeof candidate.detail === 'string' ? candidate.detail : null,
      requestId: typeof candidate.requestId === 'string' ? candidate.requestId : null,
      sessionId: typeof candidate.sessionId === 'string' ? candidate.sessionId : null,
    }];
  });
}

function normalizeServerRestartReservationAutoFix(value: unknown): ServerRestartReservationAutoFix {
  if (!value || typeof value !== 'object') {
    return {
      enabled: false,
      targetKey: null,
      requestId: null,
      sessionId: null,
      status: 'idle',
      summary: null,
      detail: null,
      requestedAt: null,
      startedAt: null,
      completedAt: null,
      failedAt: null,
    };
  }

  const candidate = value as Partial<ServerRestartReservationAutoFix>;

  return {
    enabled: candidate.enabled === true,
    targetKey: candidate.targetKey === 'test' || candidate.targetKey === 'work-server' ? candidate.targetKey : null,
    requestId: typeof candidate.requestId === 'string' ? candidate.requestId : null,
    sessionId: typeof candidate.sessionId === 'string' ? candidate.sessionId : null,
    status:
      candidate.status === 'queued'
      || candidate.status === 'running'
      || candidate.status === 'completed'
      || candidate.status === 'failed'
        ? candidate.status
        : 'idle',
    summary: typeof candidate.summary === 'string' ? candidate.summary : null,
    detail: typeof candidate.detail === 'string' ? candidate.detail : null,
    requestedAt: typeof candidate.requestedAt === 'string' ? candidate.requestedAt : null,
    startedAt: typeof candidate.startedAt === 'string' ? candidate.startedAt : null,
    completedAt: typeof candidate.completedAt === 'string' ? candidate.completedAt : null,
    failedAt: typeof candidate.failedAt === 'string' ? candidate.failedAt : null,
  };
}

function extractServerRestartReservation(response: unknown): ServerRestartReservation {
  if (!response || typeof response !== 'object') {
    throw new Error('재기동 예약 응답 형식이 올바르지 않습니다.');
  }

  const payload = response as {
    item?: unknown;
    data?: {
      item?: unknown;
    };
  };
  const nestedData = payload.data && typeof payload.data === 'object' && !Array.isArray(payload.data) ? payload.data : null;
  const item = payload.item ?? nestedData?.item;

  if (!item || typeof item !== 'object') {
    throw new Error('재기동 예약 정보를 읽지 못했습니다.');
  }

  const reservation = item as Partial<ServerRestartReservation>;
  const workloadSummary =
    reservation.workloadSummary && typeof reservation.workloadSummary === 'object'
      ? (reservation.workloadSummary as Partial<RestartReservationWorkloadSummary>)
      : {};

  return {
    enabled: reservation.enabled === true,
    target: normalizeServerRestartReservationTarget(reservation.target),
    status: normalizeServerRestartReservationStatus(reservation.status),
    requestedAt: typeof reservation.requestedAt === 'string' ? reservation.requestedAt : null,
    requestedByClientId: typeof reservation.requestedByClientId === 'string' ? reservation.requestedByClientId : null,
    lastCheckedAt: typeof reservation.lastCheckedAt === 'string' ? reservation.lastCheckedAt : null,
    nextCheckAt: typeof reservation.nextCheckAt === 'string' ? reservation.nextCheckAt : null,
    waitingReason: typeof reservation.waitingReason === 'string' ? reservation.waitingReason : null,
    workloadSummary: {
      codexRunningCount: Number(workloadSummary.codexRunningCount ?? 0),
      codexQueuedCount: Number(workloadSummary.codexQueuedCount ?? 0),
      automationRunningCount: Number(workloadSummary.automationRunningCount ?? 0),
      automationQueuedCount: Number(workloadSummary.automationQueuedCount ?? 0),
    },
    startedAt: typeof reservation.startedAt === 'string' ? reservation.startedAt : null,
    completedAt: typeof reservation.completedAt === 'string' ? reservation.completedAt : null,
    cancelledAt: typeof reservation.cancelledAt === 'string' ? reservation.cancelledAt : null,
    lastError: typeof reservation.lastError === 'string' ? reservation.lastError : null,
    activeClientCount: Number(reservation.activeClientCount ?? 0),
    notifiedActiveClientsAt:
      typeof reservation.notifiedActiveClientsAt === 'string' ? reservation.notifiedActiveClientsAt : null,
    appOrigin: typeof reservation.appOrigin === 'string' ? reservation.appOrigin : null,
    autoExecuteAt: typeof reservation.autoExecuteAt === 'string' ? reservation.autoExecuteAt : null,
    autoExecuteDelaySeconds: Number(reservation.autoExecuteDelaySeconds ?? 10),
    executionPhase: normalizeServerRestartReservationExecutionPhase(reservation.executionPhase),
    updatedAt: typeof reservation.updatedAt === 'string' ? reservation.updatedAt : null,
    workItems: normalizeServerRestartReservationWorkItems(reservation.workItems),
    autoFix: normalizeServerRestartReservationAutoFix(reservation.autoFix),
  };
}

export async function fetchServerCommands(options?: ServerCommandRequestOptions) {
  const response = await request<unknown>('/server-commands', undefined, options);
  return extractServerCommandItems(response);
}

export async function restartServerCommand(key: ServerCommandKey, options?: { signal?: AbortSignal; shareToken?: string | null }) {
  const response = await request<unknown>(`/server-commands/${key}/actions/restart`, {
    method: 'POST',
    body: JSON.stringify({}),
    signal: options?.signal,
    timeoutMs: key === 'test' || key === 'rel' ? 30000 : 12000,
  }, { shareToken: options?.shareToken });

  return extractServerCommandActionResult(response);
}

export async function deployWorkServerCommand(options?: { signal?: AbortSignal; shareToken?: string | null }) {
  const response = await request<unknown>('/server-commands/work-server/actions/deploy', {
    method: 'POST',
    body: JSON.stringify({}),
    signal: options?.signal,
    timeoutMs: 12000,
  }, { shareToken: options?.shareToken });

  return extractServerCommandActionResult(response);
}

export async function deployTestServerCommand(options?: { signal?: AbortSignal; shareToken?: string | null }) {
  const response = await request<unknown>('/server-commands/test/actions/deploy', {
    method: 'POST',
    body: JSON.stringify({}),
    signal: options?.signal,
    timeoutMs: 120000,
  }, { shareToken: options?.shareToken });

  return extractServerCommandActionResult(response);
}

export async function fetchWorkServerDeploymentStatus(options?: { signal?: AbortSignal; shareToken?: string | null }) {
  const response = await request<unknown>('/server-commands/work-server/deployment', {
    signal: options?.signal,
  }, { shareToken: options?.shareToken });
  return extractWorkServerDeploymentState(response);
}

export async function fetchTestServerDeploymentStatus(options?: { signal?: AbortSignal; shareToken?: string | null }) {
  const response = await request<unknown>('/server-commands/test/deployment', {
    signal: options?.signal,
  }, { shareToken: options?.shareToken });
  return extractTestServerDeploymentState(response);
}

export async function fetchServerRestartReservation(options?: { signal?: AbortSignal; shareToken?: string | null }) {
  const response = await request<unknown>('/server-commands/restart-reservation', {
    signal: options?.signal,
  }, { shareToken: options?.shareToken });
  return extractServerRestartReservation(response);
}

export async function scheduleServerRestartReservation(options?: {
  signal?: AbortSignal;
  autoExecuteDelaySeconds?: number;
  target?: 'all' | 'test' | 'work-server';
  shareToken?: string | null;
}) {
  const response = await request<unknown>('/server-commands/restart-reservation', {
    method: 'PUT',
    body: JSON.stringify({
      target: options?.target,
      autoExecuteDelaySeconds: options?.autoExecuteDelaySeconds,
    }),
    signal: options?.signal,
  }, { shareToken: options?.shareToken });
  return extractServerRestartReservation(response);
}

export async function cancelServerRestartReservation(options?: { signal?: AbortSignal; shareToken?: string | null }) {
  const response = await request<unknown>('/server-commands/restart-reservation', {
    method: 'DELETE',
    signal: options?.signal,
  }, { shareToken: options?.shareToken });
  return extractServerRestartReservation(response);
}

export async function confirmServerRestartReservation(options?: { signal?: AbortSignal; shareToken?: string | null }) {
  const response = await request<unknown>('/server-commands/restart-reservation/confirm', {
    method: 'POST',
    body: JSON.stringify({}),
    signal: options?.signal,
  }, { shareToken: options?.shareToken });
  return extractServerRestartReservation(response);
}
