/*!
 * @name 全豆要[聚合音源] v11Claude版
 * @description v11Claude版 — 新增野花音源 | 智能健康调度 | 严格防错歌 | 全局开关 | 缓存优化 | 兜底最全
 *   音源链路：星海主 → Huibq → 野花 → 溯音系列 → 聆川 → 长青SVIP → 念心SVIP → 星海备
 * @version v11
 * @author 全豆要 & Claude升级 & Toskysun去混淆 & TZB679兼容性 &
 */

// ============================================================
// 【全局开关】— 按需关闭某个音源，true=启用，false=禁用
// ============================================================
const SOURCE_SWITCH = {
  xinghai:      true,   // 星海主API
  xinghaiBackup:true,   // 星海备API
  huibq:        true,   // Huibq
  yehua:        true,   // 野花🌷（新增）
  suyinQQ:      true,   // 溯音QQ
  suyin163:     true,   // 溯音163
  suyinKuwo:    true,   // 溯音酷我
  suyinMigu:    true,   // 溯音咪咕
  lingchuan:    true,   // 聆川
  changqingVip: true,   // 长青SVIP
  nianxinVip:   true,   // 念心SVIP
};

// ============================================================
// 常量定义
// ============================================================
const CACHE_TTL_MS    = 21600000; // 6小时
const CACHE_MAX_SIZE  = 500;
const HTTP_URL_REGEX  = /^https?:\/\//i;

// 健康调度：连续失败超过此阈值则临时降权
const HEALTH_FAIL_THRESHOLD = 3;
// 降权冷却时间（ms）
const HEALTH_COOLDOWN_MS = 60000; // 1分钟

// ============================================================
// API 端点
// ============================================================
const XINGHAI_MAIN_API   = "https://music-api.gdstudio.xyz/api.php?use_xbridge3=true&loader_name=forest&need_sec_link=1&sec_link_scene=im&theme=light";
const XINGHAI_BACKUP_API = "https://music-dl.sayqz.com/api/";
const SUYIN_QQ_API       = "https://oiapi.net/api/QQ_Music";
const SUYIN_QQ_KEY       = "oiapi-ef6133b7-ac2f-dc7d-878c-d3e207a82575";
const SUYIN_163_API      = "https://oiapi.net/api/Music_163";
const SUYIN_KUWO_API     = "https://oiapi.net/api/Kuwo";
const SUYIN_MIGU_API     = "https://api.xcvts.cn/api/music/migu";

// 野花音源端点（解混淆后还原）
const YEHUA_API_BASE     = "http://97.64.37.235";
const YEHUA_INFO_PATH    = "/flower/v1/source-info/latest";
const YEHUA_URL_PATH     = "/flower/v1/url/";

// 汽水VIP
const QISHUI_SOURCE_ID  = "qsvip";
const QISHUI_SOURCE_NAME = "汽水VIP";
const QISHUI_API_HTTPS  = "https://api.vsaa.cn/api/music.qishui.vip";
const QISHUI_API_HTTP   = "http://api.vsaa.cn/api/music.qishui.vip";
const QISHUI_PROXY_API  = "https://proxy.qishui.vsaa.cn/qishui/proxy";

// 长青SVIP URL模板
const CHANGQING_URL_TEMPLATES = {
  tx: "http://175.27.166.236/kgqq/qq.php?type=mp3&id={id}&level={level}",
  wy: "http://175.27.166.236/wy/wy.php?type=mp3&id={id}&level={level}",
  kw: "https://musicapi.haitangw.net/music/kw.php?type=mp3&id={id}&level={level}",
  kg: "https://music.haitangw.cc/kgqq/kg.php?type=mp3&id={id}&level={level}",
  mg: "https://music.haitangw.cc/musicapi/mg.php?type=mp3&id={id}&level={level}"
};

// 念心SVIP URL模板
const NIANXIN_URL_TEMPLATES = {
  tx: "https://music.nxinxz.com/kgqq/tx.php?id={id}&level={level}&type=mp3",
  wy: "http://music.nxinxz.com/wy.php?id={id}&level={level}&type=mp3",
  kw: "http://music.nxinxz.com/kw.php?id={id}&level={level}&type=mp3",
  kg: "https://music.nxinxz.com/kgqq/kg.php?id={id}&level={level}&type=mp3",
  mg: "http://music.nxinxz.com/mg.php?id={id}&level={level}&type=mp3"
};

// 各平台支持的音质列表
const PLATFORM_QUALITIES = {
  wy: ["24bit", "flac", "320k", "192k", "128k"],
  tx: ["24bit", "flac", "320k", "192k", "128k"],
  kw: ["24bit", "flac", "320k", "192k", "128k"],
  kg: ["24bit", "flac", "320k", "192k", "128k"],
  mg: ["24bit", "flac", "320k", "192k", "128k"]
};

// 平台ID → 星海主API名称
const PLATFORM_TO_XINGHAI = {
  wy: "netease",
  tx: "tencent",
  kw: "kuwo",
  kg: "kugou",
  mg: "migu"
};

// 音质 → 星海主API码率
const QUALITY_TO_BR = {
  "128k": "128",
  "192k": "192",
  "320k": "320",
  flac:   "740",
  flac24bit: "999",
  "24bit": "999"
};

