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

const WORK_SERVER_TIMEOUT_MS = 12_000;

export type EReaderExtractedArticle = {
  title: string;
  sourceLabel: string;
  url: string;
  lead: string;
  body: string;
  htmlBody?: string;
  tags?: string[];
  signals?: string[];
  listedDate?: string;
  publishedAt?: string;
};

export type EReaderLibraryArticle = {
  id: string;
  title: string;
  sourceLabel: string;
  url: string;
  lead: string;
  body: string;
  htmlBody?: string;
  tags?: string[];
  signals?: string[];
  listedDate?: string;
  publishedAt?: string;
  createdAt?: string;
  updatedAt?: string;
};

export type EReaderNewsArticle = {
  id: string;
  title: string;
  sourceLabel: string;
  url: string;
  lead: string;
  body: string;
  htmlBody?: string;
  tags?: string[];
  signals?: string[];
  listedDate?: string;
  publishedAt?: string;
  createdAt?: string;
  updatedAt?: string;
};

export type EReaderNewsSearchParams = {
  keyword?: string;
  topics?: Array<'politics' | 'economy' | 'society' | 'culture' | 'world' | 'it'>;
  sources?: string[];
  dateFrom: string;
  dateTo: string;
  limit?: number;
};

function resolveWorkServerBaseUrl() {
  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 WORK_SERVER_BASE_URL = resolveWorkServerBaseUrl();
const WORK_SERVER_FALLBACK_BASE_URL =
  !import.meta.env.VITE_WORK_SERVER_URL && WORK_SERVER_BASE_URL === '/api'
    ? resolveWorkServerFallbackBaseUrl()
    : null;
let eReaderShareTokenOverride = '';

class EReaderApiError extends Error {
  status: number;

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

function normalizeShareToken(value: string | null | undefined) {
  return value?.trim() ?? '';
}

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

  return normalizeShareToken(new URLSearchParams(window.location.search).get('shareToken'));
}

function resolveReaderShareToken() {
  return normalizeShareToken(eReaderShareTokenOverride) || readShareTokenFromUrl();
}

export function setEReaderShareTokenOverride(shareToken: string | null | undefined) {
  eReaderShareTokenOverride = normalizeShareToken(shareToken);
}

function parseJsonPayload<T>(text: string, fallbackMessage: string) {
  if (!text.trim()) {
    throw new EReaderApiError(fallbackMessage, 502);
  }

  try {
    return JSON.parse(text) as T;
  } catch (error) {
    if (error instanceof Error && /Unexpected non-whitespace character after JSON/i.test(error.message)) {
      throw new EReaderApiError('뉴스 응답 형식이 올바르지 않습니다. 잠시 후 다시 시도해 주세요.', 502);
    }

    throw new EReaderApiError(fallbackMessage, 502);
  }
}

async function requestOnce<T>(baseUrl: string, path: string, init: RequestInit, fallbackMessage: string): Promise<T> {
  let response: Response;

  try {
    response = await fetch(`${baseUrl}${path}`, init);
  } catch (error) {
    if (error instanceof DOMException && error.name === 'AbortError') {
      throw new EReaderApiError('응답이 지연됩니다. 잠시 후 다시 시도해 주세요.', 408);
    }

    throw error;
  }

  const text = await response.text();

  if (!response.ok) {
    try {
      const payload = JSON.parse(text) as { message?: string };
      throw new EReaderApiError(payload.message || fallbackMessage, response.status);
    } catch (error) {
      if (error instanceof EReaderApiError) {
        throw error;
      }

      throw new EReaderApiError(text || fallbackMessage, response.status);
    }
  }

  const contentType = response.headers.get('content-type') ?? '';

  if (!contentType.toLowerCase().includes('application/json')) {
    throw new EReaderApiError(fallbackMessage, 502);
  }

  return parseJsonPayload<T>(text, fallbackMessage);
}

async function requestReaderApi<T>(path: string, init: RequestInit, fallbackMessage: string) {
  try {
    return await requestOnce<T>(WORK_SERVER_BASE_URL, path, init, fallbackMessage);
  } catch (error) {
    const shouldRetryWithFallback =
      WORK_SERVER_FALLBACK_BASE_URL &&
      WORK_SERVER_FALLBACK_BASE_URL !== WORK_SERVER_BASE_URL &&
      (error instanceof EReaderApiError
        ? error.status === 404 || error.status === 408 || error.status === 502
        : false);

    if (!shouldRetryWithFallback) {
      throw error;
    }

    return requestOnce<T>(WORK_SERVER_FALLBACK_BASE_URL, path, init, fallbackMessage);
  }
}

export async function extractReaderArticle(url: string) {
  const headers = appendClientIdHeader();
  const controller = new AbortController();
  const timeoutId = window.setTimeout(() => controller.abort(), WORK_SERVER_TIMEOUT_MS);
  const token = getRegisteredAccessToken();

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

  try {
    const params = new URLSearchParams({ url: url.trim() });
    const payload = await requestReaderApi<{
      ok: boolean;
      item: EReaderExtractedArticle;
    }>(`/reader/extract?${params.toString()}`, {
      method: 'GET',
      headers,
      signal: controller.signal,
      cache: 'no-store',
    }, '원문 자동 가져오기에 실패했습니다.');

    return payload.item;
  } finally {
    window.clearTimeout(timeoutId);
  }
}

function buildReaderHeaders() {
  const headers = appendClientIdHeader();
  const token = getRegisteredAccessToken();
  const shareToken = resolveReaderShareToken();

  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);
  }

  return headers;
}

