import { NextRequest, NextResponse } from 'next/server';
import { PrismaClient } from '@prisma/client';
import { requireStaffAuth, StaffContext } from '@/lib/auth';

const prisma = new PrismaClient();

async function getCuratorAssignments(curatorId: number) {
    const [genres, specials] = await Promise.all([
        prisma.curatorGenre.findMany({
            where: { curatorId },
            select: { genreId: true },
        }),
        prisma.curatorSpecial.findMany({
            where: { curatorId },
            select: { specialId: true },
        }),
    ]);

    return {
        genreIds: new Set(genres.map(g => g.genreId)),
        specialIds: new Set(specials.map(s => s.specialId)),
    };
}

function curatorCanEditSong(context: StaffContext, song: any, assignments: { genreIds: Set<number>; specialIds: Set<number> }) {
    if (context.role === 'admin') return true;

    const songGenreIds = (song.genres || []).map((g: any) => g.id);
    const songSpecialIds = (song.specials || [])
        .map((s: any) => {
            if (s?.specialId != null) return s.specialId;
            if (s?.special?.id != null) return s.special.id;
            if (s?.id != null && s?.specialId == null && s?.special == null) return s.id;
            return undefined;
        })
        .filter((id: any): id is number => typeof id === 'number');

    if (songGenreIds.length === 0 && songSpecialIds.length === 0) {
        return true;
    }

    const hasGenre = songGenreIds.some((id: number) => assignments.genreIds.has(id));
    const hasSpecial = songSpecialIds.some((id: number) => assignments.specialIds.has(id));

    return hasGenre || hasSpecial;
}