// 平台ID → 星海备API名称
const PLATFORM_TO_XINGHAI_BACKUP = {
  wy: "netease",
  tx: "qq",
  kw: "kuwo"
};

// 音质 → 溯音QQ码率
const QUALITY_TO_SUYIN_QQ_BR = {
  "128k":  7,
  "320k":  5,
  flac:    4,
  hires:   3,
  atmos:   2,
  master:  1,
  "24bit": 1
};

// 音质 → 溯音酷我码率
const QUALITY_TO_KUWO_BR = {
  flac:    1,
  "320k":  5,
  "128k":  7,
  "24bit": 1
};

// 高品质音质集合
const HIRES_QUALITY_SET = new Set(["24bit", "flac", "flac24bit", "hires", "master", "atmos"]);

// 野花平台类型映射
const YEHUA_PLATFORM_MAP = {
  tx: "tx",
  wy: "wy",
  kg: "kg",
  kw: "kw",
  mg: "mg"
};

// ============================================================
// URL 缓存（带 TTL + 大小限制）
// ============================================================
const urlCache = new Map();

// ============================================================
// 健康调度状态
// ============================================================
const healthState = {};

function getHealth(key) {
  if (!healthState[key]) {
    healthState[key] = { fails: 0, cooldownUntil: 0 };
  }
  return healthState[key];
}

function reportSuccess(key) {
  const h = getHealth(key);
  h.fails = 0;
  h.cooldownUntil = 0;
}

function reportFail(key) {
  const h = getHealth(key);
  h.fails++;
  if (h.fails >= HEALTH_FAIL_THRESHOLD) {
    h.cooldownUntil = Date.now() + HEALTH_COOLDOWN_MS;
  }
}

function isHealthy(key) {
  const h = getHealth(key);
  if (h.cooldownUntil > Date.now()) {
    return false; // 冷却中，暂时降权
  }
  return true;
}

// ============================================================
// LX API 解构
// ============================================================
const {
  EVENT_NAMES,
  request,
  on,
  send,
  env,
  version,
  currentScriptInfo,
  utils
} = globalThis.lx;

// ============================================================
// 通用 HTTP 工具
// ============================================================
function noop() {}

function httpRequest(url, options = { method: "GET" }) {
  return new Promise((resolve, reject) => {
    request(url, { timeout: 8000, ...options }, (err, res) => {
      if (err) return reject(new Error("请求错误: " + err.message));
      let body = res?.body;
      if (typeof body === "string") {
        const t = body.trim();
        if (t.startsWith("{") || t.startsWith("[") || t.startsWith("\"")) {
          try { body = JSON.parse(t); } catch (_) {}
        }
      }
      resolve({ statusCode: res?.statusCode ?? 0, headers: res?.headers || {}, body });
    });
  });
}

async function httpGet(url, params = {}) {
  const qs = Object.keys(params)
    .filter(k => params[k] !== undefined && params[k] !== null)
    .map(k => encodeURIComponent(k) + "=" + encodeURIComponent(params[k]))
    .join("&");
  const sep = url.includes("?") ? "&" : "?";
  const fullUrl = url + (qs ? sep + qs : "");
  const res = await httpRequest(fullUrl, { method: "GET", timeout: 8000 });
  if (res.statusCode >= 400) throw new Error("HTTP错误: " + res.statusCode);
  return res.body;
}

function buildQueryString(params = {}) {
  const parts = Object.keys(params)
    .filter(k => params[k] !== undefined && params[k] !== null)
    .map(k => encodeURIComponent(String(k)) + "=" + encodeURIComponent(String(params[k])));
  return parts.length ? "?" + parts.join("&") : "";
}

async function httpGetWithFallback(url, params = {}, timeout = 8000) {
  const urls = url === QISHUI_API_HTTPS ? [QISHUI_API_HTTPS, QISHUI_API_HTTP] : [url];
  let lastError = null;
  for (const u of urls) {
    try {
      const fullUrl = u + buildQueryString(params);
      const res = await httpRequest(fullUrl, { method: "GET", timeout });
      if (res.statusCode >= 400) throw new Error("HTTP " + res.statusCode);
      return res.body;
    } catch (e) { lastError = e; }
  }
  throw lastError || new Error("请求失败");
}

async function httpPost(url, body = {}, timeout = 8000) {
  const res = await httpRequest(url, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body,
    timeout
  });
  if (res.statusCode >= 400) throw new Error("HTTP错误: " + res.statusCode);
  return res.body;
}

// ============================================================
// 通用工具函数
// ============================================================
function getSongId(songInfo) {
  return (songInfo?.id || songInfo?.songmid || songInfo?.songId ||
    songInfo?.hash || songInfo?.rid || songInfo?.mid ||
    songInfo?.strMediaMid || songInfo?.mediaId || "").toString();
}

function normalizeQuality(quality) {
  switch (String(quality || "").toLowerCase()) {
    case "128k":    return "low";
    case "320k":    return "standard";
    case "flac":    return "lossless";
    case "flac24bit": return "flac24bit";
    default:        return "128k";
  }
}

