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

import {
  fetchCollectionPayload,
  fetchPodcastPayload,
  fetchEpisodePayload,
} from '../shared/xiaoyuzhou-api.js';

import {
  transformCollection,
  transformPodcast,
  transformEpisode,
} from '../shared/xiaoyuzhou-transform.js';

import { getCacheEntry, setCacheEntry, resolveCacheTtlMs } from './cache-store.js';
import { installPlaybackHandlers, ensureOffscreen } from './playback.js';
import { initializeAutoFetch } from './auto-fetch.js';
const FETCH_ENTITY_HANDLERS = {
  [FETCH_ENTITY_COLLECTION]: handleCollectionFetch,
  [FETCH_ENTITY_PODCAST]: handlePodcastFetch,
  [FETCH_ENTITY_EPISODE]: handleEpisodeFetch,
};

chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
  if (!message || typeof message.type !== 'string') {
    return false;
  }

  switch (message.type) {
    case 'fetchCollection':
      return handleLegacyFetchMessage(message, sendResponse, {
        entity: FETCH_ENTITY_COLLECTION,
        idField: 'collectionId',
        logTag: 'fetchCollection',
      });
    case 'fetchPodcast':
      return handleLegacyFetchMessage(message, sendResponse, {
        entity: FETCH_ENTITY_PODCAST,
        idField: 'podcastId',
        logTag: 'fetchPodcast',
      });
    case 'fetchEpisode':
      return handleLegacyFetchMessage(message, sendResponse, {
        entity: FETCH_ENTITY_EPISODE,
        idField: 'episodeId',
        logTag: 'fetchEpisode',
      });
    case 'xyz.fetch':
      dispatchFetchRequest(message, sendResponse);
      return true;
    case 'xyz.subscribe':
      handleSubscribe(message, sendResponse);
      return true;
    case 'xyz.checkSubscription':
      handleCheckSubscription(message, sendResponse);
      return true;
    // playback control handled in playback.js via its own onMessage listener
    default:
      return false;
  }
});

// Handle subscribe message
async function handleSubscribe(message, sendResponse) {
  try {
    const podcast = message.podcast;
    if (!podcast || !podcast.id) {
      sendResponse({ ok: false, error: 'Missing podcast data' });
      return;
    }

    const { subscriptions = [] } = await chrome.storage.sync.get('subscriptions');

    // Check if already subscribed
    const exists = subscriptions.some(sub => sub.sourceRef === podcast.id);
    if (exists) {
      sendResponse({ ok: true, alreadySubscribed: true });
      return;
    }

    // Add subscription
    subscriptions.push({
      source: podcast.source || 'xiaoyuzhou',
      sourceRef: podcast.id,
      title: podcast.title,
      addedAt: Date.now(),
    });

    await chrome.storage.sync.set({ subscriptions });
    sendResponse({ ok: true });
  } catch (err) {
    console.error('[核桃FM] Subscribe failed:', err);
    sendResponse({ ok: false, error: err.message });
  }
}

// Handle check subscription message
async function handleCheckSubscription(message, sendResponse) {
  try {
    const podcastId = message.podcastId;
    const podcastTitle = message.podcastTitle;
    console.log('[核桃FM] Checking subscription for:', podcastId, podcastTitle);
    if (!podcastId && !podcastTitle) {
      sendResponse({ subscribed: false });
      return;
    }

    const { subscriptions = [] } = await chrome.storage.sync.get('subscriptions');
    console.log('[核桃FM] Current subscriptions:', subscriptions);

    // Check by sourceRef or title
    const subscribed = subscriptions.some(sub =>
      (podcastId && sub.sourceRef === podcastId) ||
      (podcastTitle && sub.title === podcastTitle)
    );
    console.log('[核桃FM] Subscribed:', subscribed);

    sendResponse({ subscribed });
  } catch (err) {
    console.error('[核桃FM] Check subscription failed:', err);
    sendResponse({ subscribed: false });
  }
}

/**
 * 兼容旧版消息格式，将特定 ID 的请求转为统一调度。
 */
