import AppKit
import SwiftUI

struct ContentView: View {
    var body: some View {
        TabView {
            ConversionToolView()
                .tabItem {
                    Label("Datenumwandlung", systemImage: "arrow.triangle.2.circlepath")
                }

            SDCardToolView()
                .tabItem {
                    Label("SD-Karte", systemImage: "sdcard")
                }

            MP3GainToolView()
                .tabItem {
                    Label("MP3Gain", systemImage: "waveform")
                }

            ID3TagToolView()
                .tabItem {
                    Label("ID3-Tags", systemImage: "tag")
                }
        }
    }
}

private struct ConversionToolView: View {
    @State private var inputURL: URL?
    @State private var outputURL: URL?
    @State private var episodes: [EpisodePlan] = []
    @State private var messages: [ExportMessage] = []
    @State private var overwriteExisting = false
    @State private var writeReport = true
    @State private var cleanAfterExport = true
    @State private var validateAfterExport = true
    @State private var trackMode: TonUinoExportOptions.TrackMode = .merged
    @State private var isExporting = false

    private var canExport: Bool {
        inputURL != nil && outputURL != nil && !episodes.isEmpty && !isExporting
    }

    var body: some View {
        VStack(spacing: 0) {
            toolbar

            Divider()

            HSplitView {
                VStack(alignment: .leading, spacing: 12) {
                    header
                    episodeTable
                }
                .frame(minWidth: 520)

                VStack(alignment: .leading, spacing: 12) {
                    Text("Protokoll")
                        .font(.headline)
                    messageList
                }
                .frame(minWidth: 280)
                .padding(16)
            }
        }
        .onChange(of: inputURL) { _ in reloadPreview() }
        .onAppear(perform: reloadPreview)
    }

    private var toolbar: some View {
        VStack(alignment: .leading, spacing: 10) {
            HStack(spacing: 10) {
                Button {
                    chooseFolder(title: "Input-Ordner wählen") { url in
                        inputURL = url
                    }
                } label: {
                    Label("Input", systemImage: "folder")
                }

                Button {
                    chooseFolder(title: "Output-Ordner wählen") { url in
                        outputURL = url
                    }
                } label: {
                    Label("Output", systemImage: "externaldrive")
                }

                Button {
                    reloadPreview()
                } label: {
                    Label("Aktualisieren", systemImage: "arrow.clockwise")
                }
                .disabled(inputURL == nil)

                Spacer(minLength: 12)

                Picker("Modus", selection: $trackMode) {
                    Text("Zusammenführen").tag(TonUinoExportOptions.TrackMode.merged)
                    Text("Getrennt").tag(TonUinoExportOptions.TrackMode.separate)
                }
                .pickerStyle(.segmented)
                .frame(width: 230)

                Button {
                    export()
                } label: {
                    Label("Exportieren", systemImage: "square.and.arrow.down")
                }
                .buttonStyle(.borderedProminent)
                .disabled(!canExport)
            }

            HStack(spacing: 18) {
                Toggle("Überschreiben", isOn: $overwriteExisting)
                    .frame(minWidth: 130, alignment: .leading)
                Toggle("Bericht", isOn: $writeReport)
                    .frame(minWidth: 90, alignment: .leading)
                Toggle("dot_clean", isOn: $cleanAfterExport)
                    .frame(minWidth: 100, alignment: .leading)
                Toggle("Prüfen", isOn: $validateAfterExport)
                    .frame(minWidth: 90, alignment: .leading)
            }
            .toggleStyle(.checkbox)
            .fixedSize(horizontal: true, vertical: false)
        }
        .padding(12)
    }

    private var header: some View {
        VStack(alignment: .leading, spacing: 8) {
            Text("TonUINO Export")
                .font(.title2.weight(.semibold))

            VStack(alignment: .leading, spacing: 4) {
                PathRow(label: "Input", url: inputURL)
                PathRow(label: "Output", url: outputURL)
            }

            Text("\(episodes.count) Folgen, \(episodes.reduce(0) { $0 + $1.tracks.count }) Quelldateien")
                .foregroundStyle(.secondary)
        }
        .padding([.horizontal, .top], 16)
    }