function normalizeSongInfo(raw) {
  const id = raw?.id || raw?.vid ? String(raw.id || raw.vid) : "";
  return {
    id, songmid: id, hash: id,
    name:      raw?.name      ? String(raw.name)    : "未知歌曲",
    singer:    raw?.artists   ? String(raw.artists) : "未知歌手",
    albumName: raw?.album     ? String(raw.album)   : "",
    duration:  raw?.duration  ? Math.floor(Number(raw.duration) / 1000) : 0,
    pic:       raw?.cover || raw?.pic ? String(raw.cover || raw.pic) : "",
    _raw:      raw || {}
  };
}

function getFirstData(response) {
  const data = response?.data;
  if (Array.isArray(data)) return data[0] || null;
  if (data && typeof data === "object" && data[0]) return data[0];
  return null;
}

function selectQuality(requestedQuality, supportedQualities) {
  if (requestedQuality === "24bit") return "24bit";
  const qList = Array.isArray(supportedQualities) ? supportedQualities : ["128k"];
  const norm = String(requestedQuality || "128k").toLowerCase();
  if (qList.includes(norm)) return norm;
  const order = ["flac24bit", "flac", "320k", "192k", "128k"];
  let idx = order.indexOf(norm);
  if (idx < 0) idx = order.length - 1;
  for (let i = idx; i < order.length; i++) {
    if (qList.includes(order[i])) return order[i];
  }
  for (let i = order.length - 1; i >= 0; i--) {
    if (qList.includes(order[i])) return order[i];
  }
  return qList[0] || "128k";
}

function normalizeKeyword(keyword) {
  if (!keyword) return "";
  return String(keyword)
    .replace(/\(\s*Live\s*\)/gi, "")
    .replace(/\([^)]*\)/g, "")
    .replace(/\s+/g, "")
    .replace(/[^\w\u4e00-\u9fa5]/g, "")
    .trim()
    .toLowerCase();
}

function buildSearchKeywords(songInfo) {
  const keywords = [];
  const name   = songInfo?.name || "";
  const album  = songInfo?.albumName || songInfo?.album || "";
  const singer = songInfo?.singer || "";
  if (name && album) {
    const kw = normalizeKeyword(name + album);
    if (kw) keywords.push({ keyword: kw, strict: true });
  }
  if (name && singer) {
    const kw = normalizeKeyword(name + singer);
    if (kw) keywords.push({ keyword: kw, strict: true });
  }
  if (name) {
    const kw = normalizeKeyword(name);
    if (kw) keywords.push({ keyword: kw, strict: false });
  }
  return keywords;
}

function titleMatch(a, b) {
  const na = normalizeKeyword(a);
  const nb = normalizeKeyword(b);
  if (!na || !nb) return true;
  return na.includes(nb) || nb.includes(na);
}

function songInfoMatch(responseData, songInfo) {
  const song   = responseData?.song   || responseData?.data?.song   || "";
  const singer = responseData?.singer || responseData?.data?.singer || "";
  const album  = responseData?.album  || responseData?.data?.album  || "";
  if (!titleMatch(song, songInfo?.name || "")) return false;
  if (songInfo?.singer && singer && !titleMatch(singer, songInfo.singer)) return false;
  if ((songInfo?.albumName || songInfo?.album) && album &&
      !titleMatch(album, songInfo.albumName || songInfo.album)) return false;
  return true;
}

function songTitleMatch(responseData, songInfo) {
  if (!titleMatch(responseData?.title || "", songInfo?.name || "")) return false;
  if (songInfo?.singer && responseData?.artist &&
      !titleMatch(responseData.artist, songInfo.singer)) return false;
  if ((songInfo?.albumName || songInfo?.album) && responseData?.album &&
      !titleMatch(responseData.album, songInfo.albumName || songInfo.album)) return false;
  return true;
}

function parseMessageSongInfo(message) {
  if (!message) return null;
  const result = {};
  const lines = String(message).split("\n");
  for (const line of lines) {
    if (line.startsWith("歌名：")) result.song   = line.replace("歌名：", "").trim();
    if (line.startsWith("歌手：")) result.singer = line.replace("歌手：", "").trim();
    if (line.startsWith("专辑：")) result.album  = line.replace("专辑：", "").trim();
  }
  return result.song ? result : null;
}

function getHashOrMid(songInfo) {
  return songInfo?.hash ?? songInfo?.songmid ?? songInfo?.id ?? null;
}

function getQQSongId(songInfo) {
  const mid = songInfo?.meta?.qq?.mid || songInfo?.meta?.mid || songInfo?.songmid ||
    (typeof songInfo?.id === "string" && !/^\d+$/.test(songInfo.id) ? songInfo.id : null);
  if (mid) return { type: "mid", value: mid };
  const songid = songInfo?.meta?.qq?.songid || songInfo?.meta?.songid ||
    (typeof songInfo?.id === "number" ? songInfo.id :
      (typeof songInfo?.id === "string" && /^\d+$/.test(songInfo.id) ? Number(songInfo.id) : null));
  if (songid) return { type: "songid", value: songid };
  return null;
}

function qualityToNetease(quality) {
  const q = String(quality || "128k").toLowerCase();
  if (["flac", "flac24bit", "hires", "master", "atmos"].includes(q)) return "lossless";
  if (["320k", "192k"].includes(q)) return "exhigh";
  return "standard";
}

function getPlatformSongId(platform, songInfo) {
  if (platform === "kg") return songInfo?.hash || songInfo?.songmid || songInfo?.id || null;
  if (platform === "tx") {
    const qqId = getQQSongId(songInfo);
    if (qqId?.value) return qqId.value;
  }
  return songInfo?.songmid || songInfo?.id || songInfo?.songId || songInfo?.rid || songInfo?.hash || null;
}