function handleLegacyFetchMessage(message, sendResponse, { entity, idField, logTag }) {
  const sourceRef = ensureString(message[idField]);
  if (!sourceRef) {
    sendResponse({ ok: false, error: `Missing ${idField}` });
    return true;
  }

  const payload = {
    source: SOURCE_XIAOYUZHOU,
    entity,
    sourceRef,
  };
  const cacheHints = extractCacheHints(message);
  if (cacheHints) {
    payload.cache = cacheHints;
  }
  const requestId = createDispatcherRequestId(payload);

  executeFetchBySourceRef(payload)
    .then(({ data, meta }) => {
      sendResponse({ ok: true, ...data, meta, requestId });
    })
    .catch(error => {
      const errorPayload = buildFetchErrorResponse(error, payload, requestId);
      console.error(`[核桃FM] ${logTag} failed:`, error);
      sendResponse({
        ok: false,
        error: errorPayload.error.message,
        errorCode: errorPayload.error.code,
        meta: errorPayload.meta,
        requestId: errorPayload.requestId,
      });
    });

  return true;
}

/**
 * 处理统一调度入口消息。
 */
function dispatchFetchRequest(message, sendResponse) {
  const payload = message?.payload ?? {};
  const requestId = ensureString(message?.requestId);
  const effectiveRequestId = requestId || createDispatcherRequestId(payload);

  executeFetchBySourceRef(payload)
    .then(({ data, meta }) => {
      sendResponse({
        ok: true,
        requestId: effectiveRequestId,
        data,
        meta,
      });
    })
    .catch(async error => {
      const errorPayload = buildFetchErrorResponse(error, payload, effectiveRequestId);
      try {
        console.error('[核桃FM] dispatchFetchRequest failed:', error, 'url=', error && error.url ? error.url : '(n/a)');
      } catch (_) {
        console.error('[核桃FM] dispatchFetchRequest failed:', error);
      }
      try {
        const src = ensureString(payload?.source) || SOURCE_XIAOYUZHOU;
        const ref = ensureString(payload?.sourceRef);
        const ent = ensureString(payload?.entity).toLowerCase();
        if (ent === FETCH_ENTITY_PODCAST && ref) {
          const stored = await chrome.storage.local.get({ fetchStatus: {} });
          const map = stored.fetchStatus && typeof stored.fetchStatus === 'object' ? stored.fetchStatus : {};
          const key = `${src}:${ref}`;
          map[key] = {
            lastSuccessAt: map[key]?.lastSuccessAt || null,
            lastErrorAt: Date.now(),
            lastError: (error && (error.message || error.toString())) || 'unknown error',
          };
          await chrome.storage.local.set({ fetchStatus: map });
        }
      } catch (_) { /* ignore */ }
      sendResponse(errorPayload);
    });
}

/**
 * 根据 sourceRef 执行抓取与转换流程。
 */
async function executeFetchBySourceRef(payload) {
  const source = ensureString(payload?.source) || SOURCE_XIAOYUZHOU;
  const entity = ensureString(payload?.entity).toLowerCase();
  const sourceRef = ensureString(payload?.sourceRef);

  if (!sourceRef) {
    throw createFetchError('ERR_MISSING_SOURCE_REF', 'Missing sourceRef for fetch dispatcher');
  }
  if (source !== SOURCE_XIAOYUZHOU) {
    // 非小宇宙来源不通过该统一抓取（例如 RSS），由聚合器自行抓取。
    throw createFetchError('ERR_UNSUPPORTED_SOURCE', `Unsupported source: ${source}`);
  }

  const handler = FETCH_ENTITY_HANDLERS[entity];
  if (!handler) {
    throw createFetchError('ERR_UNSUPPORTED_ENTITY', `Unsupported entity type: ${entity || '(empty)'}`);
  }

  const cacheOptions = normalizeCacheOptions(payload?.cache ?? payload);
  const shouldBypassCache = cacheOptions.bypassCache || entity === FETCH_ENTITY_COLLECTION;
  if (!shouldBypassCache) {
    const cachedEntry = await getCacheEntry({ entity, source, sourceRef });
    if (cachedEntry && cachedEntry.data) {
      return {
        data: cachedEntry.data,
        meta: enrichCachedMeta(cachedEntry.meta, {
          source,
          entity,
          sourceRef,
          cachedAt: cachedEntry.cachedAt,
          expiresAt: cachedEntry.expiresAt,
        }),
      };
    }
  }

  const data = await handler(sourceRef);
  const metaBase = {
    source,
    entity,
    sourceRef,
  };

  const isCacheable = entity !== FETCH_ENTITY_COLLECTION;
  const resolvedTtlMs = isCacheable ? resolveCacheTtlMs(entity, cacheOptions.ttlMs) : 0;
  const nowMs = Date.now();
  const cachedAt = new Date(nowMs).toISOString();
  const expiresAtIso = Number.isFinite(resolvedTtlMs)
    ? new Date(nowMs + resolvedTtlMs).toISOString()
    : null;

  const meta = {
    ...metaBase,
    fetchedAt: cachedAt,
    cache: isCacheable
      ? {
          status: cacheOptions.deferStore ? 'deferred' : 'updated',
          cachedAt,
          expiresAt: expiresAtIso,
          ttlMs: resolvedTtlMs,
        }
      : {
          status: 'uncached',
        },
  };

  if (isCacheable && !cacheOptions.deferStore) {
    await setCacheEntry(
      { entity, source, sourceRef },
      {
        data,
        meta,
        cachedAt,
      },
      { ttlMs: resolvedTtlMs }
    );
  }

  // 记录抓取状态（成功）
  if (entity === FETCH_ENTITY_PODCAST) {
    try {
      const stored = await chrome.storage.local.get({ fetchStatus: {} });
      const map = stored.fetchStatus && typeof stored.fetchStatus === 'object' ? stored.fetchStatus : {};
      const key = `${source}:${sourceRef}`;
      map[key] = {
        lastSuccessAt: Date.now(),
        lastErrorAt: map[key]?.lastErrorAt || null,
        lastError: '',
      };
      await chrome.storage.local.set({ fetchStatus: map });
    } catch (e) { /* ignore */ }
  }

  return {
    data,
    meta,
  };
}

