const logger = require('consola');
const os = require('os');
const md5 = require('md5');
const path = require('path');
const cmd = require('../../utils/cmd');
const fs = require('fs');
const configManager = require('../config_manager')
const downloadFile = require('../../utils/download');
const { getSongInfo, buildSongUrl } = require('../music_platform/tunehub');

const { getBinPath } = require('./media_get');

const basePath = path.join(os.tmpdir(), 'melody-tmp-songs');
// create path if not exists
if (!fs.existsSync(basePath)) {
    fs.mkdirSync(basePath);
}
logger.info(`[tmp path] use ${basePath}`)

function parseTunehubParams(url) {
    try {
        const parsed = new URL(url);
        if (!parsed.hostname.includes('music-dl.sayqz.com')) {
            return null;
        }
        const source = parsed.searchParams.get('source');
        const id = parsed.searchParams.get('id');
        if (!source || !id) {
            return null;
        }
        return { source, id };
    } catch (err) {
        return null;
    }
}

function parsePageUrlParams(url) {
    if (!url) {
        return null;
    }
    // 支持网易云音乐PC版和手机版
    if (url.indexOf('music.163.com') >= 0 || url.indexOf('m.music.163.com') >= 0) {
        const match = url.match(/id=(\d+)/);
        if (match && match[1]) {
            logger.info(`[parsePageUrlParams] Parsed Netease URL: ${url} -> netease:${match[1]}`);
            return { source: 'netease', id: match[1] };
        }
    }
    // 支持QQ音乐
    if (url.indexOf('y.qq.com') >= 0) {
        const match = url.match(/songDetail\/([A-Za-z0-9]+)/);
        if (match && match[1]) {
            logger.info(`[parsePageUrlParams] Parsed QQ URL: ${url} -> qq:${match[1]}`);
            return { source: 'qq', id: match[1] };
        }
    }
    logger.info(`[parsePageUrlParams] URL not recognized: ${url}`);
    return null;
}

function buildMetaFromTunehub(source, info, url, songId) {
    return {
        songName: info.name || '',
        artist: info.artist || '',
        album: info.album || '',
        duration: 0,
        coverUrl: info.pic || '',
        publicTime: '',
        isTrial: false,
        resourceType: '',
        audios: [
            {
                url: info.url || buildSongUrl(source, songId),
            }
        ],
        fromMusicPlatform: true,
        resourceForbidden: false,
        source,
        pageUrl: url,
    };
}


async function downloadViaSourceUrl(url) {
    logger.info(`downloadViaSourceUrl params: url: ${url}`);

    const requestHash = md5(url);
    const downloadPath = `${basePath}/${requestHash}.mp3`;
    logger.info(`start download from ${url}`);


    const isSucceed = await downloadFile(url, downloadPath);
    if (!isSucceed) {
        logger.error(`download failed with ${url}`);
        return false;
    }

    if (!fs.existsSync(downloadPath)) {
        logger.error(`download failed with ${url}, the file not exists ${downloadPath}`);
        return false;
    }
    logger.info(`download success, path: ${downloadPath}`);
    return downloadPath;
}

