// 引入 dotenv，并执行 config()
// 这会让 Node.js 从 .env 文件中加载环境变量
require('dotenv').config();

const express = require('express');
const path = require('path');
const fs = require('fs');
const fsp = fs.promises; // 引入 fs.promises 以便使用 async/await
const fetch = (...args) => import('node-fetch').then(({default: fetch}) => fetch(...args));

const app = express();

// --- 1. 配置和全局变量 ---

// 从环境变量中读取配置，如果环境变量未设置，则使用默认值
const port = process.env.PORT || 8101;
// Docker 容器内的视频目录路径，将通过 Docker volumes 映射到宿主机上的实际目录
const videosDir = process.env.VIDEOS_DIR || '/videos'; 
const UPDATE_INTERVAL_MS = parseInt(process.env.UPDATE_INTERVAL_MS, 10) || 30 * 60 * 1000; // 默认30分钟
// 从环境变量读取有效的子目录列表，以逗号分隔，并去除空格
const VALID_SUBDIRECTORIES = process.env.VALID_SUBDIRECTORIES 
    ? process.env.VALID_SUBDIRECTORIES.split(',').map(s => s.trim()) 
    : ['分集'];
const IYUU_KEY = process.env.IYUU_KEY || "IYUU20053Tcb524cc74f1cbdc348e75a25124525282bd78bd6";

const indexFilePath = path.join(__dirname, 'series_index.json'); // 索引文件将存放在项目根目录
// 内存缓存，所有 /api/series 请求都将从这里以极速读取数据
let seriesCache = [];

// --- 2. 中间件 ---

app.use(express.json()); // 用于解析 /api/request 的 JSON body

// 托管 'public' 文件夹，用于前端文件
app.use(express.static('public'));

// 将 E:/videos/short 目录映射到 URL 的 /videos 路径下
app.use('/videos', express.static(videosDir));


// --- 3. 核心索引更新函数 (新增) ---
/**
 * 异步扫描视频目录，生成剧集列表，更新缓存并写入 JSON 文件。
 * 这个函数会优先在剧集根目录查找海报，如果找不到，则会继续搜索 VALID_SUBDIRECTORIES 中定义的子目录。
 */
async function updateAndCacheSeries() {
  console.log('（后台任务）开始更新剧集索引...');
  try {
    const dirents = await fsp.readdir(videosDir, { withFileTypes: true });
    
    const seriesPromises = dirents
      .filter(dirent => dirent.isDirectory())
      .map(async (dirent) => {
        const seriesName = dirent.name;
        const seriesPath = path.join(videosDir, seriesName);
        const potentialPosters = ['poster.png', 'poster.jpg', '0.jpg', '0.png', "00.jpg", "poster.jpeg", "poster.webp", "0.jpeg", "00.png"];
        
        // --- 核心修改：分步查找海报 ---
        let posterRelativePath = null; 

        // 1. 优先在剧集根目录查找
        for (const filename of potentialPosters) {
          try {
            await fsp.access(path.join(seriesPath, filename));
            posterRelativePath = filename; // 路径就是文件名本身
            break; // 找到了就跳出循环
          } catch (e) { /* 文件不存在，继续 */ }
        }

        // 2. 如果根目录没找到，则到指定的子目录中查找
        if (!posterRelativePath) {
          try {
            const itemsInSeries = await fsp.readdir(seriesPath, { withFileTypes: true });
            const subdirsToScan = itemsInSeries
              .filter(item => item.isDirectory() && VALID_SUBDIRECTORIES.includes(item.name))
              .map(item => item.name);

            // 使用 labeled loop 以便找到后能跳出所有循环
            search_in_subdirs:
            for (const subdir of subdirsToScan) {
              for (const filename of potentialPosters) {
                const posterFullPath = path.join(seriesPath, subdir, filename);
                try {
                  await fsp.access(posterFullPath);
                  // 【关键】路径必须包含子目录，并使用'/'作为分隔符
                  posterRelativePath = path.posix.join(subdir, filename); 
                  break search_in_subdirs; // 找到一个就彻底退出
                } catch (e) { /* 文件不存在，继续 */ }
              }
            }
          } catch(err) {
            console.warn(`[警告] 扫描 ${seriesName} 的子目录时出错:`, err.message);
          }
        }

        if (!posterRelativePath) {
          console.warn(`[警告] 在 ${seriesName} 目录及其子目录中均未找到任何海报文件。`);
        }

        // 使用找到的相对路径构建最终URL
        const posterUrl = posterRelativePath
          // posterRelativePath 可能是 'poster.jpg' 或 '分集/poster.jpg'
          ? `./videos/${encodeURIComponent(seriesName)}/${posterRelativePath}` 
          : './default-poster.png'; // 备用默认海报

        return {
          name: seriesName,
          posterUrl: posterUrl,
        };
      });

    const newSeriesList = await Promise.all(seriesPromises);
    newSeriesList.sort((a, b) => a.name.localeCompare(b.name, 'zh-CN'));
    seriesCache = newSeriesList;
    await fsp.writeFile(indexFilePath, JSON.stringify(seriesCache, null, 2), 'utf-8');
    console.log(`（后台任务）索引更新成功！共找到 ${seriesCache.length} 个剧集。`);

  } catch (err) {
    console.error('（后台任务）更新索引时发生严重错误:', err);
  }
}

