//
//  InputBar.swift
//  oAI
//
//  Message input bar with resizable height and online toggle
//
// 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
#if os(macOS)
import AppKit
#endif

struct InputBar: View {
    @Binding var text: String
    let isGenerating: Bool
    let onlineMode: Bool
    let onSend: () -> Void
    let onCancel: () -> Void
    let onToggleOnline: () -> Void

    private let settings = SettingsService.shared

    // Resizable input height — persisted to settings
    @State private var inputHeight: CGFloat = CGFloat(SettingsService.shared.inputBarHeight)
    @State private var dragStartHeight: CGFloat = CGFloat(SettingsService.shared.inputBarHeight)

    @State private var showCommandDropdown = false
    @State private var selectedSuggestionIndex: Int = 0
    @FocusState private var isInputFocused: Bool

    private static let minInputHeight: CGFloat = 56
    private static let maxInputHeight: CGFloat = 320

    /// Commands that execute immediately without additional arguments
    private static let immediateCommands: Set<String> = [
        "/help", "/history", "/model", "/clear", "/retry", "/stats", "/config",
        "/settings", "/credits", "/list", "/load", "/shortcuts", "/skills", "/jarvis",
        "/memory on", "/memory off", "/online on", "/online off",
        "/mcp on", "/mcp off", "/mcp status", "/mcp list",
        "/mcp write on", "/mcp write off",
        "/export md", "/export json",
    ]

    var body: some View {
        VStack(spacing: 0) {
            // Command dropdown (if showing)
            if showCommandDropdown && text.hasPrefix("/") {
                HStack {
                    CommandSuggestionsView(
                        searchText: text,
                        selectedIndex: selectedSuggestionIndex,
                        onSelect: selectCommand
                    )
                    .frame(width: 400)
                    .frame(maxHeight: 200)
                    .transition(.move(edge: .bottom).combined(with: .opacity))
                    Spacer()
                }
                .padding(.leading, 16)
            }

            // Drag-to-resize handle
            dragHandle

            // Input row
            HStack(alignment: .bottom, spacing: 12) {
                // Text input with globe toggle in bottom-left corner
                ZStack(alignment: .topLeading) {
                    // Placeholder
                    if text.isEmpty {
                        Text("Type a message or / for commands...")
                            .font(.system(size: settings.inputTextSize))
                            .foregroundColor(.oaiSecondary)
                            .padding(.horizontal, 12)
                            .padding(.top, 10)
                            .allowsHitTesting(false)
                    }

                    // Editor — fills the fixed-height box, bottom area reserved for globe
                    TextEditor(text: $text)
                        .font(.system(size: settings.inputTextSize))
                        .foregroundColor(.oaiPrimary)
                        .scrollContentBackground(.hidden)
                        .padding(.horizontal, 8)
                        .padding(.top, 6)
                        .padding(.bottom, 30)
                        .frame(maxWidth: .infinity, maxHeight: .infinity)
                        .focused($isInputFocused)
                        .onChange(of: text) {
                            showCommandDropdown = text.hasPrefix("/")
                            selectedSuggestionIndex = 0
                        }
                        #if os(macOS)
                        .onKeyPress(.upArrow) {
                            if showCommandDropdown && selectedSuggestionIndex > 0 {
                                selectedSuggestionIndex -= 1
                                return .handled
                            }
                            return .ignored
                        }
                        .onKeyPress(.downArrow) {
                            if showCommandDropdown {
                                let count = CommandSuggestionsView.filteredCommands(for: text).count
                                if selectedSuggestionIndex < count - 1 {
                                    selectedSuggestionIndex += 1
                                    return .handled
                                }
                            }
                            return .ignored
                        }
                        .onKeyPress(.escape) {
                            if showCommandDropdown { showCommandDropdown = false; return .handled }
                            if isGenerating { onCancel(); return .handled }
                            return .ignored
                        }
                        .onKeyPress(.return, phases: .down) { press in
                            if press.modifiers.contains(.shift) { return .ignored }
                            if showCommandDropdown {
                                let suggestions = CommandSuggestionsView.filteredCommands(for: text)
                                if !suggestions.isEmpty && selectedSuggestionIndex < suggestions.count {
                                    selectCommand(suggestions[selectedSuggestionIndex].command)
                                    return .handled
                                }
                            }
                            if !text.isEmpty { onSend(); return .handled }
                            return .handled
                        }
                        #endif

                    // Online / offline toggle — bottom-left of the text box
                    VStack {
                        Spacer()
                        HStack {
                            Button(action: onToggleOnline) {
                                Image(systemName: onlineMode ? "globe" : "network.slash")
                                    .font(.system(size: 13, weight: .medium))
                                    .foregroundStyle(onlineMode ? Color.green : Color.secondary)
                                    .padding(8)
                            }
                            .buttonStyle(.plain)
                            .help(onlineMode
                                  ? "Online mode on — click to go offline"
                                  : "Offline — click to go online")
                            Spacer()
                        }
                    }
                }
                .frame(height: inputHeight)
                .background(Color.oaiSurface)
                .cornerRadius(8)
                .overlay(
                    RoundedRectangle(cornerRadius: 8)
                        .stroke(isInputFocused ? Color.oaiAccent : Color.oaiBorder, lineWidth: 1)
                )

                // Send / stop + attach buttons
                VStack(spacing: 8) {
                    #if os(macOS)
                    Button(action: pickFile) {
                        Image(systemName: "paperclip")
                            .font(.title2)
                            .foregroundColor(.oaiPrimary.opacity(0.7))
                    }
                    .buttonStyle(.plain)
                    .help("Attach file")
                    #endif

                    if isGenerating {
                        Button(action: onCancel) {
                            Image(systemName: "stop.circle.fill")
                                .font(.title)
                                .foregroundColor(.oaiError.opacity(0.9))
                        }
                        .buttonStyle(.plain)
                        .help("Stop generation")
                    } else {
                        Button(action: onSend) {
                            Image(systemName: "arrow.up.circle.fill")
                                .font(.title)
                                .foregroundColor(text.isEmpty ? .oaiPrimary.opacity(0.4) : .oaiAccent.opacity(0.9))
                        }
                        .buttonStyle(.plain)
                        .disabled(text.isEmpty)
                        .help("Send message")
                    }
                }
                .frame(width: 40)
            }
            .padding()
            .background(Color.oaiSurface)
        }
        .onAppear {
            isInputFocused = true
        }
    }

