import { appendClientIdHeader } from './clientIdentity';
import { getRegisteredAccessToken } from './tokenAccess';

export type ErrorLogItem = {
  id: number;
  source: 'server' | 'client' | string;
  sourceLabel: string | null;
  errorType: string;
  errorName: string | null;
  errorMessage: string;
  detail: string | null;
  stackTrace: string | null;
  statusCode: number | null;
  requestMethod: string | null;
  requestPath: string | null;
  relatedPlanId: number | null;
  relatedWorkId: string | null;
  context: Record<string, unknown> | null;
  createdAt: string;
};

export type ReportClientErrorPayload = {
  errorType: string;
  errorName?: string | null;
  errorMessage: string;
  detail?: string | null;
  stackTrace?: string | null;
  statusCode?: number | null;
  requestMethod?: string | null;
  requestPath?: string | null;
  context?: Record<string, unknown> | null;
};

class ErrorLogApiError extends Error {
  status: number;

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

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

  return '/api';
}

function resolveWorkServerFallbackBaseUrl() {
  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 ERROR_LOG_API_BASE_URL = resolveErrorLogApiBaseUrl();
const ERROR_LOG_API_FALLBACK_BASE_URL =
  !import.meta.env.VITE_WORK_SERVER_URL && ERROR_LOG_API_BASE_URL === '/api'
    ? resolveWorkServerFallbackBaseUrl()
    : null;

async function requestOnce<T>(baseUrl: string, path: string, init?: RequestInit): Promise<T> {
  const headers = appendClientIdHeader(init?.headers);
  const hasBody = init?.body !== undefined && init.body !== null;
  const method = init?.method?.toUpperCase() ?? 'GET';

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

  const response = await fetch(`${baseUrl}${path}`, {
    ...init,
    headers,
    cache: init?.cache ?? (method === 'GET' ? 'no-store' : undefined),
  });

  if (!response.ok) {
    const text = await response.text();

    try {
      const payload = JSON.parse(text) as { message?: string };
      throw new ErrorLogApiError(payload.message || '에러 로그 요청에 실패했습니다.', response.status);
    } catch {
      throw new ErrorLogApiError(text || '에러 로그 요청에 실패했습니다.', response.status);
    }
  }

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

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

export async function fetchErrorLogs(limit = 50) {
  const token = getRegisteredAccessToken();
  const response = await request<{ ok: boolean; items: ErrorLogItem[] }>(`/error-logs?limit=${limit}`, {
    headers: {
      'X-Access-Token': token,
    },
  });

  return response.items;
}

export async function reportClientError(payload: ReportClientErrorPayload) {
  try {
    await request('/error-logs/report', {
      method: 'POST',
      body: JSON.stringify({
        source: 'client',
        sourceLabel: '프론트엔드',
        errorType: payload.errorType,
        errorName: payload.errorName ?? null,
        errorMessage: payload.errorMessage,
        detail: payload.detail ?? null,
        stackTrace: payload.stackTrace ?? null,
        statusCode: payload.statusCode ?? null,
        requestMethod: payload.requestMethod ?? null,
        requestPath: payload.requestPath ?? null,
        context: payload.context ?? null,
      }),
    });
  } catch {
    // ignore client-side logging failures
  }
}
