/**
 * Similar Tracks Routes
 * Multi-signal similarity scoring to find tracks that sound like a given track
 */ import { Router } from "express";
import { getDatabase } from "../database.js";
import { serverLogger } from "../../utils/serverLogger.js";
/** Camelot wheel harmonic compatibility */ function getHarmonicKeys(camelotCode) {
    if (!camelotCode) return [];
    const match = camelotCode.match(/^(\d+)([AB])$/);
    if (!match) return [
        camelotCode
    ];
    const number = parseInt(match[1]);
    const letter = match[2];
    return [
        camelotCode,
        `${number}${letter === "A" ? "B" : "A"}`,
        `${number === 12 ? 1 : number + 1}${letter}`,
        `${number === 1 ? 12 : number - 1}${letter}`
    ];
}
export function createSimilarTracksRoutes() {
    const router = Router();
    /**
   * GET /api/similar/:trackPath
   * Find tracks similar to the given track using multi-signal scoring
   */ router.get("/similar/:trackPath", (req, res)=>{
        try {
            const db = getDatabase();
            if (!db?.db) {
                return res.status(500).json({
                    error: "Database not available"
                });
            }
            const trackPath = decodeURIComponent(req.params.trackPath);
            const limit = Math.min(parseInt(req.query.limit) || 30, 100);
            // Get source track with audio analysis
            const sourceTrack = db.db.prepare(`
        SELECT t.path, t.title, t.artist, t.album, t.genre, t.duration, t.artwork_hash,
               a.bpm, a.energy, a.danceability, a.camelot_code
        FROM tracks t
        LEFT JOIN audio_analysis a ON t.path = a.track_path
        WHERE t.path = ?
      `).get(trackPath);
            if (!sourceTrack) {
                return res.status(404).json({
                    error: "Track not found"
                });
            }
            // Get candidates: exclude source track and same album
            // If source has a genre, prioritize same genre first, then expand
            let candidates;
            if (sourceTrack.genre) {
                // Try genre-filtered first for better relevance
                candidates = db.db.prepare(`
          SELECT t.path, t.title, t.artist, t.album, t.genre, t.duration, t.artwork_hash,
                 a.bpm, a.energy, a.danceability, a.camelot_code
          FROM tracks t
          LEFT JOIN audio_analysis a ON t.path = a.track_path
          WHERE t.path != ?
            AND (t.album != ? OR t.album IS NULL)
            AND t.genre = ?
          LIMIT 3000
        `).all(trackPath, sourceTrack.album, sourceTrack.genre);
                // If not enough same-genre results, expand to all
                if (candidates.length < limit * 2) {
                    const extraCandidates = db.db.prepare(`
            SELECT t.path, t.title, t.artist, t.album, t.genre, t.duration, t.artwork_hash,
                   a.bpm, a.energy, a.danceability, a.camelot_code
            FROM tracks t
            LEFT JOIN audio_analysis a ON t.path = a.track_path
            WHERE t.path != ?
              AND (t.album != ? OR t.album IS NULL)
              AND (t.genre != ? OR t.genre IS NULL)
            LIMIT 2000
          `).all(trackPath, sourceTrack.album, sourceTrack.genre);
                    candidates = candidates.concat(extraCandidates);
                }
            } else {
                candidates = db.db.prepare(`
          SELECT t.path, t.title, t.artist, t.album, t.genre, t.duration, t.artwork_hash,
                 a.bpm, a.energy, a.danceability, a.camelot_code
          FROM tracks t
          LEFT JOIN audio_analysis a ON t.path = a.track_path
          WHERE t.path != ?
            AND (t.album != ? OR t.album IS NULL)
          LIMIT 5000
        `).all(trackPath, sourceTrack.album);
            }
            // Precompute harmonic keys
            const harmonicKeys = sourceTrack.camelot_code ? getHarmonicKeys(sourceTrack.camelot_code) : [];
            // Score each candidate
            const scored = candidates.map((c)=>{
                let score = 0;
                const reasons = [];
                // Genre match (0.30)
                if (sourceTrack.genre && c.genre && sourceTrack.genre.toLowerCase() === c.genre.toLowerCase()) {
                    score += 0.30;
                    reasons.push("Same genre");
                }
                // BPM proximity (0.20) - within 10%
                if (sourceTrack.bpm && c.bpm) {
                    const bpmDiff = Math.abs(sourceTrack.bpm - c.bpm) / sourceTrack.bpm;
                    if (bpmDiff <= 0.10) {
                        score += 0.20 * (1 - bpmDiff / 0.10);
                        reasons.push("Similar tempo");
                    }
                }
                // Energy proximity (0.15) - within 0.2
                if (sourceTrack.energy != null && c.energy != null) {
                    const energyDiff = Math.abs(sourceTrack.energy - c.energy);
                    if (energyDiff <= 0.2) {
                        score += 0.15 * (1 - energyDiff / 0.2);
                        reasons.push("Similar energy");
                    }
                }
                // Key compatibility (0.15) - Camelot wheel neighbors
                if (c.camelot_code && harmonicKeys.length > 0) {
                    if (harmonicKeys.includes(c.camelot_code)) {
                        score += 0.15;
                        reasons.push("Compatible key");
                    }
                }
                // Same artist (0.10)
                if (sourceTrack.artist && c.artist && sourceTrack.artist.toLowerCase() === c.artist.toLowerCase()) {
                    score += 0.10;
                    reasons.push("Same artist");
                }
                // Danceability proximity (0.10) - within 0.2
                if (sourceTrack.danceability != null && c.danceability != null) {
                    const danceDiff = Math.abs(sourceTrack.danceability - c.danceability);
                    if (danceDiff <= 0.2) {
                        score += 0.10 * (1 - danceDiff / 0.2);
                        reasons.push("Similar feel");
                    }
                }
                return {
                    path: c.path,
                    title: c.title || "Unknown",
                    artist: c.artist || "Unknown",
                    album: c.album || "Unknown",
                    duration: c.duration || 0,
                    artwork_hash: c.artwork_hash,
                    score: Math.round(score * 100) / 100,
                    reasons
                };
            });
            // Sort by score descending, filter out zero-score, take top N
            scored.sort((a, b)=>b.score - a.score);
            const results = scored.filter((t)=>t.score > 0).slice(0, limit);
            res.json({
                source: {
                    path: sourceTrack.path,
                    title: sourceTrack.title || "Unknown",
                    artist: sourceTrack.artist || "Unknown",
                    album: sourceTrack.album || "Unknown"
                },
                similar: results,
                count: results.length
            });
        } catch (error) {
            serverLogger.error("Failed to find similar tracks:", error);
            res.status(500).json({
                error: "Failed to find similar tracks"
            });
        }
    });
    serverLogger.info("Similar tracks routes registered");
    return router;
}

//# sourceMappingURL=similarTracksRoutes.js.map