/**
 * ═══════════════════════════════════════════════════════════
 * 笔趣阁聚合搜索适配器 · Biquge Aggregator Direct Adapter
 * ═══════════════════════════════════════════════════════════
 *
 * 聚合多个免费小说网站进行搜索和下载
 * 这些站点对海外IP友好，无地域封锁
 *
 * 搜索策略:
 *   1. 69shu.buzs.cc (69书吧 · TXT资源丰富)
 *   2. www.bqgda.cc  (笔趣阁 · 免费小说源)
 *
 * 守护: 铸渊 · ICE-GL-ZY001
 * 版权: 国作登字-2026-A-00037559
 * ═══════════════════════════════════════════════════════════
 */

'use strict';

const http = require('http');
const https = require('https');

const USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36';
const CHAPTER_DOWNLOAD_DELAY_MS = 500;

/**
 * 清洁化并验证数字ID
 * @param {string} id - 原始ID
 * @param {string} label - 用于错误消息的标签
 * @returns {string} 纯数字ID
 */
function sanitizeNumericId(id, label) {
  const safe = String(id || '').replace(/[^0-9]/g, '');
  if (!safe) throw new Error(`Invalid ${label}: must be numeric`);
  return safe;
}

// ─── 数据源配置（可动态扩展） ───
const SOURCES = [
  {
    id: 'shu69',
    name: '69书吧',
    searchUrl: 'https://69shu.buzs.cc/modules/article/search.php',
    searchMethod: 'GET',
    searchParam: 'searchkey',
    charset: 'utf-8',
    enabled: true
  }
];

/**
 * HTTP(S) GET 请求 · 返回原始 HTML/文本
 */
function httpGetRaw(urlStr, timeoutMs, extraHeaders) {
  const timeout = timeoutMs || 15000;
  return new Promise((resolve, reject) => {
    let url;
    try {
      url = new URL(urlStr);
    } catch (e) {
      return reject(new Error(`Invalid URL: ${urlStr}`));
    }
    const mod = url.protocol === 'https:' ? https : http;
    const opts = {
      hostname: url.hostname,
      port: url.port || (url.protocol === 'https:' ? 443 : 80),
      path: url.pathname + url.search,
      method: 'GET',
      timeout,
      headers: {
        'User-Agent': USER_AGENT,
        'Accept': 'text/html,application/xhtml+xml,*/*',
        'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
        ...(extraHeaders || {})
      }
    };

    const req = mod.request(opts, (res) => {
      // Handle redirects
      if ((res.statusCode === 301 || res.statusCode === 302) && res.headers.location) {
        const redirectUrl = res.headers.location.startsWith('http')
          ? res.headers.location
          : `${url.protocol}//${url.host}${res.headers.location}`;
        httpGetRaw(redirectUrl, timeout, extraHeaders).then(resolve).catch(reject);
        res.resume();
        return;
      }

      const chunks = [];
      res.on('data', c => chunks.push(c));
      res.on('end', () => {
        resolve({
          body: Buffer.concat(chunks).toString('utf-8'),
          status: res.statusCode,
          headers: res.headers
        });
      });
    });

    req.on('error', reject);
    req.on('timeout', () => { req.destroy(); reject(new Error('timeout')); });
    req.end();
  });
}

/**
 * 从HTML中提取纯文本（安全清理）
 * 先移除标签，再解码实体
 */