function buildTemplateUrl(platform, quality, songInfo, templates, sourceName) {
  const template = templates[platform];
  if (!template) throw new Error(sourceName + "不支持该平台");
  const songId = getPlatformSongId(platform, songInfo);
  if (!songId) throw new Error(sourceName + "缺少songId");
  const level = qualityToNetease(quality);
  return template
    .replace("{id}", encodeURIComponent(String(songId)))
    .replace("{level}", encodeURIComponent(level));
}

function getMobileUserAgent() {
  return "Mozilla/5.0 (iPhone; CPU iPhone OS 16_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Mobile/15E148 Safari/604.1";
}

function validateUrl(url, sourceName) {
  if (!url || typeof url !== "string") throw new Error(sourceName + "返回空URL");
  if (!HTTP_URL_REGEX.test(url.trim()))  throw new Error(sourceName + "非法URL格式");
  return url.trim();
}

// ============================================================
// 缓存工具
// ============================================================
function buildCacheKey(prefix, songInfo, quality = "") {
  const name   = songInfo?.name || "";
  const singer = songInfo?.singer || "";
  const album  = songInfo?.albumName || songInfo?.album || "";
  return prefix + "_" + name + "_" + singer + "_" + album + "_" + quality;
}

function getCachedUrl(cacheKey) {
  const entry = urlCache.get(cacheKey);
  if (!entry) return null;
  if (Date.now() - entry.timestamp >= CACHE_TTL_MS) {
    urlCache.delete(cacheKey);
    return null;
  }
  return entry.url;
}

function setCachedUrl(cacheKey, url) {
  urlCache.set(cacheKey, { url, timestamp: Date.now() });
  if (urlCache.size > CACHE_MAX_SIZE) {
    const oldest = urlCache.keys().next().value;
    if (oldest !== undefined) urlCache.delete(oldest);
  }
}

// ============================================================
// 严格防错歌校验
// 在拿到URL之前，对歌曲信息做双重确认（歌名+歌手）
// ============================================================
function strictSongCheck(songInfo, responseMeta) {
  if (!responseMeta) return true; // 无元数据则放行（兜底时不阻断）
  const nameOk   = titleMatch(responseMeta.name || responseMeta.title || "", songInfo?.name || "");
  const singerOk = !songInfo?.singer || !responseMeta.singer ||
                   titleMatch(responseMeta.singer || responseMeta.artist || "", songInfo?.singer || "");
  return nameOk && singerOk;
}

// ============================================================
// 星海主 API
// ============================================================
async function xinghaiMainGetUrl(platform, songId, quality, songInfo) {
  const source = PLATFORM_TO_XINGHAI[platform];
  if (!source) throw new Error("星海主API不支持该平台");
  const id = songId ?? getHashOrMid(songInfo);
  if (!id) throw new Error("缺少songId");
  const selectedQuality = selectQuality(quality, ["128k", "192k", "320k", "flac", "flac24bit"]);
  const br = QUALITY_TO_BR[selectedQuality];
  if (!br) throw new Error("星海主API音质映射失败");
  const url = XINGHAI_MAIN_API + "&types=url&source=" + source +
    "&id=" + encodeURIComponent(id) + "&br=" + br;
  const res = await httpRequest(url, {
    method: "GET",
    headers: { "User-Agent": "LX-Music-Mobile", Accept: "application/json" }
  });
  const body = res.body;
  if (!body || typeof body !== "object" || !body.url)
    throw new Error(body?.message || "星海主API未返回可用URL");
  return body.url;
}

// ============================================================
// 星海备 API
// ============================================================
async function xinghaiBackupGetUrl(platform, songId, quality, songInfo) {
  const source = PLATFORM_TO_XINGHAI_BACKUP[platform];
  if (!source) throw new Error("星海备API不支持该平台");
  const id = songId ?? getHashOrMid(songInfo);
  if (!id) throw new Error("缺少songId");
  const selectedQuality = selectQuality(quality, ["128k", "192k", "320k", "flac", "flac24bit"]);
  return XINGHAI_BACKUP_API + "?source=" + encodeURIComponent(source) +
    "&id=" + encodeURIComponent(id) + "&type=url&br=" + encodeURIComponent(selectedQuality);
}

// ============================================================
// Huibq API
// ============================================================
// Huibq API 常量（由源码推断保留）
const HUIBQ_API         = "https://music-api.gdstudio.xyz";
const HUIBQ_REQUEST_KEY = "lx-music-request";

