import SwiftData
import SwiftUI

/// Folder browser — drill down into the Documents directory tree.
/// Supports recursive add-to-playlist at any folder level.
struct FolderBrowserView: View {
    let folderURL: URL
    let title: String

    @Environment(PlayerViewModel.self) private var playerVM
    @Environment(PlaylistViewModel.self) private var playlistVM
    @EnvironmentObject private var theme: AppTheme
    @EnvironmentObject private var libraryManager: LibraryManager
    @Environment(\.modelContext) private var modelContext

    @Query private var allTracks: [Track]

    @State private var subfolders: [URL] = []
    @State private var audioFiles: [URL] = []
    @State private var showAddGroupToPlaylist: [Track]?
    @State private var cachedAllTracks: [Track]?

    private static let mixColors: [Color] = [
        Color(red: 0.95, green: 0.3, blue: 0.3),
        Color(red: 0.3, green: 0.75, blue: 0.95),
        Color(red: 0.95, green: 0.75, blue: 0.2),
    ]

    var body: some View {
        List {
            // Action row — show if there are tracks in this folder tree
            if let allTrx = cachedAllTracks, !allTrx.isEmpty {
                folderActionRow(allTrx)
            }

            // Subfolders
            if !subfolders.isEmpty {
                Section("Folders") {
                    ForEach(subfolders, id: \.path) { folder in
                        NavigationLink {
                            FolderBrowserView(folderURL: folder, title: folder.lastPathComponent)
                        } label: {
                            HStack(spacing: 12) {
                                folderArtwork(folder)
                                    .frame(width: 40, height: 40)

                                Text(folder.lastPathComponent)
                                    .foregroundStyle(theme.primaryText)
                                    .lineLimit(2)
                            }
                        }
                        .contextMenu {
                            let tracks = allTracksRecursive(in: folder)
                            if !tracks.isEmpty {
                                Button {
                                    if let first = tracks.first {
                                        playerVM.loadAndPlay(first)
                                    }
                                } label: {
                                    Label("Play All (\(tracks.count))", systemImage: "play.fill")
                                }

                                Divider()

                                ForEach(0..<3, id: \.self) { slot in
                                    if playlistVM.mixTargets[slot] != nil {
                                        Button {
                                            for track in tracks {
                                                _ = playlistVM.quickAddToMix(slot: slot, track: track, context: modelContext)
                                            }
                                            playlistVM.showStatus("Added \(tracks.count) to \(playlistVM.mixTargetName(slot))")
                                        } label: {
                                            Label("Add all to \(playlistVM.mixTargetName(slot))", systemImage: "\(slot + 1).circle.fill")
                                        }
                                    }
                                }

                                Divider()

                                Button {
                                    showAddGroupToPlaylist = tracks
                                } label: {
                                    Label("Add all to Playlist...", systemImage: "plus.circle")
                                }
                            }
                        }
                    }
                }
            }

            // Audio files in this folder
            if !audioFiles.isEmpty {
                Section("Audio files") {
                    ForEach(audioFiles, id: \.path) { fileURL in
                        if let track = trackForFile(fileURL) {
                            Button {
                                playerVM.loadAndPlay(track)
                                playerVM.showNowPlaying = true
                            } label: {
                                TrackRow(track: track)
                                    .contentShape(Rectangle())
                            }
                            .buttonStyle(.plain)
                            .contextMenu {
                                Button {
                                    playerVM.loadAndPlay(track)
                                    playerVM.showNowPlaying = true
                                } label: {
                                    Label("Play", systemImage: "play.fill")
                                }

                                Divider()

                                ForEach(0..<3, id: \.self) { slot in
                                    if playlistVM.mixTargets[slot] != nil {
                                        Button {
                                            _ = playlistVM.quickAddToMix(slot: slot, track: track, context: modelContext)
                                        } label: {
                                            Label("Add to \(playlistVM.mixTargetName(slot))", systemImage: "\(slot + 1).circle.fill")
                                        }
                                    }
                                }

                                Divider()

                                Button {
                                    showAddGroupToPlaylist = [track]
                                } label: {
                                    Label("Add to Playlist...", systemImage: "plus.circle")
                                }
                            }
                        } else {
                            // File not yet imported
                            HStack {
                                Image(systemName: "music.note")
                                    .foregroundStyle(theme.tertiaryText)
                                    .frame(width: 40, height: 40)
                                VStack(alignment: .leading) {
                                    Text(fileURL.lastPathComponent)
                                        .foregroundStyle(theme.primaryText)
                                        .lineLimit(1)
                                    Text("Not imported")
                                        .font(.caption)
                                        .foregroundStyle(theme.tertiaryText)
                                }
                                Spacer()
                                let size = (try? FileManager.default.attributesOfItem(atPath: fileURL.path)[.size] as? Int64) ?? 0
                                Text(ByteCountFormatter.string(fromByteCount: size, countStyle: .file))
                                    .font(.caption)
                                    .foregroundStyle(theme.tertiaryText)
                            }
                        }
                    }
                }
            }

            if subfolders.isEmpty && audioFiles.isEmpty {
                Text("Empty folder")
                    .foregroundStyle(theme.tertiaryText)
                    .frame(maxWidth: .infinity, alignment: .center)
            }
        }
        .listStyle(.plain)
        .navigationTitle(title)
        .navigationBarTitleDisplayMode(.inline)
        .onAppear {
            scanFolder()
            // Compute recursive tracks once, not on every render
            cachedAllTracks = allTracksRecursive(in: folderURL)
        }
        .sheet(isPresented: Binding(
            get: { showAddGroupToPlaylist != nil },
            set: { if !$0 { showAddGroupToPlaylist = nil } }
        )) {
            if let tracks = showAddGroupToPlaylist {
                AddGroupToPlaylistSheet(tracks: tracks)
                    .environmentObject(theme)
            }
        }
    }