// --- 4. API 路由 ---

// 您的心愿单请求API
app.post('/api/request', async (req, res) => {
    const { dramaName } = req.body;

    if (!dramaName || typeof dramaName !== 'string' || dramaName.trim() === '') {
        return res.status(400).json({ message: '剧集名称不能为空' });
    }
    const IYUU_WEBHOOK_URL = `https://iyuu.cn/${IYUU_KEY}.send`;
    const message = `收到想看剧集：${dramaName.trim()}`;
    const targetUrl = `${IYUU_WEBHOOK_URL}?text=${encodeURIComponent(message)}`;

    try {
        console.log(`正在向 IYUU 推送心愿: ${dramaName}`);
        const response = await fetch(targetUrl, { method: 'POST' });

        if (!response.ok) {
            const errorText = await response.text();
            console.error(`Webhook 推送失败: ${response.status} - ${errorText}`);
            throw new Error(`推送服务返回错误`);
        }
        res.status(200).json({ message: "心愿提交成功！" });

    } catch (error) {
        console.error("处理心愿请求时服务器内部错误:", error);
        res.status(500).json({ message: "服务器在推送心愿时发生错误" });
    }
});

// API: 获取短剧列表 (现在从内存缓存读取，极速响应)
app.get('/api/series', (req, res) => {
  res.json(seriesCache);
});

// API: 获取某个短剧的视频文件列表 (改为 async/await，代码更清晰)
app.get('/api/videos/:seriesName', async (req, res) => {
  // --- 修改：不再在此处定义，而是使用全局常量 ---
  // const validSubdirectories = ['分集', '剧集', 'episodes', 'vids']; 

  try {
    const seriesName = decodeURIComponent(req.params.seriesName);
    const seriesPath = path.join(videosDir, seriesName);
    
    if (!seriesPath.startsWith(videosDir)) {
      return res.status(403).send('非法访问');
    }
    
    let allVideoFiles = [];
    const dirents = await fsp.readdir(seriesPath, { withFileTypes: true });

    const rootVideoFiles = dirents
      .filter(dirent => dirent.isFile() && ['.mp4', '.webm', '.mov', '.mkv', '.avi'].includes(path.extname(dirent.name).toLowerCase()))
      .map(dirent => dirent.name);
    allVideoFiles.push(...rootVideoFiles);

    // --- 修改：使用全局常量 VALID_SUBDIRECTORIES ---
    const subdirsToScan = dirents
        .filter(dirent => dirent.isDirectory() && VALID_SUBDIRECTORIES.includes(dirent.name))
        .map(dirent => dirent.name);

    const subdirVideoPromises = subdirsToScan.map(async (subdirName) => {
        const subdirPath = path.join(seriesPath, subdirName);
        const filesInSubdir = await fsp.readdir(subdirPath);

        return filesInSubdir
            .filter(file => ['.mp4', '.webm', '.mov', '.mkv', '.avi'].includes(path.extname(file).toLowerCase()))
            .map(videoFile => path.posix.join(subdirName, videoFile));
    });

    const nestedVideoArrays = await Promise.all(subdirVideoPromises);
    allVideoFiles.push(...nestedVideoArrays.flat());

    const collator = new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' });
    allVideoFiles.sort(collator.compare);
    
    res.json(allVideoFiles);
  } catch (err) {
    console.error(`读取短剧 "${req.params.seriesName}" 目录时出错:`, err);
    if (err.code === 'ENOENT') {
        return res.status(404).send('短剧不存在');
    }
    res.status(500).send('服务器内部错误');
  }
});

// --- 5. 服务器启动逻辑 (新增的启动模式) ---

(async () => {
  // 步骤 A: 尝试从文件加载现有索引，实现快速启动，避免冷启动时列表为空
  try {
    const indexData = await fsp.readFile(indexFilePath, 'utf-8');
    seriesCache = JSON.parse(indexData);
    console.log(`成功从索引文件加载了 ${seriesCache.length} 条剧集数据到缓存。`);
  } catch (err) {
    console.log('未找到现有索引文件或加载失败。将在后台立即创建新的索引。');
  }

  // 步骤 B: 启动 Express 服务器，使其可以立即响应请求
  app.listen(port, () => {
    // 【保持您的日志风格】
    console.log(`Server running on http://localhost:${port}`);
    console.log(`Frontend served from 'public' directory.`);
    console.log(`Videos & Posters served from '${videosDir}' and mapped to '/videos' URL.`);
  });

  // 步骤 C: 在后台立即执行一次完整的索引更新，以获取最新数据
  await updateAndCacheSeries();

  // 步骤 D: 设置定时器，定期在后台更新索引
  setInterval(updateAndCacheSeries, UPDATE_INTERVAL_MS);
})();