export async function POST(request: Request) {
    const { error, context } = await requireStaffAuth(request as unknown as NextRequest);
    if (error || !context) return error!;

    try {
        const { songIds, genreToggleIds, specialToggleIds, artist, excludeFromGlobal } = await request.json();

        if (!songIds || !Array.isArray(songIds) || songIds.length === 0) {
            return NextResponse.json({ error: 'Missing or invalid songIds array' }, { status: 400 });
        }

        // Validate that at least one operation is requested
        const hasGenreToggle = genreToggleIds && Array.isArray(genreToggleIds) && genreToggleIds.length > 0;
        const hasSpecialToggle = specialToggleIds && Array.isArray(specialToggleIds) && specialToggleIds.length > 0;
        const hasArtistChange = artist !== undefined && artist !== null && artist.trim() !== '';
        const hasExcludeGlobalChange = excludeFromGlobal !== undefined;

        if (!hasGenreToggle && !hasSpecialToggle && !hasArtistChange && !hasExcludeGlobalChange) {
            return NextResponse.json({ error: 'No update operations specified' }, { status: 400 });
        }

        // Validate artist if provided
        if (hasArtistChange && artist.trim() === '') {
            return NextResponse.json({ error: 'Artist cannot be empty' }, { status: 400 });
        }

        // Validate excludeFromGlobal permission
        if (hasExcludeGlobalChange && context.role === 'curator' && !context.curator.isGlobalCurator) {
            return NextResponse.json(
                { error: 'Forbidden: Only global curators or admins can change global playlist flag' },
                { status: 403 }
            );
        }

        let assignments: { genreIds: Set<number>; specialIds: Set<number> } | null = null;
        if (context.role === 'curator') {
            const curatorAssignments = await getCuratorAssignments(context.curator.id);
            assignments = curatorAssignments;

            // Validate genre/special toggles are within curator's assignments
            if (hasGenreToggle) {
                const invalidGenre = genreToggleIds.some((id: number) => !curatorAssignments.genreIds.has(id));
                if (invalidGenre) {
                    return NextResponse.json(
                        { error: 'Curators may only toggle their own genres' },
                        { status: 403 }
                    );
                }
            }

            if (hasSpecialToggle) {
                const invalidSpecial = specialToggleIds.some((id: number) => !curatorAssignments.specialIds.has(id));
                if (invalidSpecial) {
                    return NextResponse.json(
                        { error: 'Curators may only toggle their own specials' },
                        { status: 403 }
                    );
                }
            }
        }

        // Load all songs with relations for permission checks
        const songs = await prisma.song.findMany({
            where: { id: { in: songIds.map((id: any) => Number(id)) } },
            include: {
                genres: true,
                specials: {
                    include: {
                        special: true
                    }
                },
            },
        });

        if (songs.length === 0) {
            return NextResponse.json({ error: 'No songs found' }, { status: 404 });
        }

        // Filter songs that can be edited
        const editableSongs = context.role === 'admin'
            ? songs
            : songs.filter(song => curatorCanEditSong(context, song, assignments!));

        if (editableSongs.length === 0) {
            return NextResponse.json(
                { error: 'No songs can be edited with current permissions' },
                { status: 403 }
            );
        }

        const results = {
            total: songIds.length,
            processed: editableSongs.length,
            skipped: songs.length - editableSongs.length,
            success: 0,
            errors: [] as Array<{ songId: number; error: string }>,
        };

        // Process each song in a transaction
        for (const song of editableSongs) {
            try {
                await prisma.$transaction(async (tx) => {
                    const updateData: any = {};

                    // Handle artist change
                    if (hasArtistChange) {
                        updateData.artist = artist.trim();
                    }

                    // Handle excludeFromGlobal change
                    if (hasExcludeGlobalChange) {
                        updateData.excludeFromGlobal = excludeFromGlobal;
                    }

                    // Handle genre toggles
                    if (hasGenreToggle) {
                        const currentGenreIds = song.genres.map(g => g.id);
                        const genreIdsToToggle = genreToggleIds as number[];

                        // Determine which genres to add/remove
                        const genresToAdd = genreIdsToToggle.filter(id => !currentGenreIds.includes(id));
                        const genresToRemove = genreIdsToToggle.filter(id => currentGenreIds.includes(id));

                        // For curators, preserve genres they can't manage
                        let finalGenreIds: number[];
                        if (context.role === 'curator') {
                            const fixedGenreIds = currentGenreIds.filter(gid => !assignments!.genreIds.has(gid));
                            const managedGenreIds = currentGenreIds
                                .filter(gid => assignments!.genreIds.has(gid) && !genresToRemove.includes(gid))
                                .concat(genresToAdd);
                            finalGenreIds = Array.from(new Set([...fixedGenreIds, ...managedGenreIds]));
                        } else {
                            const newGenreIds = currentGenreIds
                                .filter(id => !genresToRemove.includes(id))
                                .concat(genresToAdd);
                            finalGenreIds = Array.from(new Set(newGenreIds));
                        }

                        updateData.genres = {
                            set: finalGenreIds.map(gId => ({ id: gId }))
                        };
                    }

                    // Update song basic data
                    if (Object.keys(updateData).length > 0) {
                        await tx.song.update({
                            where: { id: song.id },
                            data: updateData,
                        });
                    }

                    // Handle special toggles
                    if (hasSpecialToggle) {
                        const currentSpecials = await tx.specialSong.findMany({
                            where: { songId: song.id }
                        });
                        const currentSpecialIds = currentSpecials.map(ss => ss.specialId);
                        const specialIdsToToggle = specialToggleIds as number[];

                        // Determine which specials to add/remove
                        const specialsToAdd = specialIdsToToggle.filter(id => !currentSpecialIds.includes(id));
                        const specialsToRemove = specialIdsToToggle.filter(id => currentSpecialIds.includes(id));

                        // For curators, preserve specials they can't manage
                        let finalSpecialIds: number[];
                        if (context.role === 'curator') {
                            const fixedSpecialIds = currentSpecialIds.filter(sid => !assignments!.specialIds.has(sid));
                            const managedSpecialIds = currentSpecialIds
                                .filter(sid => assignments!.specialIds.has(sid) && !specialsToRemove.includes(sid))
                                .concat(specialsToAdd);
                            finalSpecialIds = Array.from(new Set([...fixedSpecialIds, ...managedSpecialIds]));
                        } else {
                            const newSpecialIds = currentSpecialIds
                                .filter(id => !specialsToRemove.includes(id))
                                .concat(specialsToAdd);
                            finalSpecialIds = Array.from(new Set(newSpecialIds));
                        }

                        // Delete removed specials
                        const toDelete = currentSpecialIds.filter(sid => !finalSpecialIds.includes(sid));
                        if (toDelete.length > 0) {
                            await tx.specialSong.deleteMany({
                                where: {
                                    songId: song.id,
                                    specialId: { in: toDelete }
                                }
                            });
                        }

                        // Add new specials
                        const toAdd = finalSpecialIds.filter(sid => !currentSpecialIds.includes(sid));
                        if (toAdd.length > 0) {
                            await tx.specialSong.createMany({
                                data: toAdd.map(specialId => ({
                                    songId: song.id,
                                    specialId,
                                    startTime: 0
                                }))
                            });
                        }
                    }
                });

                results.success++;
            } catch (error: any) {
                results.errors.push({
                    songId: song.id,
                    error: error.message || 'Unknown error'
                });
            }
        }

        return NextResponse.json(results);
    } catch (error) {
        console.error('Error in batch update:', error);
        return NextResponse.json({ error: 'Internal Server Error' }, { status: 500 });
    }
}

