import { appendClientIdHeader } from '../../app/main/clientIdentity';
import { normalizeAutomationTypeId } from '../../app/main/automationTypeAccess';
import { getRegisteredAccessToken } from '../../app/main/tokenAccess';
import type { BoardAttachment, BoardAutomationType, BoardDraft, BoardPost, BoardRequestItem } from './types';

class BoardApiError extends Error {
  status: number;

  constructor(message: string, status: number) {
    super(message);
    this.name = 'BoardApiError';
    this.status = status;
  }
}

function normalizeBoardAutomationType(value: unknown): BoardAutomationType {
  return normalizeAutomationTypeId(value);
}

function normalizeBoardAttachment(item: unknown): BoardAttachment | null {
  if (!item || typeof item !== 'object') {
    return null;
  }

  const candidate = item as Partial<BoardAttachment>;
  const id = String(candidate.id ?? '').trim();
  const path = String(candidate.path ?? '').trim();

  if (!id || !path) {
    return null;
  }

  return {
    id,
    name: String(candidate.name ?? '').trim() || path.split('/').pop() || '첨부 파일',
    path,
    publicUrl: String(candidate.publicUrl ?? '').trim() || path,
    size: Math.max(0, Number(candidate.size ?? 0) || 0),
    mimeType: String(candidate.mimeType ?? '').trim() || 'application/octet-stream',
  };
}

function normalizeBoardPost(item: BoardPost): BoardPost {
  return {
    ...item,
    automationType: normalizeBoardAutomationType(item.automationType),
    attachments: Array.isArray(item.attachments) ? item.attachments.map(normalizeBoardAttachment).filter(Boolean) as BoardAttachment[] : [],
    requestExecutionMode:
      item.requestExecutionMode === 'after_previous_finished' || item.requestExecutionMode === 'after_previous_success'
        ? item.requestExecutionMode
        : 'all_at_once',
    requestItems: Array.isArray(item.requestItems)
      ? item.requestItems.map((requestItem) => ({
          ...requestItem,
          id: Number(requestItem.id ?? 0),
          boardPostId: Number(requestItem.boardPostId ?? item.id ?? 0),
          sequence: Number(requestItem.sequence ?? 0),
          title: String(requestItem.title ?? '').trim(),
          content: String(requestItem.content ?? ''),
          planItemId:
            requestItem.planItemId === null || requestItem.planItemId === undefined
              ? null
              : Number(requestItem.planItemId),
          automationReceivedAt:
            requestItem.automationReceivedAt === null || requestItem.automationReceivedAt === undefined
              ? null
              : String(requestItem.automationReceivedAt),
          workflowState: String(requestItem.workflowState ?? 'pending') as BoardRequestItem['workflowState'],
          status: String(requestItem.status ?? 'pending') as BoardRequestItem['status'],
          statusLabel: String(requestItem.statusLabel ?? '').trim() || '미접수',
          planStatus:
            requestItem.planStatus === null || requestItem.planStatus === undefined
              ? null
              : String(requestItem.planStatus),
          workerStatus:
            requestItem.workerStatus === null || requestItem.workerStatus === undefined
              ? null
              : String(requestItem.workerStatus),
          lastError:
            requestItem.lastError === null || requestItem.lastError === undefined
              ? null
              : String(requestItem.lastError),
          createdAt: String(requestItem.createdAt ?? ''),
          updatedAt: String(requestItem.updatedAt ?? ''),
        }))
      : [],
    requestSummary: {
      total: Math.max(0, Number(item.requestSummary?.total ?? 0) || 0),
      completed: Math.max(0, Number(item.requestSummary?.completed ?? 0) || 0),
      failed: Math.max(0, Number(item.requestSummary?.failed ?? 0) || 0),
      running: Math.max(0, Number(item.requestSummary?.running ?? 0) || 0),
      queued: Math.max(0, Number(item.requestSummary?.queued ?? 0) || 0),
      waiting: Math.max(0, Number(item.requestSummary?.waiting ?? 0) || 0),
      blocked: Math.max(0, Number(item.requestSummary?.blocked ?? 0) || 0),
    },
  };
}

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

  return '/api';
}

function resolveBoardFallbackBaseUrl() {
  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(/\/+$/, '');
}

