//
//  ShortcutsView.swift
//  oAI
//
//  Modal for managing user-defined shortcuts (opened via /shortcuts command)
//
// SPDX-License-Identifier: AGPL-3.0-or-later
// Copyright (C) 2026 Rune Olsen
//
// This file is part of oAI.
//
// oAI is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// oAI is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General
// Public License for more details.
//
// You should have received a copy of the GNU Affero General Public
// License along with oAI. If not, see <https://www.gnu.org/licenses/>.


import SwiftUI
import UniformTypeIdentifiers

struct ShortcutsView: View {
    @Environment(\.dismiss) var dismiss
    @Bindable private var settings = SettingsService.shared

    @State private var showEditor = false
    @State private var editingShortcut: Shortcut? = nil
    @State private var importConflicts: [ShortcutConflict] = []
    @State private var showConflictAlert = false
    @State private var pendingImport: [Shortcut] = []
    @State private var statusMessage: String? = nil

    var body: some View {
        VStack(spacing: 0) {
            // Header
            HStack {
                Label("Your Shortcuts", systemImage: "bolt.fill")
                    .font(.system(size: 18, weight: .bold))
                Spacer()
                Button { dismiss() } label: {
                    Image(systemName: "xmark.circle.fill")
                        .font(.title2)
                        .foregroundStyle(.secondary)
                }
                .buttonStyle(.plain)
                .keyboardShortcut(.escape, modifiers: [])
            }
            .padding(.horizontal, 24)
            .padding(.top, 20)
            .padding(.bottom, 12)

            // Toolbar
            HStack(spacing: 10) {
                Button { editingShortcut = nil; showEditor = true } label: {
                    Label("New Shortcut", systemImage: "plus")
                }
                .buttonStyle(.bordered)

                Button { importShortcuts() } label: {
                    Label("Import", systemImage: "square.and.arrow.down")
                }
                .buttonStyle(.bordered)

                Button { exportAll() } label: {
                    Label("Export All", systemImage: "square.and.arrow.up")
                }
                .buttonStyle(.bordered)
                .disabled(settings.userShortcuts.isEmpty)

                Spacer()

                if let msg = statusMessage {
                    Text(msg).font(.caption).foregroundStyle(.secondary)
                }
            }
            .padding(.horizontal, 24)
            .padding(.bottom, 12)

            Divider()

            if settings.userShortcuts.isEmpty {
                VStack(spacing: 12) {
                    Image(systemName: "bolt.slash")
                        .font(.system(size: 40))
                        .foregroundStyle(.tertiary)
                    Text("No shortcuts yet")
                        .font(.title3)
                        .foregroundStyle(.secondary)
                    Text("Create a shortcut to save a reusable prompt template accessible from the / command dropdown.")
                        .font(.callout)
                        .foregroundStyle(.secondary)
                        .multilineTextAlignment(.center)
                        .frame(maxWidth: 340)
                }
                .frame(maxWidth: .infinity, maxHeight: .infinity)
                .padding()
            } else {
                List {
                    ForEach(settings.userShortcuts) { shortcut in
                        ShortcutRow(
                            shortcut: shortcut,
                            onEdit: { editingShortcut = shortcut; showEditor = true },
                            onExport: { exportOne(shortcut) },
                            onDelete: { settings.deleteShortcut(id: shortcut.id) }
                        )
                    }
                }
                .listStyle(.inset(alternatesRowBackgrounds: true))
            }

            Divider()

            HStack(spacing: 8) {
                Image(systemName: "lightbulb")
                    .foregroundStyle(.yellow)
                    .font(.callout)
                Text("Use **{{input}}** in the template to insert whatever you type after the command.")
                    .font(.caption)
                    .foregroundStyle(.secondary)
                Spacer()
                Button("Done") { dismiss() }
                    .buttonStyle(.borderedProminent)
                    .keyboardShortcut(.return, modifiers: [])
            }
            .padding(.horizontal, 24)
            .padding(.vertical, 12)
        }
        .frame(minWidth: 560, idealWidth: 640, minHeight: 440, idealHeight: 560)
        .sheet(isPresented: $showEditor) {
            ShortcutEditorSheet(shortcut: editingShortcut) { saved in
                if editingShortcut != nil { settings.updateShortcut(saved) }
                else { settings.addShortcut(saved) }
                editingShortcut = nil
            }
        }
        .alert("Duplicate Shortcut", isPresented: $showConflictAlert, presenting: importConflicts.first) { c in
            Button("Replace") { resolveConflict(c, action: .replace) }
            Button("Keep Both") { resolveConflict(c, action: .keepBoth) }
            Button("Skip") { resolveConflict(c, action: .skip) }
        } message: { c in
            Text("A shortcut with command \(c.incoming.command) already exists.")
        }
    }