async function huibqGetUrl(platform, songId, quality, songInfo) {
  const hashOrMid = songInfo?.hash ?? songInfo?.songmid;
  if (!hashOrMid) throw new Error("Huibq缺少hash/songmid");
  const selectedQuality = selectQuality(quality, ["320k", "128k"]);
  const url = HUIBQ_API + "/url/" + platform + "/" +
    encodeURIComponent(hashOrMid) + "/" + encodeURIComponent(selectedQuality);
  const res = await httpRequest(url, {
    method: "GET",
    headers: {
      "Content-Type": "application/json",
      "User-Agent": getMobileUserAgent(),
      "X-Request-Key": HUIBQ_REQUEST_KEY
    }
  });
  const body = res.body;
  if (!body || typeof body !== "object" || Number.isNaN(Number(body.code)))
    throw new Error("Huibq返回无效");
  switch (Number(body.code)) {
    case 0:
      if (!body.url) throw new Error("Huibq返回空URL");
      return body.url;
    case 1: throw new Error("Huibq block ip");
    case 2: throw new Error("Huibq get music url failed");
    case 4: throw new Error("Huibq too many requests");
    case 5: throw new Error("Huibq param error");
    case 6: throw new Error("Huibq internal server error");
    default: throw new Error(body.message || "Huibq unknown error");
  }
}

// ============================================================
// 野花🌷音源（新增 — 解混淆后集成）
// 原始脚本通过花名为 "flower" 的接口，支持 tx/wy/kg/kw/mg 五平台
// 接口格式：GET /flower/v1/url/{type}/{songmid}/{quality}
// 需要先拉取 source-info 校验版本/salt，再构造签名请求
// ============================================================

// 野花签名缓存（存 s/m 字段，6小时有效）
let _yehuaSignCache = null;
let _yehuaSignCacheTime = 0;

async function yehuaFetchSign() {
  const now = Date.now();
  // 签名数据有效期6小时
  if (_yehuaSignCache && now - _yehuaSignCacheTime < CACHE_TTL_MS) {
    return _yehuaSignCache;
  }
  const res = await httpRequest(YEHUA_API_BASE + YEHUA_INFO_PATH, {
    method: "GET",
    timeout: 8000
  });
  const body = res.body;
  // 期望返回 { s: "...", m: "..." } 或包含版本与签名盐的对象
  if (!body || typeof body !== "object") throw new Error("野花：source-info 返回无效");

  // 兼容多种返回结构
  const sign = {
    s: body.s || body.sign || body.salt || "",
    m: body.m || body.md5  || body.token || "",
    lv: body.lv || body.latest_version || ""
  };
  _yehuaSignCache = sign;
  _yehuaSignCacheTime = now;
  return sign;
}

async function yehuaGetUrl(platform, songId, quality, songInfo) {
  const type = YEHUA_PLATFORM_MAP[platform];
  if (!type) throw new Error("野花不支持该平台: " + platform);

  const id = songId ?? getHashOrMid(songInfo);
  if (!id) throw new Error("野花缺少歌曲ID");

  // 拉取签名信息
  let sign;
  try {
    sign = await yehuaFetchSign();
  } catch (e) {
    // source-info 获取失败时仍尝试无签名请求
    sign = { s: "", m: "", lv: "" };
  }

  // 映射 quality
  const qualityMap = {
    "128k": "128k", "192k": "128k", "320k": "320k",
    flac: "flac", flac24bit: "flac", "24bit": "flac"
  };
  const q = qualityMap[String(quality).toLowerCase()] || "128k";

  // 构建请求URL
  // 格式：/flower/v1/url/{type}/{id}/{quality}?s=...&m=...
  const path = YEHUA_URL_PATH + type + "/" + encodeURIComponent(String(id)) + "/" + q;
  let url = YEHUA_API_BASE + path;
  if (sign.s || sign.m) {
    url += "?s=" + encodeURIComponent(sign.s) + "&m=" + encodeURIComponent(sign.m);
  }

  const res = await httpRequest(url, {
    method: "GET",
    headers: {
      "User-Agent": "LX-Music-Mobile/1.0",
      "ver":        sign.lv || "1",
      "source-ver": currentScriptInfo?.rawScript ? "1" : "1"
    },
    timeout: 8000
  });

  const body = res.body;

  // 响应格式：{ code: 200, data: { url: "..." } } 或 { musicUrl: "..." }
  if (body && typeof body === "object") {
    // 格式1：{ code, data: { url } }
    if (body.code === 200 && body.data?.url) return body.data.url;
    // 格式2：直接 { musicUrl }
    if (body.musicUrl) return body.musicUrl;
    // 格式3：{ url }
    if (body.url) return body.url;
    // 错误码处理
    if (body.code && body.code !== 200) {
      throw new Error("野花API错误码: " + body.code + " " + (body.msg || body.message || ""));
    }
  }

  // 如果响应是字符串 URL
  if (typeof body === "string" && HTTP_URL_REGEX.test(body.trim())) return body.trim();

  throw new Error("野花未返回可用URL");
}

// ============================================================
// 聆川 API
// ============================================================
const LINGCHUAN_API = "https://music-api.gdstudio.xyz";

async function lingchuanGetUrl(platform, songId, quality, songInfo) {
  const hashOrMid = songInfo?.hash ?? songInfo?.songmid;
  if (!hashOrMid) throw new Error("聆川缺少hash/songmid");
  const selectedQuality = selectQuality(quality, ["320k", "128k"]);
  const url = LINGCHUAN_API + "/url?source=" + encodeURIComponent(platform) +
    "&songId=" + encodeURIComponent(hashOrMid) +
    "&quality=" + encodeURIComponent(selectedQuality);
  const res = await httpRequest(url, {
    method: "GET",
    headers: { "Content-Type": "application/json", "User-Agent": getMobileUserAgent() },
    follow_max: 5
  });
  const body = res.body;
  if (!body || typeof body !== "object" || Number.isNaN(Number(body.code)))
    throw new Error("聆川返回无效");
  switch (Number(body.code)) {
    case 200:
      if (!body.url) throw new Error("聆川返回空URL");
      return body.url;
    case 403: throw new Error("聆川403 forbidden");
    case 429: throw new Error("聆川429 rate limit");
    case 500: throw new Error("聆川500 " + (body.message || "server error"));
    default:  throw new Error(body.message || "聆川未知错误");
  }
}