function cleanHtml(html) {
  if (!html || typeof html !== 'string') return '';
  let text = html;
  // 保留段落换行
  text = text.replace(/<br\s*\/?>/gi, '\n');
  text = text.replace(/<\/p>/gi, '\n');
  text = text.replace(/<\/div>/gi, '\n');
  // 循环移除标签（最多10轮防DoS）
  let prev;
  let rounds = 0;
  do {
    prev = text;
    text = text.replace(/<[^>]*>/g, '');
    rounds++;
  } while (text !== prev && rounds < 10);
  // 解码HTML实体（&amp; 最后）
  text = text
    .replace(/&#(\d+);/g, (_, code) => String.fromCharCode(parseInt(code, 10)))
    .replace(/&#x([0-9a-fA-F]+);/g, (_, code) => String.fromCharCode(parseInt(code, 16)))
    .replace(/&nbsp;/g, ' ')
    .replace(/&lt;/g, '<')
    .replace(/&gt;/g, '>')
    .replace(/&quot;/g, '"')
    .replace(/&#39;/g, "'")
    .replace(/&amp;/g, '&');
  text = text.replace(/\n{3,}/g, '\n\n').trim();
  return text;
}

/**
 * 从69书吧搜索结果HTML解析书籍列表
 * @param {string} html - 搜索结果页面HTML
 * @returns {Array} 书籍列表
 */
function parse69shuSearchResults(html) {
  const books = [];
  if (!html) return books;

  // 匹配搜索结果中的书籍条目
  // 69shu 搜索结果格式: <div class="newbox">...<a href="/book/ID">书名</a>...<span>作者</span>...
  const bookPattern = /<div[^>]*class="[^"]*newbox[^"]*"[^>]*>([\s\S]*?)<\/div>/gi;
  let match;
  while ((match = bookPattern.exec(html)) !== null) {
    const block = match[1];
    // 提取链接和书名
    const linkMatch = block.match(/<a[^>]*href="\/book\/(\d+)[^"]*"[^>]*>([\s\S]*?)<\/a>/i);
    if (!linkMatch) continue;

    const bookId = linkMatch[1];
    const title = cleanHtml(linkMatch[2]).trim();
    if (!title) continue;

    // 提取作者
    const authorMatch = block.match(/作者[：:]\s*([^<\n]+)/i)
      || block.match(/<a[^>]*class="[^"]*author[^"]*"[^>]*>([\s\S]*?)<\/a>/i);
    const author = authorMatch ? cleanHtml(authorMatch[1]).trim() : '未知';

    // 提取分类
    const categoryMatch = block.match(/分类[：:]\s*([^<\n]+)/i);
    const category = categoryMatch ? cleanHtml(categoryMatch[1]).trim() : '';

    books.push({
      id: `shu69-${bookId}`,
      title,
      author,
      category,
      source: 'shu69',
      source_name: '69书吧',
      source_book_id: bookId,
      word_count: 0,
      has_file: false,
      description: ''
    });
  }

  // 备用解析：通过表格行或列表项解析
  if (books.length === 0) {
    const trPattern = /<tr[^>]*>([\s\S]*?)<\/tr>/gi;
    while ((match = trPattern.exec(html)) !== null) {
      const row = match[1];
      const linkMatch = row.match(/<a[^>]*href="[^"]*\/book\/(\d+)[^"]*"[^>]*>([\s\S]*?)<\/a>/i)
        || row.match(/<a[^>]*href="[^"]*\/(\d+)\/?[^"]*"[^>]*>([\s\S]*?)<\/a>/i);
      if (!linkMatch) continue;

      const bookId = linkMatch[1];
      const title = cleanHtml(linkMatch[2]).trim();
      if (!title || title.length < 2) continue;

      const tdPattern = /<td[^>]*>([\s\S]*?)<\/td>/gi;
      const cells = [];
      let tdMatch;
      while ((tdMatch = tdPattern.exec(row)) !== null) {
        cells.push(cleanHtml(tdMatch[1]).trim());
      }

      // Avoid duplicates
      if (books.some(b => b.source_book_id === bookId)) continue;

      books.push({
        id: `shu69-${bookId}`,
        title,
        author: cells[1] || '未知',
        category: cells[2] || '',
        source: 'shu69',
        source_name: '69书吧',
        source_book_id: bookId,
        word_count: 0,
        has_file: false,
        description: ''
      });
    }
  }

  // Final fallback: generic link pattern
  if (books.length === 0) {
    const linkPattern = /<a[^>]*href="[^"]*\/book\/(\d+)[^"]*"[^>]*>([\s\S]*?)<\/a>/gi;
    while ((match = linkPattern.exec(html)) !== null) {
      const bookId = match[1];
      const title = cleanHtml(match[2]).trim();
      if (!title || title.length < 2) continue;
      if (books.some(b => b.source_book_id === bookId)) continue;
      books.push({
        id: `shu69-${bookId}`,
        title,
        author: '未知',
        category: '',
        source: 'shu69',
        source_name: '69书吧',
        source_book_id: bookId,
        word_count: 0,
        has_file: false,
        description: ''
      });
    }
  }

  return books.slice(0, 20);
}

/**
 * 解析69书吧书籍详情页获取章节目录
 * @param {string} html - 书籍详情页HTML
 * @returns {Array} 章节列表 [{item_id, title, index, url}]
 */
function parse69shuCatalog(html) {
  const chapters = [];
  if (!html) return chapters;

  // 69shu catalog: <li><a href="/book/ID/CHAPTER_ID">章节标题</a></li>
  const chPattern = /<a[^>]*href="[^"]*\/book\/\d+\/(\d+)[^"]*"[^>]*>([\s\S]*?)<\/a>/gi;
  let match;
  while ((match = chPattern.exec(html)) !== null) {
    const chId = match[1];
    const title = cleanHtml(match[2]).trim();
    if (!title) continue;
    chapters.push({
      item_id: chId,
      title,
      index: chapters.length
    });
  }

  // Fallback: more generic chapter link patterns
  if (chapters.length === 0) {
    const altPattern = /<a[^>]*href="([^"]*\d+\.html?)"[^>]*>([\s\S]*?)<\/a>/gi;
    while ((match = altPattern.exec(html)) !== null) {
      const url = match[1];
      const title = cleanHtml(match[2]).trim();
      if (!title || title.length < 2) continue;
      // Extract numeric ID from URL
      const idMatch = url.match(/(\d+)\.html?$/);
      if (!idMatch) continue;
      chapters.push({
        item_id: idMatch[1],
        title,
        index: chapters.length,
        url
      });
    }
  }

  return chapters;
}

/**
 * 解析章节正文内容
 * @param {string} html - 章节页面HTML
 * @returns {string} 纯文本正文
 */
function parseChapterContent(html) {
  if (!html) return '';

  // 尝试多种正文容器选择器
  const contentPatterns = [
    /<div[^>]*id="(?:content|chaptercontent|BookText|booktext|htmlContent)"[^>]*>([\s\S]*?)<\/div>/i,
    /<div[^>]*class="[^"]*(?:content|chapter-content|book-content|read-content|txt)[^"]*"[^>]*>([\s\S]*?)<\/div>/i,
    /<div[^>]*class="[^"]*txtnav[^"]*"[^>]*>([\s\S]*?)<\/div>/i
  ];

  for (const pattern of contentPatterns) {
    const match = html.match(pattern);
    if (match && match[1]) {
      const text = cleanHtml(match[1]);
      if (text.length > 50) return text;
    }
  }

  return '';
}

/**
 * 搜索 69书吧
 * @param {string} query - 搜索关键词
 * @returns {Promise<Array>} 书籍列表
 */
async function search69shu(query) {
  const src = SOURCES.find(s => s.id === 'shu69');
  if (!src || !src.enabled) return [];

  const url = `${src.searchUrl}?${src.searchParam}=${encodeURIComponent(query)}`;
  const { body } = await httpGetRaw(url, 10000, {
    'Referer': 'https://69shu.buzs.cc/'
  });

  return parse69shuSearchResults(body);
}

/**
 * 搜索所有内置源
 * @param {string} query - 搜索关键词
 * @returns {Promise<Array>} 书籍列表
 */
async function search(query) {
  const allResults = [];
  const errors = [];

  // 69书吧
  try {
    const results = await search69shu(query);
    allResults.push(...results);
  } catch (err) {
    errors.push({ source: 'shu69', error: err.message });
  }

  return { results: allResults, errors };
}

/**
 * 获取书籍章节目录
 * @param {string} bookId - 书籍ID (纯数字)
 * @returns {Promise<Array>} 章节列表
 */
async function getCatalog(bookId) {
  const safeBookId = sanitizeNumericId(bookId, 'book ID');

  const url = `https://69shu.buzs.cc/book/${safeBookId}/`;
  const { body } = await httpGetRaw(url, 15000);

  return parse69shuCatalog(body);
}

/**
 * 获取单章内容
 * @param {string} bookId - 书籍ID
 * @param {string} chapterId - 章节ID
 * @returns {Promise<string>} 章节纯文本内容
 */
async function getChapterContent(bookId, chapterId) {
  const safeBookId = sanitizeNumericId(bookId, 'book ID');
  const safeChapterId = sanitizeNumericId(chapterId, 'chapter ID');

  const url = `https://69shu.buzs.cc/book/${safeBookId}/${safeChapterId}`;
  const { body } = await httpGetRaw(url, 15000);

  return parseChapterContent(body);
}

/**
 * 下载完整书籍
 * @param {string} bookId - 书籍ID
 * @param {string} title - 书名
 * @param {string} author - 作者
 * @param {function} onProgress - 进度回调 (current, total, message)
 * @returns {Promise<string>} 完整书籍TXT内容
 */
async function downloadBook(bookId, title, author, onProgress) {
  const chapters = await getCatalog(bookId);
  if (!chapters || chapters.length === 0) {
    throw new Error('无法获取章节目录');
  }

  if (onProgress) onProgress(0, chapters.length, `共${chapters.length}章，开始下载...`);

  const contents = [];
  let failCount = 0;
  const MAX_CONSECUTIVE_FAILS = 10;
  let consecutiveFails = 0;

  for (let i = 0; i < chapters.length; i++) {
    const ch = chapters[i];
    if (!ch.item_id) continue;

    try {
      const text = await getChapterContent(bookId, ch.item_id);
      if (text && text.length > 20) {
        contents.push(`${ch.title}\n\n${text}`);
        consecutiveFails = 0;
      } else {
        failCount++;
        consecutiveFails++;
      }
    } catch (err) {
      failCount++;
      consecutiveFails++;
      console.warn(`[biquge-direct] 章节 ${i + 1} 下载失败: ${err.message}`);
    }

    // Safety: abort if too many consecutive failures (likely blocked or wrong URL)
    if (consecutiveFails >= MAX_CONSECUTIVE_FAILS) {
      console.warn(`[biquge-direct] 连续${MAX_CONSECUTIVE_FAILS}章失败，中止下载`);
      break;
    }

    if (onProgress) {
      onProgress(i + 1, chapters.length, `下载中 ${i + 1}/${chapters.length} 章...`);
    }

    // 章间延迟，避免请求过快被封
    await new Promise(r => setTimeout(r, CHAPTER_DOWNLOAD_DELAY_MS));
  }

  if (contents.length === 0) {
    throw new Error(`未能获取到任何章节内容 (尝试${chapters.length}章·全部失败)`);
  }

  if (contents.length < chapters.length) {
    console.warn(`[biquge-direct] 部分下载: ${contents.length}/${chapters.length} 章成功 (${failCount}章失败)`);
  }

  return `《${title}》\n作者：${author || '未知'}\n来源：69书吧（直连聚合）\n` +
    `下载时间：${new Date().toISOString()}\n` +
    `成功章节：${contents.length}/${chapters.length}\n\n` +
    contents.join('\n\n───────────────\n\n');
}

/**
 * 健康检查
 */
async function healthCheck() {
  try {
    const { results } = await search('斗破苍穹');
    return {
      reachable: Array.isArray(results) && results.length > 0,
      source: 'biquge-direct',
      name: '笔趣阁聚合(直连)',
      result_count: results.length
    };
  } catch (err) {
    return {
      reachable: false,
      source: 'biquge-direct',
      name: '笔趣阁聚合(直连)',
      error: err.message
    };
  }
}

module.exports = {
  search,
  search69shu,
  getCatalog,
  getChapterContent,
  downloadBook,
  healthCheck,
  cleanHtml,
  parse69shuSearchResults,
  parse69shuCatalog,
  parseChapterContent
};