    // MARK: - Drag handle

    private var dragHandle: some View {
        Color.clear
            .frame(height: 8)
            .frame(maxWidth: .infinity)
            .contentShape(Rectangle())
            .overlay {
                Capsule()
                    .fill(Color.secondary.opacity(0.25))
                    .frame(width: 36, height: 3)
            }
            .gesture(
                DragGesture(minimumDistance: 1)
                    .onChanged { value in
                        let proposed = dragStartHeight - value.translation.height
                        inputHeight = max(Self.minInputHeight, min(Self.maxInputHeight, proposed))
                    }
                    .onEnded { _ in
                        dragStartHeight = inputHeight
                        settings.inputBarHeight = Double(inputHeight)
                    }
            )
            #if os(macOS)
            .onHover { hovering in
                if hovering { NSCursor.resizeUpDown.push() } else { NSCursor.pop() }
            }
            #endif
    }

    // MARK: - Helpers

    private func selectCommand(_ command: String) {
        showCommandDropdown = false
        if Self.immediateCommands.contains(command) {
            text = command
            onSend()
        } else if let shortcut = SettingsService.shared.userShortcuts.first(where: { $0.command == command }) {
            text = shortcut.needsInput ? command + " " : command
            if !shortcut.needsInput { onSend() }
        } else {
            text = command + " "
        }
    }

    #if os(macOS)
    private func pickFile() {
        let panel = NSOpenPanel()
        panel.allowsMultipleSelection = true
        panel.canChooseDirectories = false
        panel.canChooseFiles = true
        panel.message = "Select files to attach"
        guard panel.runModal() == .OK else { return }
        let attachmentText = panel.urls.map { "@<\($0.path)>" }.joined(separator: " ")
        text = text.isEmpty ? attachmentText + " " : text + " " + attachmentText
    }
    #endif
}

