import { NextResponse } from 'next/server';
import fs from 'fs-extra';
import path from 'path';
import { execFile } from 'child_process';
import util from 'util';
import { getFlacMetadata, parseLabel } from '@/lib/utils/audio';
import { AnalysisTrack, AnalysisResponse } from '@/types';

const execFileAsync = util.promisify(execFile);

// This route analyzes the album: extracts metadata and checks quality
export async function POST(request: Request) {
    try {
        const { albumPath } = await request.json();

        if (!albumPath || !(await fs.pathExists(albumPath))) {
            return NextResponse.json({ error: 'Invalid album path' }, { status: 400 });
        }

        const files = await fs.readdir(albumPath);
        const flacFiles = files.filter((file) => file.toLowerCase().endsWith('.flac'));

        if (flacFiles.length === 0) {
            return NextResponse.json({ error: 'No FLAC files found' }, { status: 400 });
        }

        const tracks: (AnalysisTrack & { size?: number })[] = await Promise.all(
            flacFiles.map(async (file) => {
                const filePath = path.join(albumPath, file);
                try {
                    // Check for audio corruption
                    try {
                        await execFileAsync('ffmpeg', ['-v', 'error', '-i', filePath, '-f', 'null', '-']);
                    } catch (ffmpegError) {
                        return {
                            filename: file,
                            path: filePath,
                            error: 'Corrupted audio data or length metadata mismatch',
                        };
                    }

                    const stats = await fs.stat(filePath);
                    const metadata = await getFlacMetadata(filePath);
                    return {
                        filename: file,
                        path: filePath,
                        ...metadata,
                        fullPathLength: filePath.length,
                        size: stats.size,
                    };
                } catch (e) {
                    return {
                        filename: file,
                        path: filePath,
                        error: 'Failed to parse metadata',
                    };
                }
            })
        );

        // Calculate total size
        const totalSize = tracks.reduce((acc, track) => acc + (track.size || 0), 0);

        // Sort tracks by track number if available, else by filename
        tracks.sort((a, b) => {
            if ('error' in a || 'error' in b) return 0; // Keep order if error
            const trackA = a.common?.track?.no ?? 0;
            const trackB = b.common?.track?.no ?? 0;
            if (trackA && trackB) return trackA - trackB;
            return a.filename.localeCompare(b.filename);
        });

        // Analyze quality
        let isLossless = true;
        let isHiRes = true;
        let mixed = false;
        let qualityDetails = '';

        const sampleRates = new Set();
        const bitDepths = new Set();

        tracks.forEach((track) => {
            if ('error' in track) return;
            const sr = track.format?.sampleRate;
            const bd = track.format?.bitsPerSample;

            if (sr) sampleRates.add(sr);
            if (bd) bitDepths.add(bd);

            if (sr !== 44100 || bd !== 16) {
                isLossless = false; // Not strictly CD quality
            }
            if (!sr || sr < 48000 || !bd || bd < 24) {
                isHiRes = false; // Not strictly Hi-Res
            }
        });

        if (sampleRates.size > 1 || bitDepths.size > 1) {
            mixed = true;
            qualityDetails = `NOTE: Mixed formats. Sample Rates: ${Array.from(sampleRates).join(', ')} Hz. Bit Depths: ${Array.from(bitDepths).join(', ')} bit.`;

            // Generate detailed message for mixed content
            const mixedDetails = tracks.map(t => {
                if ('error' in t) return `${t.filename}: Error`;
                return `Track ${t.common?.track?.no || '?'}: ${t.format?.bitsPerSample}bit / ${t.format?.sampleRate}Hz`;
            }).join('\n');

            qualityDetails += '\n' + mixedDetails;
        }

        // Determine overall status
        let status = 'UNKNOWN';
        if (tracks.some(t => 'error' in t)) {
            status = 'CORRUPT';
        } else if (mixed) {
            status = 'MIXED';
        } else if (isHiRes) {
            status = 'HI-RES'; // 24bit LOSSLESS
        } else if (sampleRates.has(44100) && bitDepths.has(16) && sampleRates.size === 1 && bitDepths.size === 1) {
            status = 'LOSSLESS';
        } else {
            // Could be 48/16 or something else
            status = `OTHER (${Array.from(bitDepths)[0]}bit / ${Array.from(sampleRates)[0]}Hz)`;
        }

        // Extract Album Metadata (from first track)
        const firstValidTrack = tracks.find((t): t is Extract<AnalysisTrack, { error?: never }> => !('error' in t));

        let label = parseLabel(firstValidTrack?.common?.copyright || 'Unknown Label');
        if (!label) label = 'Unknown Label'; // Fallback if parsing resulted in empty string (e.g. Records DK)

        // Additional artists: filter out album artist from artists list
        const albumArtist = firstValidTrack?.common?.albumartist || firstValidTrack?.common?.artist || '';
        // Collect artists from ALL tracks, not just the first one
        const allArtistsSet = new Set<string>();
        tracks.forEach(t => {
            if ('error' in t) return;
            const trackArtists = t.common?.artists || [];
            trackArtists.forEach(artist => {
                // Split by common delimiters to handle combined strings like "A, B & C"
                const parts = artist.split(/,\s+|&|\s+\/\s+/).map(a => a.trim()).filter(a => a.length > 0);
                parts.forEach(p => allArtistsSet.add(p));
            });
        });
        const allArtists = Array.from(allArtistsSet);

        // Identify all main artists (Album Artist + Track Artist) individually
        const mainArtistNames = new Set(
            [albumArtist, firstValidTrack?.common?.artist || '']
                .flatMap(name => name.split(/,\s+|&|\s+\/\s+/)) // Split by comma, &, /
                .map(a => a.trim())
                .filter(a => a.length > 0)
        );

        const additionalArtists = allArtists.filter(a => !mainArtistNames.has(a));

        const albumMetadata = {
            artist: firstValidTrack?.common?.artist || 'Unknown Artist',
            album: firstValidTrack?.common?.album || 'Unknown Album',
            year: firstValidTrack?.common?.year || (firstValidTrack?.common?.date ? parseInt(firstValidTrack.common.date.split('-')[0]) : undefined),
            upc: firstValidTrack?.common?.barcode,
            label: label,
            tidalUrl: firstValidTrack?.native?.tidalUrl,
            additionalArtists: additionalArtists.length > 0 ? additionalArtists : undefined,
            releaseType: (() => {
                const name = firstValidTrack?.common?.album?.toLowerCase() || '';
                if (name.includes('live')) return 11; // Live album
                if (name.includes('soundtrack') || name.includes('ost')) return 3; // Soundtrack

                const validTracks = tracks.filter((t) => !('error' in t)).length;
                if (validTracks === 1) return 9; // Single
                if (validTracks <= 5) return 5; // EP

                return 1; // Album
            })(),
            totalSize
        };

        return NextResponse.json({
            albumPath,
            metadata: albumMetadata,
            tracks,
            quality: {
                status,
                mixed,
                details: qualityDetails,
            },
        });
    } catch (error) {
        console.error('Error analyzing album:', error);
        return NextResponse.json({ error: 'Failed to analyze album' }, { status: 500 });
    }
}