    // MARK: - Folder scanning

    private func scanFolder() {
        let fm = FileManager.default
        guard let contents = try? fm.contentsOfDirectory(
            at: folderURL,
            includingPropertiesForKeys: [.isDirectoryKey],
            options: [.skipsHiddenFiles]
        ) else { return }

        var folders: [URL] = []
        var files: [URL] = []

        for url in contents.sorted(by: { $0.lastPathComponent.compare($1.lastPathComponent, options: [.numeric, .caseInsensitive]) == .orderedAscending }) {
            var isDir: ObjCBool = false
            if fm.fileExists(atPath: url.path, isDirectory: &isDir) {
                if isDir.boolValue {
                    folders.append(url)
                } else if MetadataService.isSupportedAudioFile(url) {
                    files.append(url)
                }
            }
        }

        subfolders = folders
        audioFiles = files
    }

    // MARK: - Track matching

    /// Find the Track model for a file URL by matching the relative path.
    private func trackForFile(_ url: URL) -> Track? {
        let docsDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
        let standardized = url.standardizedFileURL.path
        let docsPath = docsDir.standardizedFileURL.path
        let relativePath = standardized.hasPrefix(docsPath + "/")
            ? String(standardized.dropFirst(docsPath.count + 1))
            : url.lastPathComponent
        // Try matching by full path first, fallback to fileName
        return allTracks.first { $0.filePath == relativePath }
            ?? allTracks.first { $0.fileName == url.lastPathComponent }
    }

    /// All tracks in this folder (not recursive).
    private var tracksInThisFolder: [Track] {
        audioFiles.compactMap { trackForFile($0) }
    }

    /// All tracks recursively in a folder and its subfolders.
    private func allTracksRecursive(in folder: URL) -> [Track] {
        let fm = FileManager.default
        var result: [Track] = []
        var notFound: [String] = []

        guard let enumerator = fm.enumerator(
            at: folder,
            includingPropertiesForKeys: [.isRegularFileKey],
            options: [.skipsHiddenFiles]
        ) else { return result }

        for case let fileURL as URL in enumerator {
            if MetadataService.isSupportedAudioFile(fileURL) {
                if let track = trackForFile(fileURL) {
                    result.append(track)
                } else {
                    notFound.append(fileURL.lastPathComponent)
                }
            }
        }

        if !notFound.isEmpty {
            print("allTracksRecursive: \(notFound.count) files not in DB")
        }

        // Sort by full path with numeric sorting so "12.1.1" < "12.1.2" < "12.1.10"
        result.sort { $0.filePath.compare($1.filePath, options: [.numeric, .caseInsensitive]) == .orderedAscending }

        return result
    }

    // MARK: - Folder artwork (first image in folder or first track's art)

    private func folderArtwork(_ folder: URL) -> some View {
        let fm = FileManager.default
        let imageExts: Set<String> = ["jpg", "jpeg", "png"]

        // Look for cover image in the folder
        var imageData: Data?
        if let contents = try? fm.contentsOfDirectory(at: folder, includingPropertiesForKeys: nil, options: .skipsHiddenFiles) {
            for file in contents {
                if imageExts.contains(file.pathExtension.lowercased()) {
                    imageData = try? Data(contentsOf: file)
                    break
                }
            }
        }

        return Group {
            if let data = imageData, let img = UIImage(data: data) {
                Image(uiImage: img)
                    .resizable()
                    .aspectRatio(contentMode: .fill)
                    .clipShape(RoundedRectangle(cornerRadius: 6))
            } else {
                ZStack {
                    RoundedRectangle(cornerRadius: 6)
                        .fill(theme.cardBackground)
                    Image(systemName: "folder.fill")
                        .foregroundStyle(theme.tertiaryText)
                }
            }
        }
    }

    // MARK: - Folder action row

    private func folderActionRow(_ tracks: [Track]) -> some View {
        HStack(spacing: 12) {
            Button {
                if let first = tracks.first {
                    playerVM.loadAndPlay(first)
                }
            } label: {
                Label("Play", systemImage: "play.fill")
                    .font(.caption)
                    .foregroundStyle(theme.accent)
            }
            .buttonStyle(.plain)

            Divider().frame(height: 16)

            ForEach(0..<3, id: \.self) { slot in
                if playlistVM.mixTargets[slot] != nil {
                    Button {
                        for track in tracks {
                            _ = playlistVM.quickAddToMix(slot: slot, track: track, context: modelContext)
                        }
                        playlistVM.showStatus("Added \(tracks.count) to \(playlistVM.mixTargetName(slot))")
                    } label: {
                        Text("\(slot + 1)")
                            .font(.system(size: 11, weight: .bold, design: .rounded))
                            .frame(width: 24, height: 24)
                            .foregroundStyle(Self.mixColors[slot])
                            .background(Self.mixColors[slot].opacity(0.15))
                            .clipShape(RoundedRectangle(cornerRadius: 5))
                    }
                    .buttonStyle(.plain)
                }
            }

            Divider().frame(height: 16)

            Button {
                showAddGroupToPlaylist = tracks
            } label: {
                Image(systemName: "plus.circle")
                    .font(.system(size: 16))
                    .foregroundStyle(theme.secondaryText)
            }
            .buttonStyle(.plain)

            Spacer()

            Text("\(tracks.count) tracks")
                .font(.caption)
                .foregroundStyle(theme.tertiaryText)
        }
        .padding(.vertical, 4)
        .listRowBackground(theme.cardBackground.opacity(0.3))
    }
}
