const { fetchWithUrl, getMetaWithUrl, downloadViaSourceUrl } = require('../media_fetcher');
const logger = require('consola');
const sleep = require('../../utils/sleep');
const findTheBestMatchFromWyCloud = require('../search_songs/find_the_best_match_from_wycloud');
const JobManager = require('../job_manager');
const JobStatus = require('../../consts/job_status');
const JobType = require('../../consts/job_type');
const configManager = require('../config_manager');
const fs = require('fs');
const libPath = require('path');
const utilFs = require('../../utils/fs');
const { downloadFromLocalTmpPath } = require('./download_to_local');
const uploadWithRetryThenMatch = require('./upload_to_wycloud_disk_with_retry_then_match');
const { getSongInfo, buildSongUrl } = require('../music_platform/tunehub');
const { getSongInfo: getWySongInfo } = require('../music_platform/wycloud');

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;
    }
    if (url.indexOf('music.163.com') >= 0) {
        const match = url.match(/id=(\d+)/);
        if (match && match[1]) {
            return { source: 'netease', id: match[1] };
        }
    }
    if (url.indexOf('y.qq.com') >= 0) {
        const match = url.match(/songDetail\/([A-Za-z0-9]+)/);
        if (match && match[1]) {
            return { source: 'qq', id: match[1] };
        }
    }
    return null;
}

function buildSongInfoFromTunehub(source, info) {
    return {
        songName: info.name || '',
        artist: info.artist || '',
        album: info.album || '',
        coverUrl: info.pic || '',
        duration: 0,
        fromMusicPlatform: true,
        resourceForbidden: false,
        source,
        audios: info.url ? [{ url: info.url }] : [],
    };
}

function buildSongInfoFromWyCloud(info) {
    if (!info) {
        return null;
    }
    return {
        songName: info.songName || '',
        artist: info.artists && info.artists.length > 0 ? info.artists[0] : '',
        album: info.album || '',
        coverUrl: info.cover || '',
        duration: info.duration || 0,
        fromMusicPlatform: true,
        resourceForbidden: false,
        source: 'netease',
        audios: [],
    };
}

function buildFallbackSongInfo({ songName, artist, album, source, downloadUrl }) {
    return {
        songName: songName || 'Unknown',
        artist: artist || '',
        album: album || '',
        coverUrl: '',
        duration: 0,
        fromMusicPlatform: true,
        resourceForbidden: false,
        source: source || '',
        audios: downloadUrl ? [{ url: downloadUrl }] : [],
    };
}

module.exports = async function syncSingleSongWithUrl(uid, url, {
    songName = "",
    artist = "",
    album = "",
    songFromWyCloud = null
} = {}, jobId = 0, jobType = JobType.SyncSongFromUrl, playlistName = "", collectRet) {
    // step 1. fetch song info
    let songInfo = null;
    let downloadUrl = url;
    let useTunehubDownload = false;
    const tunehubParams = parseTunehubParams(url) || parsePageUrlParams(url);
    if (tunehubParams) {
        useTunehubDownload = true;
        downloadUrl = buildSongUrl(tunehubParams.source, tunehubParams.id);
        const tunehubInfo = await getSongInfo(tunehubParams.source, tunehubParams.id);
        if (tunehubInfo) {
            songInfo = buildSongInfoFromTunehub(tunehubParams.source, tunehubInfo);
        } else if (tunehubParams.source === 'netease') {
            const wyInfo = await getWySongInfo(uid, tunehubParams.id);
            if (wyInfo) {
                songInfo = buildSongInfoFromWyCloud(wyInfo);
            }
        }
    }
    if (!songInfo) {
        if (!tunehubParams) {
            songInfo = await getMetaWithUrl(url);
            logger.info(songInfo);
            if (songInfo === false || songInfo.isTrial) {
                logger.error(`fetch song info failed or it's a trial song. ${JSON.stringify(songInfo)}`);
                return false;
            }
        } else {
            songInfo = buildFallbackSongInfo({
                songName,
                artist,
                album,
                source: tunehubParams.source,
                downloadUrl,
            });
        }
    }

    await updateJobIfNeed(uid, jobId, songInfo, jobType);

    // step 2. find the best match from wycloud
    if (songFromWyCloud === null) {
        let findSongName, findArtist, findAlbum;
        if (songName !== "" && artist !== "") {
            logger.info(`use the user input song name and artist, ${songName}, ${artist}, ${album}`);
            findSongName = songName;
            findArtist = artist;
            findAlbum = album;
        } else if (songInfo && songInfo.fromMusicPlatform) {
            findSongName = songInfo.songName;
            findArtist = songInfo.artist;
            findAlbum = songInfo.album;
        } 
        songFromWyCloud = await findTheBestMatchFromWyCloud(uid, {
            songName: findSongName,
            artist: findArtist,
            album: findAlbum,
        });
    } else {
        logger.info(`use the songFromWyCloud by params`);
    }

    logger.info('songFromWyCloud:', songFromWyCloud);
    
    // step 3. download
    // should add meta tag if not matched song on wycloud
    let path = false;
    if (useTunehubDownload && downloadUrl) {
        path = await downloadViaSourceUrl(downloadUrl);
    }
    if (path === false) {
        path = await fetchWithUrl(url, {songName: songInfo.songName, addMediaTag: songFromWyCloud ? false : true});
    }
    if (path === false) {
        return false;
    }

    // step 4. upload or download
    logger.info(`handle song start: ${path}`);

    if (jobType === JobType.DownloadSongFromUrl || jobType === JobType.SyncThePlaylistToLocalService) {
        return await downloadFromLocalTmpPath(path, songInfo, playlistName, collectRet);
    } else {
        return await uploadWithRetryThenMatch(uid, path, songInfo, songFromWyCloud);
    }
}

async function updateJobIfNeed(uid, jobId, songInfo, jobType) {
    if (!jobId) {
        return;
    }
    const operation = jobType === JobType.SyncSongFromUrl ? "上传" : "下载";
    await JobManager.updateJob(uid, jobId, {
        name: `${operation}歌曲：${songInfo.songName}`,
        status: JobStatus.InProgress,
        desc: `歌曲: ${songInfo.songName}`,
        tip: "任务开始",
    });
}