    private var episodeTable: some View {
        Table(episodes) {
            TableColumn("Ziel") { episode in
                Text(episode.targetFolderName)
                    .monospacedDigit()
            }
            .width(48)

            TableColumn("Folge") { episode in
                VStack(alignment: .leading, spacing: 2) {
                    Text(episode.sourceName)
                        .lineLimit(1)
                    if !episode.warnings.isEmpty {
                        Text(episode.warnings.joined(separator: " · "))
                            .font(.caption)
                            .foregroundStyle(.orange)
                            .lineLimit(2)
                    }
                }
            }

            TableColumn("Quellen") { episode in
                Text("\(episode.tracks.count)")
                    .monospacedDigit()
            }
            .width(64)

            TableColumn("Zieldatei") { episode in
                Text(targetFileDescription(for: episode))
                    .monospacedDigit()
            }
            .width(108)

            TableColumn("Erste Quelle") { episode in
                Text(episode.tracks.first?.source.lastPathComponent ?? "-")
                    .foregroundStyle(.secondary)
                    .lineLimit(1)
            }
        }
        .padding(.horizontal, 16)
        .padding(.bottom, 16)
    }

    private var messageList: some View {
        ScrollView {
            LazyVStack(alignment: .leading, spacing: 8) {
                if messages.isEmpty {
                    Text("Noch kein Export ausgeführt.")
                        .foregroundStyle(.secondary)
                } else {
                    ForEach(messages) { message in
                        HStack(alignment: .top, spacing: 8) {
                            Image(systemName: message.iconName)
                                .foregroundStyle(message.color)
                                .frame(width: 18)
                            Text(message.text)
                                .textSelection(.enabled)
                                .frame(maxWidth: .infinity, alignment: .leading)
                        }
                    }
                }
            }
        }
    }

    private func reloadPreview() {
        guard let inputURL else {
            episodes = []
            return
        }

        do {
            let scanner = TonUinoScanner(fileManager: .default)
            episodes = try scanner.scan(inputURL: inputURL)
            messages = scannerMessages(for: episodes)
        } catch {
            episodes = []
            messages = [.error("Vorschau fehlgeschlagen: \(error.localizedDescription)")]
        }
    }

    private func scannerMessages(for episodes: [EpisodePlan]) -> [ExportMessage] {
        var result: [ExportMessage] = []

        if episodes.isEmpty {
            result.append(.warning("Keine Unterordner mit Audiodateien gefunden."))
        }

        for episode in episodes where !episode.warnings.isEmpty {
            result.append(.warning("\(episode.sourceName): \(episode.warnings.joined(separator: ", "))"))
        }

        return result
    }

    private func export() {
        guard let outputURL else { return }

        isExporting = true
        messages = [.info("Export gestartet.")]

        DispatchQueue.global(qos: .userInitiated).async {
            let exporter = TonUinoExporter(fileManager: .default)
            var result = exporter.export(
                episodes: episodes,
                outputURL: outputURL,
                options: TonUinoExportOptions(
                    overwriteExisting: overwriteExisting,
                    writeReport: writeReport,
                    trackMode: trackMode
                )
            )

            if cleanAfterExport {
                result.append(contentsOf: SDCardCleaner().clean(url: outputURL))
            }

            if validateAfterExport {
                result.append(contentsOf: SDCardValidator(fileManager: .default).validate(rootURL: outputURL))
            }

            DispatchQueue.main.async {
                messages = result
                isExporting = false
            }
        }
    }

    private func targetFileDescription(for episode: EpisodePlan) -> String {
        switch trackMode {
        case .merged:
            "001.mp3"
        case .separate:
            episode.tracks.count <= 1 ? "001.mp3" : "001...\(String(format: "%03d", episode.tracks.count)).mp3"
        }
    }
}

private struct SDCardToolView: View {
    @State private var sdCardURL: URL?
    @State private var messages: [ExportMessage] = []
    @State private var isWorking = false

