import { appendClientIdHeader, buildTrackedPageUrl, getOrCreateClientId } from '../../app/main/clientIdentity';
import { getRegisteredAccessToken } from '../../app/main/tokenAccess';
import type { AppPageDescriptor } from '../../store/appStore/types';
import type { VisitHistory, VisitorClient } from './types';

export type VisitorClientSearchFilters = {
  search?: string;
  clientId?: string;
  nickname?: string;
  path?: string;
  visitedFrom?: string;
  visitedTo?: string;
};

class HistoryApiError extends Error {
  status: number;

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

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

  return '/api';
}

function resolveHistoryFallbackBaseUrl() {
  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 HISTORY_API_BASE_URL = resolveHistoryApiBaseUrl();
const HISTORY_API_FALLBACK_BASE_URL =
  !import.meta.env.VITE_WORK_SERVER_URL && HISTORY_API_BASE_URL === '/api'
    ? resolveHistoryFallbackBaseUrl()
    : 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');
  }

  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 HistoryApiError('서버 응답이 지연됩니다.', 408);
    }
    throw error;
  }

  globalThis.clearTimeout(timeoutId);

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

    try {
      const payload = JSON.parse(text) as { message?: string };
      throw new HistoryApiError(payload.message || '방문 이력 요청에 실패했습니다.', response.status);
    } catch {
      throw new HistoryApiError(text || '방문 이력 요청에 실패했습니다.', response.status);
    }
  }

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

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

function buildAdminHeaders() {
  const headers = appendClientIdHeader();
  const token = getRegisteredAccessToken();

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

  return headers;
}

export async function reportVisitorPageView(page: AppPageDescriptor) {
  if (typeof window === 'undefined') {
    return;
  }

  const clientId = getOrCreateClientId();

  if (!clientId) {
    return;
  }

  try {
    await request('/history/track', {
      method: 'POST',
      body: JSON.stringify({
        clientId,
        url: buildTrackedPageUrl(page),
        eventType: 'page_view',
        userAgent: window.navigator.userAgent,
      }),
    });
  } catch {
    // 방문 이력 적재 실패는 사용자 흐름을 막지 않습니다.
  }
}

export async function fetchVisitorClients(filters: VisitorClientSearchFilters = {}) {
  const query = new URLSearchParams();

  Object.entries(filters).forEach(([key, value]) => {
    const normalizedValue = value?.trim();

    if (normalizedValue) {
      query.set(key, normalizedValue);
    }
  });

  const suffix = query.toString() ? `?${query.toString()}` : '';
  const response = await request<{ ok: boolean; items: VisitorClient[] }>(`/history/visitors${suffix}`, {
    headers: buildAdminHeaders(),
  });
  return response.items;
}

export async function fetchVisitorDetail(clientId: string) {
  const response = await request<{ ok: boolean; client: VisitorClient; visits: VisitHistory[] }>(
    `/history/visitors/${encodeURIComponent(clientId)}`,
    {
      headers: buildAdminHeaders(),
    },
  );

  return response;
}

export async function updateVisitorNickname(clientId: string, nickname: string) {
  const response = await request<{ ok: boolean; client: VisitorClient }>(
    `/history/visitors/${encodeURIComponent(clientId)}/nickname`,
    {
      method: 'PATCH',
      headers: buildAdminHeaders(),
      body: JSON.stringify({
        nickname,
      }),
    },
  );

  return response.client;
}
