import SwiftData
import SwiftUI
import UniformTypeIdentifiers

/// Detail view for a single playlist — shows tracks with reorder, play, remove.
struct PlaylistDetailView: View {
    let playlist: Playlist
    @Environment(PlayerViewModel.self) private var playerVM
    @Environment(PlaylistViewModel.self) private var playlistVM
    @EnvironmentObject private var libraryManager: LibraryManager
    @EnvironmentObject private var theme: AppTheme
    @EnvironmentObject private var syncManager: SyncManager
    @Environment(\.modelContext) private var modelContext

    @AppStorage("trackTapAction") private var trackTapAction = "playNow"

    @State private var showAddTracks = false
    @State private var showEntryNotes: PlaylistEntry?
    @State private var showGroupEditor = false
    @State private var isEditing = false

    var body: some View {
        List {
            // Header stats
            playlistHeader

            // Track entries — grouped if template is set
            if playlist.groupTemplate.isEmpty {
                // No grouping — flat list
                flatEntryList
            } else {
                // Grouped by template
                groupedEntryList
            }
        }
        .listStyle(.plain)
        .navigationTitle(playlist.name)
        .accessibilityIdentifier("playlistDetailView")
        .toolbar {
            ToolbarItem(placement: .topBarTrailing) {
                EditButton()
            }

            ToolbarItem(placement: .topBarTrailing) {
                Menu {
                    Button {
                        showAddTracks = true
                    } label: {
                        Label("Add Tracks", systemImage: "plus")
                    }

                    Button {
                        playlistVM.targetPlaylist = playlist
                        playlistVM.showStatus("Target: \(playlist.name)")
                    } label: {
                        Label("Set as Target", systemImage: "star.fill")
                    }

                    Divider()

                    Button {
                        showGroupEditor = true
                    } label: {
                        Label(
                            playlist.groupTemplate.isEmpty ? "Grouping..." : "Grouping: \(playlist.groupTemplate)",
                            systemImage: "rectangle.3.group"
                        )
                    }

                    Divider()

                    Button {
                        syncManager.exportPlaylists([playlist])
                        playlistVM.showStatus("Playlist exported to Sync folder")
                    } label: {
                        Label("Export for Mac", systemImage: "square.and.arrow.up")
                    }

                    // Play all
                    if let firstEntry = playlist.sortedEntries.first,
                       let track = firstEntry.track {
                        Button {
                            playerVM.playFromPlaylist(track: track, entryID: firstEntry.id, playlist: playlist)
                        } label: {
                            Label("Play All", systemImage: "play.fill")
                        }
                    }
                } label: {
                    Image(systemName: "ellipsis.circle")
                }
            }
        }
        .sheet(isPresented: $showAddTracks) {
            AddTracksToPlaylistSheet(playlist: playlist)
                .environmentObject(theme)
        }
        .sheet(item: $showEntryNotes) { entry in
            EntryNotesSheet(entry: entry)
                .environmentObject(theme)
        }
        .sheet(isPresented: $showGroupEditor) {
            GroupTemplateEditorSheet(playlist: playlist)
                .environmentObject(theme)
        }
    }

    // MARK: - Header

    private var playlistHeader: some View {
        VStack(alignment: .leading, spacing: 8) {
            HStack {
                Circle()
                    .fill(Color(hex: playlist.color) ?? theme.accent)
                    .frame(width: 16, height: 16)

                Text("\(playlist.trackCount) tracks")
                    .font(.subheadline)
                    .foregroundStyle(theme.secondaryText)

                Text("•")
                    .foregroundStyle(theme.tertiaryText)

                Text(playlist.formattedTotalDuration)
                    .font(.subheadline.monospacedDigit())
                    .foregroundStyle(theme.secondaryText)

                if let bpm = playlist.targetBPM {
                    Text("•")
                        .foregroundStyle(theme.tertiaryText)
                    Text("\(String(format: "%.0f", bpm)) BPM")
                        .font(.subheadline.monospacedDigit())
                        .foregroundStyle(theme.tertiaryText)
                }
            }

            if !playlist.notes.isEmpty {
                Text(playlist.notes)
                    .font(.caption)
                    .foregroundStyle(theme.tertiaryText)
            }
        }
        .padding(.vertical, 4)
        .listRowBackground(Color.clear)
    }

    // MARK: - Flat Entry List (no grouping)

    private var flatEntryList: some View {
        ForEach(playlist.sortedEntries) { entry in
            entryRow(entry)
        }
        .onMove { source, destination in
            if let first = source.first {
                playlistVM.moveEntry(in: playlist, from: first, to: destination, context: modelContext)
            }
        }
        .onDelete { offsets in
            let entries = playlist.sortedEntries
            for index in offsets {
                playlistVM.removeEntry(entries[index], from: playlist, context: modelContext)
            }
        }
    }

    // MARK: - Grouped Entry List (by template)