    // MARK: - Import / Export

    private func importShortcuts() {
        let panel = NSOpenPanel()
        panel.allowsMultipleSelection = true
        panel.canChooseFiles = true
        panel.canChooseDirectories = false
        panel.allowedContentTypes = [.json]
        panel.message = "Select shortcut JSON files to import"
        guard panel.runModal() == .OK else { return }

        var incoming: [Shortcut] = []
        for url in panel.urls {
            guard let data = try? Data(contentsOf: url) else { continue }
            if let pack = try? JSONDecoder().decode([Shortcut].self, from: data) {
                incoming.append(contentsOf: pack)
            } else if let single = try? JSONDecoder().decode(Shortcut.self, from: data) {
                incoming.append(single)
            }
        }
        guard !incoming.isEmpty else { show("No valid shortcuts found"); return }
        pendingImport = incoming
        processNext()
    }

    private func processNext() {
        guard !pendingImport.isEmpty else { importConflicts = []; return }
        let item = pendingImport.removeFirst()
        if settings.userShortcuts.first(where: { $0.command == item.command }) != nil {
            importConflicts.append(ShortcutConflict(incoming: item))
            showConflictAlert = true
        } else {
            settings.addShortcut(item)
            show("Imported \(item.command)")
            processNext()
        }
    }

    private func resolveConflict(_ c: ShortcutConflict, action: ConflictAction) {
        importConflicts.removeFirst()
        switch action {
        case .replace:
            if let ex = settings.userShortcuts.first(where: { $0.command == c.incoming.command }) {
                var u = c.incoming; u.id = ex.id; settings.updateShortcut(u)
            }
        case .keepBoth:
            var copy = c.incoming; copy.id = UUID(); copy.command += "-copy"; settings.addShortcut(copy)
        case .skip: break
        }
        processNext()
    }

    private func exportAll() {
        let enc = JSONEncoder(); enc.dateEncodingStrategy = .iso8601
        enc.outputFormatting = [.prettyPrinted, .sortedKeys]
        guard let data = try? enc.encode(settings.userShortcuts) else { return }
        let url = FileManager.default.urls(for: .downloadsDirectory, in: .userDomainMask).first!
            .appendingPathComponent("shortcuts.json")
        try? data.write(to: url)
        show("Exported to Downloads/shortcuts.json")
    }

    private func exportOne(_ shortcut: Shortcut) {
        let enc = JSONEncoder(); enc.dateEncodingStrategy = .iso8601
        enc.outputFormatting = [.prettyPrinted, .sortedKeys]
        guard let data = try? enc.encode(shortcut) else { return }
        let filename = String(shortcut.command.dropFirst()) + ".json"
        let url = FileManager.default.urls(for: .downloadsDirectory, in: .userDomainMask).first!
            .appendingPathComponent(filename)
        try? data.write(to: url)
        show("Exported \(filename)")
    }

    private func show(_ text: String) {
        statusMessage = text
        Task { try? await Task.sleep(for: .seconds(3)); statusMessage = nil }
    }

    // MARK: - Types

    struct ShortcutConflict { let incoming: Shortcut }
    enum ConflictAction { case replace, keepBoth, skip }
}

// MARK: - Shortcut Row