/**
 * 处理合集抓取任务。
 */
async function handleCollectionFetch(sourceRef) {
  const payload = await fetchCollectionPayload(sourceRef);
  logRawPayload('collection', sourceRef, payload);
  return transformCollection(payload, { sourceRef });
}

async function handlePodcastFetch(sourceRef) {
  const payload = await fetchPodcastPayload(sourceRef);
  logRawPayload('podcast', sourceRef, payload);
  return transformPodcast(payload, { sourceRef });
}

/**
 * 处理节目抓取任务。
 */
async function handleEpisodeFetch(sourceRef) {
  const payload = await fetchEpisodePayload(sourceRef);
  logRawPayload('episode', sourceRef, payload);
  return transformEpisode(payload, { sourceRef });
}

/**
 * 组装统一的错误响应结构。
 */
function buildFetchErrorResponse(error, payload, requestId) {
  const normalized = normalizeFetchError(error);
  const source = ensureString(payload?.source) || SOURCE_XIAOYUZHOU;
  const entity = ensureString(payload?.entity).toLowerCase();
  const sourceRef = ensureString(payload?.sourceRef);

  return {
    ok: false,
    requestId: requestId || createDispatcherRequestId(payload),
    error: normalized,
    meta: {
      source,
      entity,
      sourceRef,
    },
  };
}

function logRawPayload(entity, sourceRef, payload) {
  try {
    const pageProps = payload?.pageProps;
    console.debug(`[核桃FM] raw ${entity} payload`, {
      sourceRef,
      hasPageProps: Boolean(pageProps),
      pageProps,
    });
  } catch (err) {
    console.warn('[核桃FM] failed to log raw payload', err);
  }
}

/**
 * 将错误对象归一化为带错误码的结构。
 */
function normalizeFetchError(error) {
  if (!error) {
    return { message: 'Unknown error', code: 'ERR_UNKNOWN' };
  }
  if (typeof error === 'string') {
    return { message: error, code: 'ERR_UNKNOWN' };
  }
  const message = error.message || String(error);
  const code = error.code || error.name || 'ERR_FETCH_FAILED';
  return { message, code };
}

/**
 * 根据请求参数生成服务端请求 ID。
 */
function createDispatcherRequestId(payload) {
  const entity = ensureString(payload?.entity) || 'unknown';
  const sourceRef = ensureString(payload?.sourceRef) || 'unknown';
  const randomPart = Math.random().toString(16).slice(2, 8);
  return `srv:${entity}:${sourceRef}:${Date.now()}:${randomPart}`;
}

// Install playback controller handlers (offscreen audio + control API)
installPlaybackHandlers();
initializeAutoFetch();

