import {
  SOURCE_XIAOYUZHOU,
  FETCH_ENTITY_COLLECTION,
  FETCH_ENTITY_PODCAST,
  FETCH_ENTITY_EPISODE,
} from './constants.js';

export {
  SOURCE_XIAOYUZHOU,
  FETCH_ENTITY_COLLECTION,
  FETCH_ENTITY_PODCAST,
  FETCH_ENTITY_EPISODE,
} from './constants.js';

const DEFAULT_TIMEOUT_MS = 15000;

/**
 * 通过 source/sourceRef 请求统一的抓取调度接口。
 */
export async function requestResourceBySourceRef(params) {
  const {
    entity,
    sourceRef,
    source = SOURCE_XIAOYUZHOU,
    timeoutMs = DEFAULT_TIMEOUT_MS,
  } = params ?? {};
  const normalizedEntity = ensureString(entity).toLowerCase();
  const normalizedSource = ensureString(source).toLowerCase() || SOURCE_XIAOYUZHOU;
  const normalizedSourceRef = ensureString(sourceRef);

  if (!normalizedEntity) {
    throw createClientError('ERR_MISSING_ENTITY', 'Missing entity type for fetch dispatcher');
  }
  if (!normalizedSourceRef) {
    throw createClientError('ERR_MISSING_SOURCE_REF', 'Missing sourceRef for fetch dispatcher');
  }

  const requestId = createRequestId(normalizedEntity);
  const message = {
    type: 'xyz.fetch',
    requestId,
    payload: {
      entity: normalizedEntity,
      source: normalizedSource,
      sourceRef: normalizedSourceRef,
    },
  };
  const cachePayload = buildCachePayload(params);
  if (cachePayload) {
    message.payload.cache = cachePayload;
  }

  const response = await invokeRuntimeMessage(message, timeoutMs);
  if (!response || typeof response !== 'object') {
    throw createClientError('ERR_EMPTY_RESPONSE', 'No response received from background dispatcher');
  }

  const { ok, data, error, meta } = response;
  if (!ok) {
    const messageText = error?.message ?? 'Fetch dispatcher reported a failure';
    const clientError = createClientError(error?.code ?? 'ERR_DISPATCH_FAILED', messageText);
    clientError.meta = meta || {
      entity: normalizedEntity,
      source: normalizedSource,
      sourceRef: normalizedSourceRef,
    };
    throw clientError;
  }

  return {
    data,
    meta,
    requestId: response.requestId ?? requestId,
  };
}

/**
 * 请求合集数据。
 */
export function fetchCollectionById(sourceRef, options = {}) {
  return requestResourceBySourceRef({
    entity: FETCH_ENTITY_COLLECTION,
    sourceRef,
    ...options,
  });
}

/**
 * 请求播客数据。
 */
export function fetchPodcastById(sourceRef, options = {}) {
  return requestResourceBySourceRef({
    entity: FETCH_ENTITY_PODCAST,
    sourceRef,
    ...options,
  });
}

/**
 * 请求节目数据。
 */
export function fetchEpisodeById(sourceRef, options = {}) {
  return requestResourceBySourceRef({
    entity: FETCH_ENTITY_EPISODE,
    sourceRef,
    ...options,
  });
}

/**
 * 包装 chrome.runtime 的消息调用，增加超时与错误处理。
 */
async function invokeRuntimeMessage(message, timeoutMs) {
  if (!chrome?.runtime?.sendMessage) {
    throw createClientError('ERR_NO_RUNTIME', 'chrome.runtime.sendMessage is not available in this context');
  }

  return new Promise((resolve, reject) => {
    let settled = false;
    const timer = setTimeout(() => {
      if (settled) {
        return;
      }
      settled = true;
      const timeoutError = createClientError(
        'ERR_DISPATCH_TIMEOUT',
        `Request ${message.requestId ?? '(unknown)'} timed out after ${timeoutMs}ms`
      );
      timeoutError.meta = {
        type: message.type,
        timeoutMs,
      };
      reject(timeoutError);
    }, timeoutMs);

    chrome.runtime.sendMessage(message, response => {
      if (settled) {
        return;
      }
      settled = true;
      clearTimeout(timer);

      const lastError = chrome.runtime.lastError;
      if (lastError) {
        const runtimeError = createClientError('ERR_RUNTIME_LAST_ERROR', lastError.message || 'Runtime messaging error');
        runtimeError.meta = { type: message.type };
        reject(runtimeError);
        return;
      }

      resolve(response);
    });
  });
}

/**
 * 生成调用唯一的 requestId。
 */
function createRequestId(entity) {
  const randomPart = Math.random().toString(16).slice(2, 8);
  return `req:${entity || 'unknown'}:${Date.now()}:${randomPart}`;
}

/**
 * 将输入值统一转为字符串。
 */
function ensureString(value) {
  if (typeof value === 'string') {
    return value.trim();
  }
  if (typeof value === 'number') {
    return String(value);
  }
  return '';
}

/**
 * 构造带错误码的客户端错误对象。
 */
function createClientError(code, message) {
  const error = new Error(message);
  error.code = code;
  return error;
}

function buildCachePayload(options) {
  if (!options || typeof options !== 'object') {
    return null;
  }
  const payload = {};
  const candidates = [];
  if (options.cache && typeof options.cache === 'object') {
    candidates.push(options.cache);
  }
  candidates.push(options);

  candidates.forEach(candidate => {
    if (!candidate || typeof candidate !== 'object') {
      return;
    }
    if (candidate.force === true || candidate.forceRefresh === true || candidate.refresh === true) {
      payload.force = true;
      payload.forceRefresh = true;
    }
    if (candidate.bypass === true || candidate.bypassCache === true || candidate.skip === true || candidate.skipCache === true) {
      payload.bypass = true;
      payload.bypassCache = true;
    }
    const ttl = Number(candidate.ttlMs);
    if (Number.isFinite(ttl) && ttl > 0) {
      payload.ttlMs = ttl;
    }
    if (candidate.deferStore === true || candidate.defer === true) {
      payload.deferStore = true;
    }
  });

  return Object.keys(payload).length ? payload : null;
}