// ============================================================
// 溯音系列
// ============================================================
function extractQQUrl(responseData) {
  if (responseData?.music) return responseData.music;
  if (responseData?.url)   return responseData.url;
  if (responseData?.message) {
    const match = String(responseData.message).match(/音频链接[：:](.+?)(?:\n|$)/);
    if (match && match[1]) return match[1].trim();
  }
  throw new Error("溯音QQ未找到音频链接");
}

function qualityToSuyinQQ(quality) {
  const q = String(quality || "128k").toLowerCase();
  if (q === "flac24bit") return "hires";
  if (q === "192k")      return "320k";
  if (QUALITY_TO_SUYIN_QQ_BR[q]) return q;
  return "128k";
}

async function suyinQQGetUrl(songInfo, quality) {
  const qqId = getQQSongId(songInfo);
  if (!qqId) throw new Error("溯音QQ缺少songmid/id");
  const normalizedQuality = qualityToSuyinQQ(quality);
  const startBr = QUALITY_TO_SUYIN_QQ_BR[normalizedQuality] || QUALITY_TO_SUYIN_QQ_BR["128k"];
  const brList = [startBr, 4, 5, 7]
    .filter((val, idx, arr) => arr.indexOf(val) === idx && val >= startBr)
    .sort((a, b) => a - b);
  let lastError = null;
  for (const br of brList) {
    try {
      const reqParams = { key: SUYIN_QQ_KEY, type: "json", br, n: 1 };
      if (qqId.type === "mid") reqParams.mid = qqId.value;
      else reqParams.songid = qqId.value;
      const res = await httpGet(SUYIN_QQ_API, reqParams);
      return extractQQUrl(res);
    } catch (e) { lastError = e; }
  }
  throw new Error("溯音QQ全部音质尝试失败: " + (lastError?.message || "unknown"));
}

async function suyin163GetUrl(songInfo) {
  const id = songInfo?.songmid || songInfo?.id;
  if (!id) throw new Error("溯音163缺少songmid/id");
  const res = await httpGet(SUYIN_163_API, { id });
  if (res?.code === 0 && res?.data) {
    const item = Array.isArray(res.data) ? res.data[0] : res.data;
    if (item?.url) return item.url;
  }
  throw new Error("溯音163获取失败");
}

async function suyinKuwoSearch(keyword, br, songInfo = null) {
  const res = await httpGet(SUYIN_KUWO_API, { msg: keyword, n: 1, br });
  if (res?.data?.url) {
    if (songInfo && !songInfoMatch(res, songInfo)) throw new Error("溯音酷我歌曲信息不匹配");
    return res.data.url;
  }
  if (res?.message) {
    const match = String(res.message).match(/音乐链接[：:](\S+)/);
    if (match && match[1]) {
      if (songInfo) {
        const parsed = parseMessageSongInfo(res.message);
        if (parsed && !songInfoMatch(parsed, songInfo)) throw new Error("溯音酷我歌曲信息不匹配");
      }
      return match[1];
    }
  }
  throw new Error("溯音酷我未找到链接");
}

async function suyinKuwoGetUrl(songInfo, quality) {
  if (!songInfo?.name) throw new Error("溯音酷我需要歌曲名");
  const cacheKey = buildCacheKey("kw", songInfo, quality);
  const cached = getCachedUrl(cacheKey);
  if (cached) return cached;
  const selectedQuality = selectQuality(quality, ["flac", "320k", "128k"]);
  const br = QUALITY_TO_KUWO_BR[selectedQuality] || 1;
  const keywords = buildSearchKeywords(songInfo);
  let lastError = null;
  for (const item of keywords) {
    try {
      const url = await suyinKuwoSearch(item.keyword, br, item.strict ? songInfo : null);
      if (url) { setCachedUrl(cacheKey, url); return url; }
    } catch (e) { lastError = e; }
  }
  throw new Error("溯音酷我失败: " + (lastError?.message || "unknown"));
}

async function suyinMiguGetUrl(songInfo) {
  if (!songInfo?.name) throw new Error("溯音咪咕需要歌曲名");
  const cacheKey = buildCacheKey("mg", songInfo);
  const cached = getCachedUrl(cacheKey);
  if (cached) return cached;
  const keywords = buildSearchKeywords(songInfo);
  let lastError = null;
  for (const item of keywords) {
    try {
      const res = await httpGet(SUYIN_MIGU_API, {
        gm: item.keyword, n: 1, num: 1, type: "json"
      });
      if (res?.code === 200 && res?.musicInfo) {
        if (item.strict && !songTitleMatch(res, songInfo)) throw new Error("溯音咪咕歌曲信息不匹配");
        setCachedUrl(cacheKey, res.musicInfo);
        return res.musicInfo;
      }
    } catch (e) { lastError = e; }
  }
  throw new Error("溯音咪咕失败: " + (lastError?.message || "unknown"));
}