// MARK: - Command suggestions

struct CommandSuggestionsView: View {
    let searchText: String
    let selectedIndex: Int
    let onSelect: (String) -> Void

    static let builtInCommands: [(command: String, description: LocalizedStringKey)] = [
        ("/help", "Show help and available commands"),
        ("/history", "View command history"),
        ("/model", "Select AI model"),
        ("/clear", "Clear chat history"),
        ("/retry", "Retry last message"),
        ("/shortcuts", "Manage your prompt shortcuts"),
        ("/skills", "Manage your agent skills"),
        ("/jarvis", "Open Jarvis agent manager"),
        ("/memory on", "Enable conversation memory"),
        ("/memory off", "Disable conversation memory"),
        ("/online on", "Enable web search"),
        ("/online off", "Disable web search"),
        ("/stats", "Show session statistics"),
        ("/config", "Open settings"),
        ("/provider", "Switch AI provider"),
        ("/save", "Save conversation"),
        ("/load", "Load conversation"),
        ("/list", "List saved conversations"),
        ("/export md", "Export as Markdown"),
        ("/export json", "Export as JSON"),
        ("/info", "Show model information"),
        ("/credits", "Check account credits"),
        ("/mcp on", "Enable MCP (file access)"),
        ("/mcp off", "Disable MCP"),
        ("/mcp status", "Show MCP status"),
        ("/mcp list", "List MCP folders"),
        ("/mcp add", "Add folder for MCP"),
        ("/mcp write on", "Enable MCP write permissions"),
        ("/mcp write off", "Disable MCP write permissions"),
    ]

    static func allCommands() -> [(command: String, description: LocalizedStringKey)] {
        SettingsService.shared.userShortcuts.map { s in
            (s.command, LocalizedStringKey("⚡ \(s.description)"))
        } + builtInCommands
    }

    static func filteredCommands(for searchText: String) -> [(command: String, description: LocalizedStringKey)] {
        let search = searchText.lowercased()
        return allCommands().filter { $0.command.contains(search) || search == "/" }
    }

    private var suggestions: [(command: String, description: LocalizedStringKey)] {
        Self.filteredCommands(for: searchText)
    }

    var body: some View {
        ScrollViewReader { proxy in
            ScrollView {
                VStack(spacing: 0) {
                    ForEach(Array(suggestions.enumerated()), id: \.element.command) { index, suggestion in
                        Button(action: { onSelect(suggestion.command) }) {
                            HStack {
                                VStack(alignment: .leading, spacing: 2) {
                                    Text(suggestion.command)
                                        .font(.body)
                                        .foregroundColor(.oaiPrimary)
                                    Text(suggestion.description)
                                        .font(.caption)
                                        .foregroundColor(.oaiSecondary)
                                }
                                Spacer()
                            }
                            .padding(.horizontal, 12)
                            .padding(.vertical, 8)
                            .background(index == selectedIndex ? Color.oaiAccent.opacity(0.2) : Color.oaiSurface)
                        }
                        .buttonStyle(.plain)
                        .id(suggestion.command)

                        if index < suggestions.count - 1 {
                            Divider().background(Color.oaiBorder)
                        }
                    }
                }
            }
            .onChange(of: selectedIndex) {
                if selectedIndex < suggestions.count {
                    withAnimation { proxy.scrollTo(suggestions[selectedIndex].command, anchor: .center) }
                }
            }
        }
        .background(Color.oaiSurface)
        .cornerRadius(8)
        .overlay(RoundedRectangle(cornerRadius: 8).stroke(Color.oaiBorder, lineWidth: 1))
    }
}

#Preview {
    VStack {
        Spacer()
        InputBar(
            text: .constant(""),
            isGenerating: false,
            onlineMode: true,
            onSend: {},
            onCancel: {},
            onToggleOnline: {}
        )
    }
    .background(Color.oaiBackground)
}