    private var canRun: Bool {
        sdCardURL != nil && !isWorking
    }

    var body: some View {
        VStack(spacing: 0) {
            HStack(spacing: 10) {
                Button {
                    chooseFolder(title: "TonUINO SD-Karte wählen") { url in
                        sdCardURL = url
                        messages = []
                    }
                } label: {
                    Label("SD-Karte", systemImage: "sdcard")
                }

                Spacer()

                Button {
                    validateSDCard()
                } label: {
                    Label("Prüfen", systemImage: "checklist")
                }
                .disabled(!canRun)

                Button {
                    cleanSDCard()
                } label: {
                    Label("dot_clean ausführen", systemImage: "sparkles")
                }
                .buttonStyle(.borderedProminent)
                .disabled(!canRun)
            }
            .padding(12)

            Divider()

            VStack(alignment: .leading, spacing: 16) {
                VStack(alignment: .leading, spacing: 8) {
                    Text("SD-Karte bereinigen")
                        .font(.title2.weight(.semibold))

                    PathRow(label: "Karte", url: sdCardURL)
                }

                VStack(alignment: .leading, spacing: 10) {
                    Text("Protokoll")
                        .font(.headline)

                    ScrollView {
                        LazyVStack(alignment: .leading, spacing: 8) {
                            if messages.isEmpty {
                                Text("Noch keine SD-Karte geprüft oder bereinigt.")
                                    .foregroundStyle(.secondary)
                            } else {
                                ForEach(messages) { message in
                                    HStack(alignment: .top, spacing: 8) {
                                        Image(systemName: message.iconName)
                                            .foregroundStyle(message.color)
                                            .frame(width: 18)
                                        Text(message.text)
                                            .textSelection(.enabled)
                                            .frame(maxWidth: .infinity, alignment: .leading)
                                    }
                                }
                            }
                        }
                        .frame(maxWidth: .infinity, alignment: .leading)
                    }
                }

                Spacer(minLength: 0)
            }
            .padding(16)
            .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
        }
    }

    private func cleanSDCard() {
        guard let sdCardURL else { return }

        isWorking = true
        messages = [.info("dot_clean gestartet.")]

        DispatchQueue.global(qos: .userInitiated).async {
            var result = SDCardCleaner().clean(url: sdCardURL)
            result.append(contentsOf: SDCardValidator(fileManager: .default).validate(rootURL: sdCardURL))

            DispatchQueue.main.async {
                messages = result
                isWorking = false
            }
        }
    }

    private func validateSDCard() {
        guard let sdCardURL else { return }

        isWorking = true
        messages = [.info("Prüfung gestartet.")]

        DispatchQueue.global(qos: .userInitiated).async {
            let result = SDCardValidator(fileManager: .default).validate(rootURL: sdCardURL)

            DispatchQueue.main.async {
                messages = result
                isWorking = false
            }
        }
    }

}

private struct MP3GainToolView: View {
    @State private var targetURL: URL?
    @State private var messages: [ExportMessage] = []
    @State private var isWorking = false
    @State private var gainMode: MP3GainNormalizer.Options.GainMode = .track
    @State private var preventClipping = true
    @State private var dbOffset = "4"

    private var canRun: Bool {
        targetURL != nil && !isWorking
    }

    private var options: MP3GainNormalizer.Options {
        MP3GainNormalizer.Options(
            gainMode: gainMode,
            preventClipping: preventClipping,
            dbOffset: dbOffset
        )
    }