    private var groupedEntryList: some View {
        let sorted = playlist.sortedEntries
        let groups = groupEntries(sorted, template: playlist.groupTemplate)

        return ForEach(groups, id: \.header) { group in
            Section {
                ForEach(group.entries) { entry in
                    entryRow(entry)
                }
            } header: {
                Text(group.header)
                    .font(.subheadline.weight(.semibold))
                    .foregroundStyle(theme.groupHeaderText)
            }
        }
    }

    private struct EntryGroup {
        let header: String
        let entries: [PlaylistEntry]
    }

    private func groupEntries(_ entries: [PlaylistEntry], template: String) -> [EntryGroup] {
        var groups: [(String, [PlaylistEntry])] = []
        var currentHeader = ""
        var currentEntries: [PlaylistEntry] = []

        for entry in entries {
            let header: String
            if let track = entry.track {
                header = GroupTemplateResolver.resolve(template: template, for: track)
            } else {
                header = "Unknown"
            }

            if header != currentHeader {
                if !currentEntries.isEmpty {
                    groups.append((currentHeader, currentEntries))
                }
                currentHeader = header
                currentEntries = [entry]
            } else {
                currentEntries.append(entry)
            }
        }
        if !currentEntries.isEmpty {
            groups.append((currentHeader, currentEntries))
        }

        return groups.map { EntryGroup(header: $0.0, entries: $0.1) }
    }

    // MARK: - Entry Row

    @ViewBuilder
    private func entryRow(_ entry: PlaylistEntry) -> some View {
        if let track = entry.track {
            Button {
                if trackTapAction == "addToQueue" {
                    playerVM.addToQueue(QueueEntry.from(track: track))
                } else {
                    playerVM.playFromPlaylist(track: track, entryID: entry.id, playlist: playlist)
                }
            } label: {
                PlaylistEntryRow(entry: entry, track: track)
                    .contentShape(Rectangle())
            }
            .buttonStyle(.plain)
                .contextMenu {
                    Button {
                        playerVM.playFromPlaylist(track: track, entryID: entry.id, playlist: playlist)
                    } label: {
                        Label("Play Now", systemImage: "play.fill")
                    }
                    Button {
                        playerVM.playNextInQueue(QueueEntry.from(track: track))
                    } label: {
                        Label("Play Next", systemImage: "text.insert")
                    }
                    Button {
                        playerVM.addToQueue(QueueEntry.from(track: track))
                    } label: {
                        Label("Add to Queue", systemImage: "text.append")
                    }
                }
                .swipeActions(edge: .trailing) {
                    Button(role: .destructive) {
                        playlistVM.removeEntry(entry, from: playlist, context: modelContext)
                    } label: {
                        Label("Remove", systemImage: "minus.circle")
                    }
                }
                .swipeActions(edge: .leading) {
                    Button {
                        showEntryNotes = entry
                    } label: {
                        Label("Notes", systemImage: "note.text")
                    }
                    .tint(theme.accent)
                }
        } else {
            HStack {
                Image(systemName: "exclamationmark.triangle")
                    .foregroundStyle(.orange)
                Text(entry.notes.isEmpty ? "Track not found" : entry.notes)
                    .font(.subheadline)
                    .foregroundStyle(theme.secondaryText)
            }
        }
    }
}

// MARK: - Playlist Entry Row

struct PlaylistEntryRow: View {
    let entry: PlaylistEntry
    let track: Track
    @Environment(PlayerViewModel.self) private var playerVM
    @EnvironmentObject private var theme: AppTheme

    private var isPlaying: Bool {
        playerVM.currentPlayingEntryID == entry.id
    }

    var body: some View {
        HStack(spacing: 12) {
            // Position number
            Text("\(entry.position + 1)")
                .font(.system(size: 14, design: .monospaced))
                .foregroundStyle(isPlaying ? theme.playingHighlight : theme.tertiaryText)
                .frame(width: 24)

            // Track info
            VStack(alignment: .leading, spacing: 2) {
                Text(track.title)
                    .font(.system(size: theme.dataFontSize, weight: isPlaying ? .semibold : .regular))
                    .foregroundStyle(isPlaying ? theme.playingHighlight : theme.primaryText)
                    .lineLimit(1)

                HStack(spacing: 6) {
                    if !track.artist.isEmpty {
                        Text(track.artist)
                            .font(.system(size: theme.smallFontSize))
                            .foregroundStyle(theme.secondaryText)
                            .lineLimit(1)
                    }

                    if entry.crossfadeDuration > 0 {
                        Text("⤬ \(String(format: "%.1fs", entry.crossfadeDuration))")
                            .font(.system(size: 10, design: .monospaced))
                            .foregroundStyle(theme.tertiaryText)
                    }
                }
            }

            Spacer()

            VStack(alignment: .trailing, spacing: 2) {
                Text(track.formattedDuration)
                    .font(.system(size: theme.smallFontSize, design: .monospaced))
                    .foregroundStyle(theme.secondaryText)

                if let bpm = track.bpm {
                    Text("\(String(format: "%.0f", bpm))")
                        .font(.system(size: 10, design: .monospaced))
                        .foregroundStyle(theme.tertiaryText)
                }
            }

            if isPlaying && playerVM.isPlaying {
                Image(systemName: "speaker.wave.2.fill")
                    .font(.caption)
                    .foregroundStyle(theme.playingHighlight)
            }
        }
        .padding(.vertical, 4)
    }
}

