/**
 * Adaptive Streaming Decision Engine
 * Determines optimal streaming strategy based on network conditions and file characteristics
 * Philosophy: Optimize for user's actual playback context (local, remote, cellular)
 */ import { serverLogger } from "../utils/serverLogger.js";
import path from "path";
/**
 * Parse network hints from query parameters
 */ export function parseNetworkHints(query) {
    const hints = {};
    if (query.network_type) {
        const validTypes = [
            "cellular",
            "wifi",
            "ethernet",
            "unknown"
        ];
        if (validTypes.includes(query.network_type)) {
            hints.network_type = query.network_type;
        }
    }
    if (query.effective_type) {
        const validEffective = [
            "4g",
            "3g",
            "2g",
            "slow-2g"
        ];
        if (validEffective.includes(query.effective_type)) {
            hints.effective_type = query.effective_type;
        }
    }
    if (query.downlink) {
        const downlink = parseFloat(query.downlink);
        if (!isNaN(downlink) && downlink > 0) {
            hints.downlink = downlink;
        }
    }
    if (query.rtt) {
        const rtt = parseInt(query.rtt, 10);
        if (!isNaN(rtt) && rtt > 0) {
            hints.rtt = rtt;
        }
    }
    if (query.save_data === "true" || query.save_data === "1") {
        hints.save_data = true;
    }
    return hints;
}
/**
 * Check if file is lossless format
 */ function isLosslessFormat(filePath) {
    const ext = path.extname(filePath).toLowerCase();
    return [
        ".flac",
        ".wav",
        ".alac",
        ".ape",
        ".dsd",
        ".dsf",
        ".dff"
    ].includes(ext);
}
/**
 * Check if file is already efficiently compressed
 */ function isEfficientFormat(filePath, bitrate) {
    const ext = path.extname(filePath).toLowerCase();
    // Already opus/aac at low bitrate
    if ([
        ".opus",
        ".aac",
        ".m4a"
    ].includes(ext)) {
        if (bitrate && bitrate <= 128) return true;
    }
    // MP3 at reasonable bitrate
    if (ext === ".mp3") {
        if (bitrate && bitrate <= 192) return true;
    }
    return false;
}
/**
 * Determine optimal streaming strategy based on network conditions
 * Philosophy: Balance quality with user's network reality
 */ export function determineStreamingStrategy(track, networkHints, userFormat, userQuality) {
    // 1. User explicit preference wins (respect user control)
    if (userFormat && userQuality) {
        const validFormats = [
            "mp3",
            "aac",
            "opus"
        ];
        const validQualities = [
            "low",
            "medium",
            "high"
        ];
        if (validFormats.includes(userFormat) && validQualities.includes(userQuality)) {
            return {
                transcode: true,
                format: userFormat,
                quality: userQuality,
                reason: "user-preference"
            };
        }
    }
    // If only format specified without quality
    if (userFormat && !userQuality) {
        const ext = path.extname(track.path).toLowerCase().slice(1);
        if (userFormat !== ext) {
            return {
                transcode: true,
                format: userFormat,
                quality: "medium",
                reason: "user-format-request"
            };
        }
    }
    // 2. Data saver mode always transcodes to most efficient format
    if (networkHints.save_data) {
        serverLogger.info("Transcoding for data saver mode", {
            path: path.basename(track.path)
        });
        return {
            transcode: true,
            format: "opus",
            quality: "low",
            bitrate: 64,
            reason: "data-saver"
        };
    }
    // 3. Check if file needs transcoding at all
    const isLossless = isLosslessFormat(track.path);
    const isEfficient = isEfficientFormat(track.path, track.bitrate);
    // If already efficient and not on terrible connection, stream as-is
    if (isEfficient && networkHints.network_type !== "cellular") {
        return {
            transcode: false,
            reason: "already-efficient"
        };
    }
    // 4. Cellular network logic
    if (networkHints.network_type === "cellular") {
        // Very slow cellular (2G/slow-2G) - aggressive compression
        if (networkHints.effective_type === "2g" || networkHints.effective_type === "slow-2g" || networkHints.downlink && networkHints.downlink < 1) {
            serverLogger.info("Transcoding for slow cellular", {
                path: path.basename(track.path),
                effectiveType: networkHints.effective_type,
                downlink: networkHints.downlink
            });
            return {
                transcode: true,
                format: "opus",
                quality: "low",
                bitrate: 64,
                reason: "slow-cellular"
            };
        }
        // 3G cellular - moderate compression
        if (networkHints.effective_type === "3g" || networkHints.downlink && networkHints.downlink < 2.5) {
            if (isLossless || track.bitrate && track.bitrate > 256) {
                serverLogger.info("Transcoding for 3G cellular", {
                    path: path.basename(track.path),
                    originalBitrate: track.bitrate
                });
                return {
                    transcode: true,
                    format: "aac",
                    quality: "low",
                    bitrate: 96,
                    reason: "3g-cellular"
                };
            }
        }
        // 4G cellular - transcode lossless only
        if (networkHints.effective_type === "4g" || networkHints.downlink) {
            if (isLossless) {
                const targetBitrate = networkHints.downlink && networkHints.downlink > 5 ? 192 : 128;
                serverLogger.info("Transcoding lossless for 4G cellular", {
                    path: path.basename(track.path),
                    targetBitrate
                });
                return {
                    transcode: true,
                    format: "aac",
                    quality: targetBitrate > 128 ? "medium" : "low",
                    bitrate: targetBitrate,
                    reason: "4g-cellular-lossless"
                };
            }
        }
        // Cellular but good quality file - still transcode lossless
        if (isLossless) {
            return {
                transcode: true,
                format: "aac",
                quality: "medium",
                bitrate: 128,
                reason: "cellular-lossless"
            };
        }
    }
    // 5. High latency connections (even on WiFi)
    if (networkHints.rtt && networkHints.rtt > 300) {
        if (isLossless || track.bitrate && track.bitrate > 320) {
            serverLogger.info("Transcoding for high latency connection", {
                path: path.basename(track.path),
                rtt: networkHints.rtt
            });
            return {
                transcode: true,
                format: "mp3",
                quality: "medium",
                bitrate: 192,
                reason: "high-latency"
            };
        }
    }
    // 6. Low bandwidth connections (regardless of type)
    if (networkHints.downlink && networkHints.downlink < 2) {
        if (isLossless || track.bitrate && track.bitrate > 192) {
            serverLogger.info("Transcoding for low bandwidth", {
                path: path.basename(track.path),
                downlink: networkHints.downlink
            });
            return {
                transcode: true,
                format: "aac",
                quality: "low",
                bitrate: 96,
                reason: "low-bandwidth"
            };
        }
    }
    // 7. Good connection (WiFi/Ethernet) - stream original
    // Even lossless files on good connections
    return {
        transcode: false,
        reason: "good-connection"
    };
}
/**
 * Get informational headers about streaming decision
 */ export function getStreamingHeaders(track, strategy) {
    const headers = {};
    const originalExt = path.extname(track.path).toLowerCase().slice(1);
    headers["X-Original-Format"] = originalExt;
    if (track.bitrate) {
        headers["X-Original-Bitrate"] = track.bitrate.toString();
    }
    if (track.lossless !== undefined && track.lossless !== null) {
        headers["X-Original-Lossless"] = track.lossless.toString();
    }
    if (strategy.transcode) {
        headers["X-Transcoded"] = "true";
        headers["X-Actual-Format"] = strategy.format || "mp3";
        headers["X-Actual-Bitrate"] = (strategy.bitrate || 128).toString();
        headers["X-Transcode-Reason"] = strategy.reason || "unknown";
    } else {
        headers["X-Transcoded"] = "false";
        headers["X-Stream-Reason"] = strategy.reason || "original-quality";
    }
    return headers;
}

//# sourceMappingURL=adaptiveStreaming.js.map