private struct ShortcutRow: View {
    let shortcut: Shortcut
    let onEdit: () -> Void
    let onExport: () -> Void
    let onDelete: () -> Void

    var body: some View {
        HStack(spacing: 12) {
            VStack(alignment: .leading, spacing: 3) {
                Text(shortcut.command)
                    .font(.system(size: 13, weight: .semibold, design: .monospaced))
                Text(shortcut.description)
                    .font(.callout)
                    .foregroundStyle(.secondary)
                    .lineLimit(1)
            }
            Spacer()
            if shortcut.needsInput {
                Label("needs input", systemImage: "text.cursor")
                    .font(.caption2).foregroundStyle(.secondary)
                    .padding(.horizontal, 6).padding(.vertical, 3)
                    .background(.secondary.opacity(0.12), in: Capsule())
            } else {
                Label("immediate", systemImage: "bolt")
                    .font(.caption2).foregroundStyle(.orange)
                    .padding(.horizontal, 6).padding(.vertical, 3)
                    .background(.orange.opacity(0.12), in: Capsule())
            }
            Button(action: onEdit) { Text("Edit").font(.caption) }
                .buttonStyle(.bordered).controlSize(.small)
            Button(action: onExport) { Image(systemName: "square.and.arrow.up") }
                .buttonStyle(.bordered).controlSize(.small).help("Export shortcut")
            Button(role: .destructive, action: onDelete) { Image(systemName: "trash") }
                .buttonStyle(.bordered).controlSize(.small).help("Delete shortcut")
        }
        .padding(.vertical, 4)
    }
}

// MARK: - Shortcuts Tab Content (embedded in SettingsView)

struct ShortcutsTabContent: View {
    @Bindable private var settings = SettingsService.shared

    @State private var showEditor = false
    @State private var editingShortcut: Shortcut? = nil
    @State private var importConflicts: [ShortcutsView.ShortcutConflict] = []
    @State private var showConflictAlert = false
    @State private var pendingImport: [Shortcut] = []
    @State private var statusMessage: String? = nil

    var body: some View {
        VStack(alignment: .leading, spacing: 16) {
            HStack(spacing: 10) {
                Button { editingShortcut = nil; showEditor = true } label: {
                    Label("New Shortcut", systemImage: "plus")
                }
                .buttonStyle(.bordered)

                Button { importShortcuts() } label: {
                    Label("Import", systemImage: "square.and.arrow.down")
                }
                .buttonStyle(.bordered)

                Button { exportAll() } label: {
                    Label("Export All", systemImage: "square.and.arrow.up")
                }
                .buttonStyle(.bordered)
                .disabled(settings.userShortcuts.isEmpty)

                Spacer()
                if let msg = statusMessage {
                    Text(msg).font(.caption).foregroundStyle(.secondary)
                }
            }

            if settings.userShortcuts.isEmpty {
                HStack {
                    Spacer()
                    VStack(spacing: 8) {
                        Image(systemName: "bolt.slash")
                            .font(.system(size: 32)).foregroundStyle(.tertiary)
                        Text("No shortcuts yet — click New Shortcut to create one.")
                            .font(.callout).foregroundStyle(.secondary)
                    }
                    Spacer()
                }
                .padding(.vertical, 32)
            } else {
                VStack(spacing: 0) {
                    ForEach(Array(settings.userShortcuts.enumerated()), id: \.element.id) { idx, shortcut in
                        ShortcutRow(
                            shortcut: shortcut,
                            onEdit: { editingShortcut = shortcut; showEditor = true },
                            onExport: { exportOne(shortcut) },
                            onDelete: { settings.deleteShortcut(id: shortcut.id) }
                        )
                        if idx < settings.userShortcuts.count - 1 { Divider() }
                    }
                }
                .background(.background, in: RoundedRectangle(cornerRadius: 10))
                .overlay(RoundedRectangle(cornerRadius: 10).stroke(.quaternary))
            }

            HStack(spacing: 8) {
                Image(systemName: "lightbulb").foregroundStyle(.yellow).font(.callout)
                Text("Use **{{input}}** in the template to insert whatever you type after the command.")
                    .font(.caption).foregroundStyle(.secondary)
            }
            .padding(10)
            .background(.blue.opacity(0.06), in: RoundedRectangle(cornerRadius: 8))
        }
        .sheet(isPresented: $showEditor) {
            ShortcutEditorSheet(shortcut: editingShortcut) { saved in
                if editingShortcut != nil { settings.updateShortcut(saved) }
                else { settings.addShortcut(saved) }
                editingShortcut = nil
            }
        }
        .alert("Duplicate Shortcut", isPresented: $showConflictAlert, presenting: importConflicts.first) { c in
            Button("Replace") { resolveConflict(c, action: .replace) }
            Button("Keep Both") { resolveConflict(c, action: .keepBoth) }
            Button("Skip") { resolveConflict(c, action: .skip) }
        } message: { c in
            Text("A shortcut with command \(c.incoming.command) already exists.")
        }
    }