    var body: some View {
        VStack(spacing: 0) {
            HStack(spacing: 10) {
                Button {
                    chooseFolder(title: "Ordner für MP3Gain wählen") { url in
                        targetURL = url
                        messages = []
                    }
                } label: {
                    Label("Ordner", systemImage: "folder")
                }

                Spacer()

                Button {
                    normalizeMP3Gain()
                } label: {
                    Label("MP3Gain ausführen", systemImage: "waveform")
                }
                .buttonStyle(.borderedProminent)
                .disabled(!canRun)
            }
            .padding(12)

            Divider()

            VStack(alignment: .leading, spacing: 16) {
                VStack(alignment: .leading, spacing: 8) {
                    Text("MP3Gain Normalisierung")
                        .font(.title2.weight(.semibold))
                    PathRow(label: "Ordner", url: targetURL)
                    Text("Generierter Befehl: \(options.commandDescription)")
                        .foregroundStyle(.secondary)
                }

                VStack(alignment: .leading, spacing: 10) {
                    Text("Optionen")
                        .font(.headline)

                    Picker("Modus", selection: $gainMode) {
                        Text("Track-Gain (-r)").tag(MP3GainNormalizer.Options.GainMode.track)
                        Text("Album-Gain (-a)").tag(MP3GainNormalizer.Options.GainMode.album)
                    }
                    .pickerStyle(.segmented)
                    .frame(width: 300)

                    Toggle("Clipping vermeiden (-k)", isOn: $preventClipping)
                        .toggleStyle(.checkbox)

                    HStack(spacing: 8) {
                        Text("dB-Offset (-d)")
                            .font(.caption.weight(.semibold))
                            .foregroundStyle(.secondary)
                        TextField("", text: $dbOffset)
                            .textFieldStyle(.roundedBorder)
                            .frame(width: 80)
                    }
                }

                VStack(alignment: .leading, spacing: 10) {
                    Text("Protokoll")
                        .font(.headline)
                    MessageList(messages: messages, emptyText: "Noch keine MP3-Dateien normalisiert.")
                }

                Spacer(minLength: 0)
            }
            .padding(16)
            .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
        }
    }

    private func normalizeMP3Gain() {
        guard let targetURL else { return }

        isWorking = true
        let currentOptions = options
        messages = [.info("MP3Gain gestartet: \(currentOptions.commandDescription)")]

        DispatchQueue.global(qos: .userInitiated).async {
            let result = MP3GainNormalizer(fileManager: .default).normalize(rootURL: targetURL, options: currentOptions)

            DispatchQueue.main.async {
                messages = result
                isWorking = false
            }
        }
    }
}

private struct ID3TagToolView: View {
    @State private var targetURL: URL?
    @State private var messages: [ExportMessage] = []
    @State private var isWorking = false
    @State private var removeAllTags = false
    @State private var removeTitle = true
    @State private var removeArtist = true
    @State private var removeAlbum = true
    @State private var removeTrack = true
    @State private var removeYear = true
    @State private var removeGenre = true
    @State private var removeComment = true
    @State private var removeArtwork = true
    @State private var writeComment = false
    @State private var commentText = ""
    @State private var tagSearchText = ""

    private var canApply: Bool {
        targetURL != nil && !isWorking && (removeAllTags || selectedFields.isEmpty == false || writeComment)
    }

    private var canSearchTags: Bool {
        targetURL != nil && !isWorking && !tagSearchText.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
    }

    private var selectedFields: Set<ID3TagField> {
        var fields = Set<ID3TagField>()
        if removeTitle { fields.insert(.title) }
        if removeArtist { fields.insert(.artist) }
        if removeAlbum { fields.insert(.album) }
        if removeTrack { fields.insert(.track) }
        if removeYear { fields.insert(.year) }
        if removeGenre { fields.insert(.genre) }
        if removeComment && !writeComment { fields.insert(.comment) }
        if removeArtwork { fields.insert(.artwork) }
        return fields
    }