async function fetchWithUrl(url, {
    songName = "",
    addMediaTag = false,
}) {
    logger.info(`fetchWithUrl params: ${JSON.stringify(arguments)}`);
    if (songName) {
        songName = songName.replace(/ /g, '').replace(/\./g, '').replace(/\//g, '').replace(/"/g, '');
    }
    const requestHash = md5(`${url}${songName}${addMediaTag}`);
    const fileBasePath = `${basePath}/${requestHash}`;
    try {
        fs.mkdirSync(fileBasePath, { recursive: true });
    } catch (err) {
        logger.error('create dir failed', err);
        return false;
    }

    // Try TuneHub first for supported URLs
    const params = parseTunehubParams(url) || parsePageUrlParams(url);
    if (params) {
        logger.info(`[fetchWithUrl] Trying TuneHub first for ${params.source}:${params.id}`);
        try {
            const downloadUrl = buildSongUrl(params.source, params.id);
            if (downloadUrl) {
                logger.info(`[fetchWithUrl] TuneHub download URL: ${downloadUrl}`);
                const downloadPath = `${fileBasePath}/${songName ? songName : requestHash}.mp3`;
                const isSucceed = await downloadFile(downloadUrl, downloadPath);
                if (isSucceed && fs.existsSync(downloadPath)) {
                    logger.info(`[fetchWithUrl] Download success via TuneHub: ${downloadPath}`);
                    return downloadPath;
                }
                logger.warn(`[fetchWithUrl] TuneHub download failed, will try media-get as fallback`);
            }
        } catch (error) {
            logger.error(`[fetchWithUrl] TuneHub download failed for ${params.source}:${params.id}`, error);
        }
    } else {
        logger.info(`[fetchWithUrl] URL not recognized as TuneHub-supported format, using media-get`);
    }

    // Fallback to media-get
    addMediaTag = false; // todo: 等到 media-get fix 偶现的 添加 addMediaTag 后 panic 的问题，再移除这行代码
    const downloadPath = `${fileBasePath}/${songName ? songName : requestHash}.mp3`;
    logger.info(`[fetchWithUrl] Falling back to media-get for ${url} (may be unstable)`);

    let args = ['-u', `"${url}"`, '--out', `${downloadPath}`, '-t', 'audio', `${addMediaTag ? '--addMediaTag' : ''}`];

    logger.info(`${getBinPath()} ${args.join(' ')}`);

    try {
        const {code, message} = await cmd(getBinPath(), args);
        logger.info('-------')
        logger.info(code);
        logger.info(message);
        logger.info('-------')
        if (code != 0) {
            logger.error(`[fetchWithUrl] media-get failed with code ${code}: ${message}`);
            return false;
        }

        if (!fs.existsSync(downloadPath)) {
            logger.error(`[fetchWithUrl] media-get succeeded but file not found: ${downloadPath}`);
            return false;
        }
        logger.info(`[fetchWithUrl] media-get download success: ${downloadPath}`);
        return downloadPath;
    } catch (error) {
        logger.error(`[fetchWithUrl] media-get crashed or timed out for ${url}`, error);
        return false;
    }
}

async function getMetaWithUrl(url) {
    logger.info(`getMetaWithUrl from ${url}`);

    const params = parseTunehubParams(url) || parsePageUrlParams(url);
    if (params) {
        logger.info(`[getMetaWithUrl] Trying TuneHub first for ${params.source}:${params.id}`);
        const tunehubInfo = await getSongInfo(params.source, params.id);
        if (tunehubInfo) {
            logger.info(`[getMetaWithUrl] TuneHub success for ${params.source}:${params.id}`);
            return buildMetaFromTunehub(params.source, tunehubInfo, url, params.id);
        }
        logger.warn(`[getMetaWithUrl] TuneHub failed for ${params.source}:${params.id}, will try media-get as fallback`);
    } else {
        logger.info(`[getMetaWithUrl] URL not recognized as TuneHub-supported format, trying media-get`);
    }

    // Fallback to media-get only if TuneHub fails or URL format not supported
    logger.warn(`[getMetaWithUrl] Falling back to media-get for ${url} (may be unstable)`);
    let args = ['-u', `"${url}"`, '-m', '--infoFormat=json', '-l=silence'];

    try {
        const {code, message} = await cmd(getBinPath(), args);
        logger.info('-------')
        logger.info(code);
        // logger.info(message);
        logger.info('-------')
        if (code != 0) {
            logger.error(`getMetaWithUrl failed with ${url}, err: ${message}`);
            return false;
        }

        let meta;
        try {
            meta = JSON.parse(message);
        } catch (e) {
            logger.error(`[getMetaWithUrl] Failed to parse media-get response: ${e}`, message)
            return false;
        }

        return {
            songName: meta.title,
            artist: meta.artist,
            album: meta.album,
            duration: meta.duration,
            coverUrl: meta.cover_url,
            publicTime: meta.public_time,
            isTrial: meta.is_trial,
            resourceType: meta.resource_type,
            audios: meta.audios,
            fromMusicPlatform: meta.from_music_platform,
            resourceForbidden: meta.resource_forbidden,
            source: meta.source
        }
    } catch (error) {
        logger.error(`[getMetaWithUrl] media-get crashed or timed out for ${url}`, error);
        return false;
    }
}

async function searchSongFromAllPlatform({
    keyword,
    songName, artist, album
}) {
    logger.info(`searchSong with ${JSON.stringify(arguments)}`);

    const globalConfig = await configManager.getGlobalConfig();

    // Try TuneHub first
    const searchKeyword = keyword || `${songName} ${artist}`.trim();
    if (searchKeyword) {
        logger.info(`[searchSongFromAllPlatform] Using TuneHub to search: ${searchKeyword}`);
        const tunehubResult = await require('../music_platform/tunehub').searchSongs(searchKeyword, {
            aggregate: true,
        });
        if (tunehubResult && tunehubResult.result && tunehubResult.result.songs) {
            logger.info(`[searchSongFromAllPlatform] TuneHub search success, found ${tunehubResult.result.songs.length} songs`);
            return tunehubResult.result.songs.map(song => {
                return {
                    songName: song.name || '',
                    artist: song.artist || '',
                    album: song.album || '',
                    duration: song.duration || 0,
                    url: song.url || '',
                    resourceForbidden: song.resourceForbidden || false,
                    source: song.source || '',
                    fromMusicPlatform: true,
                    score: song.score || 0,
                }
            });
        }
        logger.warn(`[searchSongFromAllPlatform] TuneHub search failed or no results, will try media-get as fallback`);
    } else {
        logger.info(`[searchSongFromAllPlatform] No search keyword provided, using media-get`);
    }

    // Fallback to media-get only if TuneHub fails
    logger.warn(`[searchSongFromAllPlatform] Falling back to media-get (may be unstable)`);
    let searchParams = keyword
        ? ['-k', `"${keyword}"`]
        : ['--searchSongName', `"${songName}"`, '--searchArtist', `"${artist}"`, '--searchAlbum', `"${album}"`];
    searchParams = searchParams.concat([
        '--searchType="song"',
        '-m',
        `--sources=${globalConfig.sources.join(',')}`,
        '--infoFormat=json',
        '-l', 'silence'
    ]);

    logger.info(`cmdStr: ${getBinPath()} ${searchParams.join(' ')}`);

    try {
        const {code, message} = await cmd(getBinPath(), searchParams);
        logger.info('-------')
        logger.info(code);
        // logger.info(message);
        logger.info('-------')
        if (code != 0) {
            logger.error(`searchSong failed with ${arguments}, err: ${message}`);
            return false;
        }

        let jsonResponse;
        try {
            jsonResponse = JSON.parse(message);
        } catch (e) {
            logger.error(`[searchSongFromAllPlatform] Failed to parse media-get response: ${e}`, message)
            return false;
        }

        return jsonResponse.map(searchItem => {
            return {
                songName: searchItem.Name,
                artist: searchItem.Artist,
                album: searchItem.Album,
                duration: searchItem.Duration,
                url: searchItem.Url,
                resourceForbidden: searchItem.ResourceForbidden,
                source: searchItem.Source,
                fromMusicPlatform: searchItem.FromMusicPlatform,
                score: searchItem.Score,
            }
        })
    } catch (error) {
        logger.error(`[searchSongFromAllPlatform] media-get crashed or timed out`, error);
        return false;
    }
}

module.exports = {
    downloadViaSourceUrl: downloadViaSourceUrl,
    fetchWithUrl: fetchWithUrl,
    getMetaWithUrl: getMetaWithUrl,
    searchSongFromAllPlatform: searchSongFromAllPlatform,
}