// MARK: - Add Tracks to Playlist Sheet

struct AddTracksToPlaylistSheet: View {
    let playlist: Playlist
    @Environment(PlaylistViewModel.self) private var playlistVM
    @EnvironmentObject private var theme: AppTheme
    @Environment(\.modelContext) private var modelContext
    @Environment(\.dismiss) private var dismiss

    @Query(sort: \Track.title) private var allTracks: [Track]
    @State private var searchText = ""
    @State private var selectedTracks: Set<UUID> = []
    @State private var existingTrackIDs: Set<UUID> = []

    private var filteredTracks: [Track] {
        if searchText.isEmpty { return allTracks }
        let q = searchText.lowercased()
        return allTracks.filter {
            $0.title.lowercased().contains(q) ||
            $0.artist.lowercased().contains(q)
        }
    }

    var body: some View {
        NavigationStack {
            List(filteredTracks) { track in
                let isInPlaylist = existingTrackIDs.contains(track.id)
                let isSelected = selectedTracks.contains(track.id)

                HStack {
                    TrackRow(track: track)

                    Spacer()

                    if isInPlaylist {
                        Image(systemName: "checkmark.circle.fill")
                            .foregroundStyle(theme.tertiaryText)
                    } else if isSelected {
                        Image(systemName: "checkmark.circle.fill")
                            .foregroundStyle(theme.accent)
                    } else {
                        Image(systemName: "circle")
                            .foregroundStyle(theme.tertiaryText)
                    }
                }
                .contentShape(Rectangle())
                .onTapGesture {
                    guard !isInPlaylist else { return }
                    if isSelected {
                        selectedTracks.remove(track.id)
                    } else {
                        selectedTracks.insert(track.id)
                    }
                }
                .opacity(isInPlaylist ? 0.5 : 1)
            }
            .searchable(text: $searchText, prompt: "Search library")
            .navigationTitle("Add Tracks")
            .navigationBarTitleDisplayMode(.inline)
            .onAppear {
                // Pre-compute existing track IDs once — avoids per-row database queries
                let playlistID = playlist.id
                let descriptor = FetchDescriptor<PlaylistEntry>(
                    predicate: #Predicate<PlaylistEntry> { $0.playlist?.id == playlistID }
                )
                if let entries = try? modelContext.fetch(descriptor) {
                    existingTrackIDs = Set(entries.compactMap { $0.track?.id })
                }
            }
            .toolbar {
                ToolbarItem(placement: .cancellationAction) {
                    Button("Cancel") { dismiss() }
                }
                ToolbarItem(placement: .confirmationAction) {
                    Button("Add \(selectedTracks.count)") {
                        let tracksToAdd = allTracks.filter { selectedTracks.contains($0.id) }
                        playlistVM.addTracks(tracksToAdd, to: playlist, context: modelContext)
                        dismiss()
                    }
                    .disabled(selectedTracks.isEmpty)
                }
            }
        }
    }
}

// MARK: - Entry Notes Sheet

struct EntryNotesSheet: View {
    let entry: PlaylistEntry
    @EnvironmentObject private var theme: AppTheme
    @Environment(\.modelContext) private var modelContext
    @Environment(\.dismiss) private var dismiss
    @State private var notes: String = ""

    var body: some View {
        NavigationStack {
            VStack(alignment: .leading, spacing: 12) {
                if let track = entry.track {
                    Text("\(track.artist) — \(track.title)")
                        .font(.headline)
                        .foregroundStyle(theme.primaryText)
                }

                TextEditor(text: $notes)
                    .font(.body)
                    .frame(minHeight: 200)
                    .overlay(
                        RoundedRectangle(cornerRadius: 8)
                            .stroke(theme.separatorColor, lineWidth: 1)
                    )

                Spacer()
            }
            .padding()
            .navigationTitle("Notes")
            .navigationBarTitleDisplayMode(.inline)
            .toolbar {
                ToolbarItem(placement: .cancellationAction) {
                    Button("Cancel") { dismiss() }
                }
                ToolbarItem(placement: .confirmationAction) {
                    Button("Save") {
                        entry.notes = notes
                        try? modelContext.save()
                        dismiss()
                    }
                }
            }
            .onAppear {
                notes = entry.notes
            }
        }
    }
}