    var body: some View {
        VStack(spacing: 0) {
            HStack(spacing: 10) {
                Button {
                    chooseFolder(title: "SD-Karte oder Ordner wählen") { url in
                        targetURL = url
                        scanTags(for: url)
                    }
                } label: {
                    Label("Ordner wählen", systemImage: "folder")
                }

                Spacer()

                Button {
                    if let targetURL {
                        scanTags(for: targetURL)
                    }
                } label: {
                    Label("Tags anzeigen", systemImage: "list.bullet.rectangle")
                }
                .disabled(targetURL == nil || isWorking)

                Button {
                    applyChanges()
                } label: {
                    Label("Tags anwenden", systemImage: "tag.slash")
                }
                .buttonStyle(.borderedProminent)
                .disabled(!canApply)
            }
            .padding(12)

            Divider()

            HSplitView {
                VStack(alignment: .leading, spacing: 16) {
                    VStack(alignment: .leading, spacing: 8) {
                        Text("ID3-Tags bearbeiten")
                            .font(.title2.weight(.semibold))
                        PathRow(label: "Ordner", url: targetURL)
                    }

                    VStack(alignment: .leading, spacing: 10) {
                        Toggle("Alle ID3-Tags löschen", isOn: $removeAllTags)
                            .toggleStyle(.checkbox)

                        Divider()

                        Text("Felder leeren/löschen")
                            .font(.headline)

                        Grid(alignment: .leading, horizontalSpacing: 24, verticalSpacing: 8) {
                            GridRow {
                                Toggle("Titel", isOn: $removeTitle)
                                Toggle("Interpret", isOn: $removeArtist)
                            }
                            GridRow {
                                Toggle("Album", isOn: $removeAlbum)
                                Toggle("Tracknummer", isOn: $removeTrack)
                            }
                            GridRow {
                                Toggle("Jahr", isOn: $removeYear)
                                Toggle("Genre", isOn: $removeGenre)
                            }
                            GridRow {
                                Toggle("Kommentar", isOn: $removeComment)
                                    .disabled(writeComment)
                                Toggle("Cover/Bilder", isOn: $removeArtwork)
                            }
                        }
                        .toggleStyle(.checkbox)
                        .disabled(removeAllTags)

                        Divider()

                        Toggle("Kommentar eintragen", isOn: $writeComment)
                            .toggleStyle(.checkbox)
                            .onChange(of: writeComment) { newValue in
                                if newValue {
                                    removeComment = false
                                }
                            }

                        HStack(spacing: 8) {
                            TextField("", text: $commentText)
                                .textFieldStyle(.roundedBorder)
                                .frame(width: 240)

                            Button {
                                useClipboardAsComment()
                            } label: {
                                Label("Aus Zwischenablage", systemImage: "doc.on.clipboard")
                            }

                            Button {
                                useSelectedFolderNameAsComment()
                            } label: {
                                Label("Ordnername", systemImage: "folder")
                            }

                            Button {
                                clearComment()
                            } label: {
                                Label("Leeren", systemImage: "xmark.circle")
                            }
                        }

                        Divider()

                        Text("Tag-Suche")
                            .font(.headline)

                        HStack(spacing: 8) {
                            TextField("", text: $tagSearchText)
                                .textFieldStyle(.roundedBorder)
                                .frame(width: 240)

                            Button {
                                searchTags()
                            } label: {
                                Label("Tags suchen", systemImage: "magnifyingglass")
                            }
                            .disabled(!canSearchTags)
                        }
                    }

                    Spacer(minLength: 0)
                }
                .padding(16)
                .frame(minWidth: 420, maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)

                VStack(alignment: .leading, spacing: 12) {
                    Text("Protokoll")
                        .font(.headline)
                    MessageList(messages: messages, emptyText: "Noch keine ID3-Tags angezeigt oder bearbeitet.")
                }
                .frame(minWidth: 300)
                .padding(16)
            }
        }
    }

    private func applyChanges() {
        guard let targetURL else { return }

        isWorking = true
        messages = [.info("ID3-Bearbeitung gestartet.")]

        let options = ID3TagEditOptions(
            removeAllTags: removeAllTags,
            fieldsToRemove: selectedFields,
            commentToWrite: writeComment ? commentText.trimmingCharacters(in: .whitespacesAndNewlines) : nil
        )

        DispatchQueue.global(qos: .userInitiated).async {
            var result = ID3TagEditor(fileManager: .default).apply(to: targetURL, options: options)
            result.append(contentsOf: ID3TagInspector(fileManager: .default).inspect(rootURL: targetURL))

            DispatchQueue.main.async {
                messages = result
                isWorking = false
            }
        }
    }