const BOARD_API_BASE_URL = resolveBoardApiBaseUrl();
const BOARD_API_FALLBACK_BASE_URL =
  !import.meta.env.VITE_WORK_SERVER_URL && BOARD_API_BASE_URL === '/api'
    ? resolveBoardFallbackBaseUrl()
    : null;

async function requestOnce<T>(baseUrl: string, path: string, init?: RequestInit) {
  const headers = appendClientIdHeader(init?.headers);
  const hasBody = init?.body !== undefined && init.body !== null;
  const method = init?.method?.toUpperCase() ?? 'GET';
  const controller = new AbortController();
  const timeoutId = globalThis.setTimeout(() => controller.abort(), 8000);

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

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

  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 (error instanceof DOMException && error.name === 'AbortError') {
      throw new BoardApiError('게시판 서버 응답이 지연됩니다.', 408);
    }

    throw error;
  }

  globalThis.clearTimeout(timeoutId);

  if (!response.ok) {
    const text = await response.text();
    let message = text || '게시판 요청에 실패했습니다.';

    try {
      const payload = JSON.parse(text) as { message?: string };
      message = payload.message || message;
    } catch {
      // Keep the plain text fallback when the response body is not JSON.
    }

    throw new BoardApiError(message, response.status);
  }

  return response.json() as Promise<T>;
}

async function request<T>(path: string, init?: RequestInit): Promise<T> {
  try {
    return await requestOnce<T>(BOARD_API_BASE_URL, path, init);
  } catch (error) {
    const shouldRetryWithFallback =
      BOARD_API_FALLBACK_BASE_URL &&
      BOARD_API_FALLBACK_BASE_URL !== BOARD_API_BASE_URL &&
      (error instanceof BoardApiError
        ? 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>(BOARD_API_FALLBACK_BASE_URL, path, init);
  }
}

export async function setupBoard() {
  return request<{ ok: boolean; table: string }>('/board/setup', {
    method: 'POST',
    body: JSON.stringify({}),
  });
}

export async function fetchBoardPosts() {
  const response = await request<{ ok: boolean; items: BoardPost[] }>('/board/posts');
  return response.items.map((item) => normalizeBoardPost(item));
}

export async function createBoardPost(draft: BoardDraft) {
  const response = await request<{ ok: boolean; item: BoardPost }>('/board/posts', {
    method: 'POST',
    body: JSON.stringify({
      title: draft.title,
      content: draft.content,
      attachments: draft.attachments,
      automationType: draft.automationType,
      requestExecutionMode: draft.requestExecutionMode,
      requestItems: draft.requestItems.map((requestItem) => ({
        title: requestItem.title,
        content: requestItem.content,
      })),
    }),
  });

  return normalizeBoardPost(response.item);
}

export async function updateBoardPost(draft: BoardDraft) {
  if (!draft.id) {
    throw new Error('수정할 게시글 ID가 없습니다.');
  }

  const response = await request<{ ok: boolean; item: BoardPost }>(`/board/posts/${draft.id}`, {
    method: 'PATCH',
    body: JSON.stringify({
      title: draft.title,
      content: draft.content,
      attachments: draft.attachments,
      automationType: draft.automationType,
      requestExecutionMode: draft.requestExecutionMode,
      requestItems: draft.requestItems.map((requestItem) => ({
        title: requestItem.title,
        content: requestItem.content,
      })),
    }),
  });

  return normalizeBoardPost(response.item);
}

export async function receiveBoardPostAutomation(id: number) {
  const response = await request<{
    ok: boolean;
    item: BoardPost;
    planItemIds: number[];
    alreadyReceived: boolean;
  }>(`/board/posts/${id}/actions/automation-receive`, {
    method: 'POST',
    body: JSON.stringify({}),
  });

  return {
    item: normalizeBoardPost(response.item),
    planItemIds: Array.isArray(response.planItemIds) ? response.planItemIds : [],
    alreadyReceived: response.alreadyReceived,
  };
}

export async function deleteBoardPost(id: number) {
  const response = await request<{ ok: boolean; id: number }>(`/board/posts/${id}`, {
    method: 'DELETE',
  });

  return response.id;
}