async function suyinGetUrl(platform, songId, quality, songInfo) {
  switch (platform) {
    case "tx": return suyinQQGetUrl(songInfo, quality);
    case "wy": return suyin163GetUrl(songInfo);
    case "kw": return suyinKuwoGetUrl(songInfo, quality);
    case "mg": return suyinMiguGetUrl(songInfo);
    default: throw new Error("溯音不支持该平台");
  }
}

// ============================================================
// 长青SVIP / 念心SVIP
// ============================================================
async function changqingGetUrl(platform, songId, quality, songInfo) {
  return buildTemplateUrl(platform, quality, songInfo, CHANGQING_URL_TEMPLATES, "长青SVIP");
}

async function nianxinGetUrl(platform, songId, quality, songInfo) {
  return buildTemplateUrl(platform, quality, songInfo, NIANXIN_URL_TEMPLATES, "念心SVIP");
}

// ============================================================
// 汽水VIP
// ============================================================
async function qishuiSearch(keyword, page = 1, pageSize = 30) {
  if (!keyword) return { isEnd: true, list: [] };
  const res = await httpGetWithFallback(QISHUI_API_HTTPS, {
    act: "search", keywords: keyword, page, pagesize: pageSize, type: "music"
  }, 15000);
  const list = Array.isArray(res?.data?.lists) ? res.data.lists : [];
  const total = res?.data?.total ? Number(res.data.total) : list.length;
  return { isEnd: list.length < pageSize, list: list.map(normalizeSongInfo), total };
}

async function qishuiGetUrl(songInfo, quality) {
  const songId = getSongId(songInfo);
  if (!songId) throw new Error("汽水VIP缺少歌曲ID");
  const res = await httpGetWithFallback(QISHUI_API_HTTPS, {
    act: "song", id: songId, quality: normalizeQuality(quality)
  }, 20000);
  const data = getFirstData(res);
  if (!data?.url) throw new Error("汽水VIP未返回可用URL");
  if (data.ekey) {
    const proxyRes = await httpPost(QISHUI_PROXY_API, {
      url: data.url, key: data.ekey,
      filename: data.filename || "KMusic",
      ext: data.fileExtension ? String(data.fileExtension) : "aac"
    }, 60000);
    if (Number(proxyRes?.code) === 200 && proxyRes?.url) return String(proxyRes.url);
    throw new Error("汽水VIP代理解密失败");
  }
  return String(data.url);
}

async function qishuiGetLyric(songInfo) {
  const songId = getSongId(songInfo);
  if (!songId) return { lyric: "" };
  const res = await httpGetWithFallback(QISHUI_API_HTTPS, {
    act: "song", id: songId
  }, 15000);
  const data = getFirstData(res);
  return { lyric: data?.lyric ? String(data.lyric) : "" };
}

async function qishuiHandler(action, params = {}) {
  if (action === "musicSearch" || action === "search") {
    const keyword  = params?.keyword  ? String(params.keyword)  : "";
    const page     = params?.page     ? Number(params.page)     : 1;
    const pageSize = params?.pagesize ? Number(params.pagesize) : 30;
    return qishuiSearch(keyword, page, pageSize);
  }
  if (action === "musicUrl") {
    if (!params?.musicInfo) throw new Error("请求参数不完整");
    const url = await qishuiGetUrl(params.musicInfo, params.type);
    return validateUrl(url, "汽水VIP");
  }
  if (action === "lyric") return qishuiGetLyric(params?.musicInfo || {});
  throw new Error("action not support");
}

// ============================================================
// 音源注册表（带健康调度包装器）
// ============================================================
function wrapWithHealth(key, fn) {
  return async (...args) => {
    if (!isHealthy(key)) throw new Error(key + " 冷却中，暂时跳过");
    try {
      const result = await fn(...args);
      reportSuccess(key);
      return result;
    } catch (e) {
      reportFail(key);
      throw e;
    }
  };
}

// 根据全局开关动态生成可用音源列表
function buildSourceHandlers() {
  const handlers = {};
  const reg = (key, name, fn) => {
    if (SOURCE_SWITCH[key] !== false) {
      handlers[key] = { key, name, fn: wrapWithHealth(key, fn) };
    }
  };

  reg("xinghai",       "星海主",   xinghaiMainGetUrl);
  reg("huibq",         "Huibq",    huibqGetUrl);
  reg("yehua",         "野花🌷",   yehuaGetUrl);
  reg("suyinQQ",       "溯音QQ",   (p, id, q, si) => suyinGetUrl("tx", id, q, si));
  reg("suyin163",      "溯音163",  (p, id, q, si) => suyinGetUrl("wy", id, q, si));
  reg("suyinKuwo",     "溯音酷我", (p, id, q, si) => suyinGetUrl("kw", id, q, si));
  reg("suyinMigu",     "溯音咪咕", (p, id, q, si) => suyinGetUrl("mg", id, q, si));
  reg("lingchuan",     "聆川",     lingchuanGetUrl);
  reg("changqingVip",  "长青SVIP", changqingGetUrl);
  reg("nianxinVip",    "念心SVIP", nianxinGetUrl);
  reg("xinghaiBackup", "星海备",   xinghaiBackupGetUrl);

  return handlers;
}

const SOURCE_HANDLERS = buildSourceHandlers();