    private func scanTags(for url: URL) {
        isWorking = true
        messages = [.info("ID3-Tags werden gelesen.")]

        DispatchQueue.global(qos: .userInitiated).async {
            let result = ID3TagInspector(fileManager: .default).inspect(rootURL: url)

            DispatchQueue.main.async {
                messages = result
                isWorking = false
            }
        }
    }

    private func searchTags() {
        guard let targetURL else { return }

        isWorking = true
        messages = [.info("Tag-Suche gestartet.")]

        let query = tagSearchText.trimmingCharacters(in: .whitespacesAndNewlines)

        DispatchQueue.global(qos: .userInitiated).async {
            let result = ID3TagSearcher(fileManager: .default).search(rootURL: targetURL, query: query)

            DispatchQueue.main.async {
                messages = result
                isWorking = false
            }
        }
    }

    private func useClipboardAsComment() {
        guard let text = NSPasteboard.general.string(forType: .string)?
            .trimmingCharacters(in: .whitespacesAndNewlines),
            !text.isEmpty else {
            messages = [.warning("Zwischenablage enthält keinen Text.")]
            return
        }

        setComment(text)
    }

    private func useSelectedFolderNameAsComment() {
        guard let targetURL else {
            messages = [.warning("Kein Ordner gewählt.")]
            return
        }

        setComment(targetURL.lastPathComponent)
    }

    private func clearComment() {
        commentText = ""
        writeComment = false
    }

    private func setComment(_ text: String) {
        commentText = text
        writeComment = true
        removeComment = false
    }
}

private struct MessageList: View {
    let messages: [ExportMessage]
    let emptyText: String

    var body: some View {
        ScrollView {
            LazyVStack(alignment: .leading, spacing: 8) {
                if messages.isEmpty {
                    Text(emptyText)
                        .foregroundStyle(.secondary)
                } else {
                    ForEach(messages) { message in
                        HStack(alignment: .top, spacing: 8) {
                            Image(systemName: message.iconName)
                                .foregroundStyle(message.color)
                                .frame(width: 18)
                            Text(message.text)
                                .textSelection(.enabled)
                                .frame(maxWidth: .infinity, alignment: .leading)
                        }
                    }
                }
            }
            .frame(maxWidth: .infinity, alignment: .leading)
        }
    }
}

private func chooseFolder(title: String, completion: (URL) -> Void) {
    let panel = NSOpenPanel()
    panel.title = title
    panel.canChooseFiles = false
    panel.canChooseDirectories = true
    panel.allowsMultipleSelection = false
    panel.canCreateDirectories = true

    if panel.runModal() == .OK, let url = panel.url {
        completion(url)
    }
}

private struct PathRow: View {
    let label: String
    let url: URL?

    var body: some View {
        HStack(alignment: .firstTextBaseline, spacing: 8) {
            Text(label)
                .font(.caption.weight(.semibold))
                .foregroundStyle(.secondary)
                .frame(width: 48, alignment: .leading)
            Text(url?.path(percentEncoded: false) ?? "Nicht gewählt")
                .font(.caption)
                .lineLimit(1)
                .truncationMode(.middle)
                .textSelection(.enabled)
        }
    }
}

struct ExportMessage: Identifiable {
    enum Kind {
        case info
        case warning
        case error
    }

    let id = UUID()
    let kind: Kind
    let text: String

    var iconName: String {
        switch kind {
        case .info: "checkmark.circle"
        case .warning: "exclamationmark.triangle"
        case .error: "xmark.octagon"
        }
    }

    var color: Color {
        switch kind {
        case .info: .green
        case .warning: .orange
        case .error: .red
        }
    }

    static func info(_ text: String) -> ExportMessage {
        ExportMessage(kind: .info, text: text)
    }

    static func warning(_ text: String) -> ExportMessage {
        ExportMessage(kind: .warning, text: text)
    }

    static func error(_ text: String) -> ExportMessage {
        ExportMessage(kind: .error, text: text)
    }
}