export async function listReaderLibraryArticles() {
  const headers = buildReaderHeaders();
  const controller = new AbortController();
  const timeoutId = window.setTimeout(() => controller.abort(), WORK_SERVER_TIMEOUT_MS);

  try {
    const payload = await requestReaderApi<{
      ok: boolean;
      items: EReaderLibraryArticle[];
    }>('/reader/library', {
      method: 'GET',
      headers,
      signal: controller.signal,
      cache: 'no-store',
    }, '서버 서가를 불러오지 못했습니다.');

    return payload.items;
  } finally {
    window.clearTimeout(timeoutId);
  }
}

export async function saveReaderLibraryArticle(article: EReaderLibraryArticle) {
  const headers = buildReaderHeaders();
  headers.set('Content-Type', 'application/json');
  const controller = new AbortController();
  const timeoutId = window.setTimeout(() => controller.abort(), WORK_SERVER_TIMEOUT_MS);

  try {
    const payload = await requestReaderApi<{
      ok: boolean;
      item: EReaderLibraryArticle;
    }>('/reader/library', {
      method: 'POST',
      headers,
      signal: controller.signal,
      cache: 'no-store',
      body: JSON.stringify({ item: article }),
    }, '서버 저장에 실패했습니다.');

    return payload.item;
  } finally {
    window.clearTimeout(timeoutId);
  }
}

export async function searchReaderNews(params: EReaderNewsSearchParams) {
  const headers = buildReaderHeaders();
  const controller = new AbortController();
  const timeoutId = window.setTimeout(() => controller.abort(), WORK_SERVER_TIMEOUT_MS);

  try {
    const query = new URLSearchParams({
      dateFrom: params.dateFrom.trim(),
      dateTo: params.dateTo.trim(),
    });

    if (params.keyword?.trim()) {
      query.set('keyword', params.keyword.trim());
    }

    params.sources?.forEach((source) => {
      const trimmedSource = source.trim();

      if (trimmedSource) {
        query.append('source', trimmedSource);
      }
    });

    params.topics?.forEach((topic) => {
      query.append('topic', topic);
    });

    if (typeof params.limit === 'number' && Number.isFinite(params.limit)) {
      query.set('limit', String(Math.trunc(params.limit)));
    }

    const payload = await requestReaderApi<{
      ok: boolean;
      items: EReaderNewsArticle[];
    }>(`/reader/news?${query.toString()}`, {
      method: 'GET',
      headers,
      signal: controller.signal,
      cache: 'no-store',
    }, '뉴스 검색에 실패했습니다.');

    return payload.items;
  } finally {
    window.clearTimeout(timeoutId);
  }
}