    private func importShortcuts() {
        let panel = NSOpenPanel()
        panel.allowsMultipleSelection = true; panel.canChooseFiles = true
        panel.canChooseDirectories = false; panel.allowedContentTypes = [.json]
        panel.message = "Select shortcut JSON files to import"
        guard panel.runModal() == .OK else { return }
        var incoming: [Shortcut] = []
        for url in panel.urls {
            guard let data = try? Data(contentsOf: url) else { continue }
            if let pack = try? JSONDecoder().decode([Shortcut].self, from: data) { incoming.append(contentsOf: pack) }
            else if let single = try? JSONDecoder().decode(Shortcut.self, from: data) { incoming.append(single) }
        }
        guard !incoming.isEmpty else { show("No valid shortcuts found"); return }
        pendingImport = incoming; processNext()
    }

    private func processNext() {
        guard !pendingImport.isEmpty else { importConflicts = []; return }
        let item = pendingImport.removeFirst()
        if settings.userShortcuts.first(where: { $0.command == item.command }) != nil {
            importConflicts.append(ShortcutsView.ShortcutConflict(incoming: item)); showConflictAlert = true
        } else {
            settings.addShortcut(item); show("Imported \(item.command)"); processNext()
        }
    }

    private func resolveConflict(_ c: ShortcutsView.ShortcutConflict, action: ShortcutsView.ConflictAction) {
        importConflicts.removeFirst()
        switch action {
        case .replace:
            if let ex = settings.userShortcuts.first(where: { $0.command == c.incoming.command }) {
                var u = c.incoming; u.id = ex.id; settings.updateShortcut(u)
            }
        case .keepBoth:
            var copy = c.incoming; copy.id = UUID(); copy.command += "-copy"; settings.addShortcut(copy)
        case .skip: break
        }
        processNext()
    }

    private func exportAll() {
        let enc = JSONEncoder(); enc.dateEncodingStrategy = .iso8601
        enc.outputFormatting = [.prettyPrinted, .sortedKeys]
        guard let data = try? enc.encode(settings.userShortcuts) else { return }
        let url = FileManager.default.urls(for: .downloadsDirectory, in: .userDomainMask).first!
            .appendingPathComponent("shortcuts.json")
        try? data.write(to: url); show("Exported to Downloads/shortcuts.json")
    }

    private func exportOne(_ shortcut: Shortcut) {
        let enc = JSONEncoder(); enc.dateEncodingStrategy = .iso8601
        enc.outputFormatting = [.prettyPrinted, .sortedKeys]
        guard let data = try? enc.encode(shortcut) else { return }
        let filename = String(shortcut.command.dropFirst()) + ".json"
        let url = FileManager.default.urls(for: .downloadsDirectory, in: .userDomainMask).first!
            .appendingPathComponent(filename)
        try? data.write(to: url); show("Exported \(filename)")
    }

    private func show(_ text: String) {
        statusMessage = text
        Task { try? await Task.sleep(for: .seconds(3)); statusMessage = nil }
    }
}

#Preview { ShortcutsView() }