// Proactively warm up the offscreen player when possible to reduce first-use latency
try {
  if (chrome?.runtime?.onStartup) {
    chrome.runtime.onStartup.addListener(() => {
      ensureOffscreen(1500).catch(() => {});
    });
  }
  if (chrome?.runtime?.onInstalled) {
    chrome.runtime.onInstalled.addListener(() => {
      ensureOffscreen(1500).catch(() => {});
    });
  }
} catch (_) { /* ignore */ }

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

/**
 * 构造带错误码的抓取异常。
 */
function createFetchError(code, message) {
  const error = new Error(message);
  error.code = code;
  return error;
}

function normalizeCacheOptions(rawOptions) {
  const cache = {};
  if (rawOptions && typeof rawOptions === 'object') {
    const maybeCache =
      typeof rawOptions.cache === 'object' && rawOptions.cache
        ? rawOptions.cache
        : rawOptions;
    if (maybeCache.force === true || maybeCache.forceRefresh === true || maybeCache.refresh === true) {
      cache.force = true;
    }
    if (maybeCache.bypass === true || maybeCache.bypassCache === true || maybeCache.skip === true) {
      cache.bypass = true;
    }
    if (Number.isFinite(Number(maybeCache.ttlMs)) && Number(maybeCache.ttlMs) > 0) {
      cache.ttlMs = Number(maybeCache.ttlMs);
    }
    if (maybeCache.deferStore === true || maybeCache.defer === true) {
      cache.deferStore = true;
    }
  }

  const forceRefresh = Boolean(cache.force);
  const bypassCache = Boolean(cache.bypass || forceRefresh);
  const ttlMs = cache.ttlMs;
  const deferStore = Boolean(cache.deferStore);

  return {
    forceRefresh,
    bypassCache,
    ttlMs,
    deferStore,
  };
}

function extractCacheHints(message) {
  if (!message || typeof message !== 'object') {
    return null;
  }
  const cache = {};
  if (typeof message.cache === 'object' && message.cache) {
    if (message.cache.force === true || message.cache.forceRefresh === true || message.cache.refresh === true) {
      cache.force = true;
    }
    if (message.cache.bypass === true || message.cache.bypassCache === true || message.cache.skip === true) {
      cache.bypass = true;
    }
    if (Number.isFinite(Number(message.cache.ttlMs)) && Number(message.cache.ttlMs) > 0) {
      cache.ttlMs = Number(message.cache.ttlMs);
    }
    if (message.cache.deferStore === true || message.cache.defer === true) {
      cache.deferStore = true;
    }
  }
  if (message.force === true) {
    cache.force = true;
  }
  if (message.bypassCache === true || message.skipCache === true) {
    cache.bypass = true;
  }
  if (Number.isFinite(Number(message.ttlMs)) && Number(message.ttlMs) > 0) {
    cache.ttlMs = Number(message.ttlMs);
  }
  if (message.deferStore === true || message.defer === true) {
    cache.deferStore = true;
  }

  if (!Object.keys(cache).length) {
    return null;
  }
  return cache;
}

function enrichCachedMeta(meta, context) {
  const originalMeta = (meta && typeof meta === 'object') ? meta : {};
  const baseMeta = {
    ...originalMeta,
    ...context,
  };

  const existingCache = (originalMeta.cache && typeof originalMeta.cache === 'object') ? originalMeta.cache : {};
  const cachedAt = context.cachedAt || existingCache.cachedAt || baseMeta.fetchedAt;
  const expiresIso = context.expiresAt ? new Date(context.expiresAt).toISOString() : existingCache.expiresAt;

  const cacheInfo = {
    ...existingCache,
    status: 'hit',
    cachedAt,
    expiresAt: expiresIso,
  };

  const explicitTtl = Number(cacheInfo.ttlMs);
  if (Number.isFinite(explicitTtl) && explicitTtl > 0) {
    cacheInfo.ttlMs = explicitTtl;
  } else {
    const expiresMs = Number(context.expiresAt);
    const cachedMs = cachedAt ? Date.parse(cachedAt) : NaN;
    if (Number.isFinite(expiresMs) && !Number.isNaN(cachedMs)) {
      const ttlMs = expiresMs - cachedMs;
      if (Number.isFinite(ttlMs) && ttlMs > 0) {
        cacheInfo.ttlMs = ttlMs;
      }
    } else {
      delete cacheInfo.ttlMs;
    }
  }

  baseMeta.cache = cacheInfo;
  if (!baseMeta.fetchedAt && cachedAt) {
    baseMeta.fetchedAt = cachedAt;
  }
  return baseMeta;
}