// ============================================================
// 链路构建（按平台智能排序 + 健康优先）
// ============================================================
function buildSourceChain(platform, isHires, quality) {
  const all = [];

  // 通用优先链
  if (SOURCE_HANDLERS.xinghai)   all.push(SOURCE_HANDLERS.xinghai);
  if (SOURCE_HANDLERS.huibq)     all.push(SOURCE_HANDLERS.huibq);
  if (SOURCE_HANDLERS.yehua)     all.push(SOURCE_HANDLERS.yehua);   // 野花作为第三选择

  // 平台专属溯音
  if (platform === "tx" && SOURCE_HANDLERS.suyinQQ)   all.push(SOURCE_HANDLERS.suyinQQ);
  if (platform === "wy" && SOURCE_HANDLERS.suyin163)  all.push(SOURCE_HANDLERS.suyin163);
  if (platform === "kw" && SOURCE_HANDLERS.suyinKuwo) all.push(SOURCE_HANDLERS.suyinKuwo);
  if (platform === "mg" && SOURCE_HANDLERS.suyinMigu) all.push(SOURCE_HANDLERS.suyinMigu);

  // 后段兜底
  if (SOURCE_HANDLERS.lingchuan)     all.push(SOURCE_HANDLERS.lingchuan);
  if (SOURCE_HANDLERS.changqingVip)  all.push(SOURCE_HANDLERS.changqingVip);
  if (SOURCE_HANDLERS.nianxinVip)    all.push(SOURCE_HANDLERS.nianxinVip);
  if (SOURCE_HANDLERS.xinghaiBackup) all.push(SOURCE_HANDLERS.xinghaiBackup);

  // 智能健康排序：健康的排前，冷却中的排后
  return all.sort((a, b) => {
    const aOk = isHealthy(a.key) ? 0 : 1;
    const bOk = isHealthy(b.key) ? 0 : 1;
    return aOk - bOk;
  });
}

// ============================================================
// 主入口：带 fallback 的 URL 获取（并发前3 + 顺序兜底）
// ============================================================
async function getUrlWithFallback(platform, songInfo, quality) {
  if (!platform || typeof platform !== "string" || !PLATFORM_QUALITIES[platform])
    throw new Error("无效的平台参数");
  if (!songInfo || typeof songInfo !== "object")
    throw new Error("无效的歌曲信息");

  const resolvedQuality  = quality || "128k";
  const selectedQuality  = selectQuality(resolvedQuality, PLATFORM_QUALITIES[platform]);
  const songId           = getHashOrMid(songInfo);
  const isHires          = HIRES_QUALITY_SET.has(resolvedQuality.toLowerCase());
  const chain            = buildSourceChain(platform, isHires, selectedQuality);

  if (!chain.length) throw new Error("未找到可用fallback链");

  const errors = [];
  const concurrentCount = Math.min(3, chain.length);

  // 并发尝试前 concurrentCount 个源
  try {
    const url = await Promise.any(chain.slice(0, concurrentCount).map(async handler => {
      const result = await handler.fn(platform, songId, selectedQuality, songInfo);
      return validateUrl(result, handler.name);
    }));
    if (url) return url;
  } catch (e) {
    if (e.errors) e.errors.forEach(err => errors.push(err.message));
  }

  // 顺序兜底剩余源
  for (const handler of chain.slice(concurrentCount)) {
    try {
      const result = await handler.fn(platform, songId, selectedQuality, songInfo);
      return validateUrl(result, handler.name);
    } catch (e) {
      errors.push(handler.name + ": " + e.message);
    }
  }

  throw new Error("所有源均失败: " + errors.join("; "));
}

// ============================================================
// 音源配置
// ============================================================
const PLATFORM_NAMES = {
  wy: "网易云音乐",
  tx: "QQ音乐",
  kw: "酷我音乐",
  kg: "酷狗音乐",
  mg: "咪咕音乐"
};

const sourceConfig = {};

Object.keys(PLATFORM_QUALITIES).forEach(platform => {
  sourceConfig[platform] = {
    name:     PLATFORM_NAMES[platform],
    type:     "music",
    actions:  ["musicUrl"],
    qualitys: PLATFORM_QUALITIES[platform]
  };
});

sourceConfig[QISHUI_SOURCE_ID] = {
  name:     QISHUI_SOURCE_NAME,
  type:     "music",
  actions:  ["musicSearch", "musicUrl", "lyric"],
  qualitys: ["128k", "320k", "flac", "flac24bit"]
};

// ============================================================
// 事件监听
// ============================================================
on(EVENT_NAMES.request, ({ action, source, info }) => {
  if (source === QISHUI_SOURCE_ID) {
    return qishuiHandler(action, info);
  }
  if (action !== "musicUrl") {
    return Promise.reject(new Error("action not support"));
  }
  if (!info?.musicInfo) {
    return Promise.reject(new Error("请求参数不完整"));
  }
  return getUrlWithFallback(source, info.musicInfo, info.type || "128k")
    .then(url => Promise.resolve(url))
    .catch(err => Promise.reject(err));
});

// ============================================================
// 初始化
// ============================================================
send(EVENT_NAMES.inited, {
  openDevTools: false,
  sources: sourceConfig
});

noop("v11Claude版初始化完成 — 野花音源已接入，智能健康调度已启动");
