//
//  AgentSkillEditorSheet.swift
//  oAI
//
//  Create or edit a SKILL.md-style agent skill, with optional support files
//
// 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 AgentSkillEditorSheet: View {
    @Environment(\.dismiss) var dismiss

    let isNew: Bool
    let onSave: (AgentSkill) -> Void

    @State private var skillID: UUID
    @State private var name: String
    @State private var skillDescription: String
    @State private var content: String
    @State private var isActive: Bool
    @State private var createdAt: Date

    // File management
    @State private var skillFiles: [URL] = []
    @State private var didSave = false

    init(skill: AgentSkill? = nil, onSave: @escaping (AgentSkill) -> Void) {
        self.isNew = skill == nil
        self.onSave = onSave
        let s = skill ?? AgentSkill(name: "", content: "")
        _skillID = State(initialValue: s.id)
        _name = State(initialValue: s.name)
        _skillDescription = State(initialValue: s.skillDescription)
        _content = State(initialValue: s.content)
        _isActive = State(initialValue: s.isActive)
        _createdAt = State(initialValue: s.createdAt)
    }

    private var isValid: Bool {
        !name.trimmingCharacters(in: .whitespaces).isEmpty
            && !content.trimmingCharacters(in: .whitespaces).isEmpty
    }

    private var hasLargeFile: Bool {
        skillFiles.contains { url in
            let size = AgentSkillFilesService.shared.fileSize(at: url) ?? 0
            return size > 200_000
        }
    }

    var body: some View {
        VStack(spacing: 0) {
            // Header
            HStack {
                Text(isNew ? "New Skill" : "Edit Skill")
                    .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, 16)

            Divider()

            ScrollView {
                VStack(alignment: .leading, spacing: 16) {
                    // Name
                    VStack(alignment: .leading, spacing: 6) {
                        Text("Name")
                            .font(.system(size: 13, weight: .semibold))
                        TextField("e.g. Code Review, Test Writer, Security Auditor", text: $name)
                            .textFieldStyle(.roundedBorder)
                            .font(.system(size: 13))
                    }

                    // Description (optional)
                    VStack(alignment: .leading, spacing: 6) {
                        Text("Description")
                            .font(.system(size: 13, weight: .semibold))
                        TextField("Brief summary (optional — auto-extracted from content if left blank)", text: $skillDescription)
                            .textFieldStyle(.roundedBorder)
                            .font(.system(size: 13))
                    }

                    // Active toggle
                    HStack {
                        VStack(alignment: .leading, spacing: 2) {
                            Text("Active")
                                .font(.system(size: 13, weight: .semibold))
                            Text("Inject into system prompt for every conversation")
                                .font(.caption).foregroundStyle(.secondary)
                        }
                        Spacer()
                        Toggle("", isOn: $isActive).labelsHidden()
                    }
                    .padding(10)
                    .background(.secondary.opacity(0.07), in: RoundedRectangle(cornerRadius: 8))

                    // Content (markdown)
                    VStack(alignment: .leading, spacing: 6) {
                        HStack {
                            Text("Content (Markdown)")
                                .font(.system(size: 13, weight: .semibold))
                            Spacer()
                            Text("\(content.count) chars")
                                .font(.caption).foregroundStyle(.secondary)
                        }

                        TextEditor(text: $content)
                            .font(.system(size: 12, design: .monospaced))
                            .frame(minHeight: 200)
                            .scrollContentBackground(.hidden)
                            .padding(6)
                            .background(Color(nsColor: .textBackgroundColor))
                            .cornerRadius(6)
                            .overlay(RoundedRectangle(cornerRadius: 6)
                                .stroke(Color(nsColor: .separatorColor), lineWidth: 1))

                        // Format hint
                        VStack(alignment: .leading, spacing: 6) {
                            HStack(spacing: 6) {
                                Image(systemName: "doc.text").foregroundStyle(.purple).font(.callout)
                                Text("SKILL.md format — write instructions in plain Markdown.")
                                    .font(.caption).fontWeight(.medium)
                            }
                            Text("The AI reads this content and decides when to apply it. Describe **what** the AI should do and **how** — be specific and concise.")
                                .font(.caption).foregroundStyle(.secondary)
                            Text("Example structure:")
                                .font(.caption).foregroundStyle(.secondary).fontWeight(.medium)
                            Text("# When reviewing code, always:\n- Check for security vulnerabilities\n- Verify error handling\n- Suggest tests for edge cases")
                                .font(.system(size: 11, design: .monospaced))
                                .padding(8)
                                .frame(maxWidth: .infinity, alignment: .leading)
                                .background(.purple.opacity(0.06), in: RoundedRectangle(cornerRadius: 6))
                        }
                        .padding(10)
                        .background(.purple.opacity(0.05), in: RoundedRectangle(cornerRadius: 8))
                    }

                    // Files section
                    filesSection
                }
                .padding(.horizontal, 24).padding(.vertical, 16)
            }

            Divider()

            HStack {
                Button("Cancel") { dismiss() }.buttonStyle(.bordered)
                Spacer()
                Button("Save") {
                    let skill = AgentSkill(
                        id: skillID,
                        name: name.trimmingCharacters(in: .whitespaces),
                        skillDescription: skillDescription.trimmingCharacters(in: .whitespaces),
                        content: content,
                        isActive: isActive,
                        createdAt: createdAt,
                        updatedAt: Date()
                    )
                    didSave = true
                    onSave(skill)
                    dismiss()
                }
                .buttonStyle(.borderedProminent)
                .disabled(!isValid)
                .keyboardShortcut(.return, modifiers: [.command])
            }
            .padding(.horizontal, 24).padding(.vertical, 12)
        }
        .frame(minWidth: 560, idealWidth: 640, minHeight: 640, idealHeight: 760)
        .onAppear {
            skillFiles = AgentSkillFilesService.shared.listFiles(for: skillID)
        }
        .onDisappear {
            // If this was a new skill that was cancelled, clean up any files the user added
            if isNew && !didSave {
                AgentSkillFilesService.shared.deleteAll(for: skillID)
            }
        }
    }

    // MARK: - Files Section

    @ViewBuilder
    private var filesSection: some View {
        VStack(alignment: .leading, spacing: 8) {
            HStack {
                Text("Files")
                    .font(.system(size: 13, weight: .semibold))
                Spacer()
                Button {
                    addFiles()
                } label: {
                    Label("Add File", systemImage: "plus")
                        .font(.caption)
                }
                .buttonStyle(.bordered)
                .controlSize(.small)
            }

            if skillFiles.isEmpty {
                Text("No files attached. Add JSON, YAML, CSV or TXT files to inject data into the system prompt alongside this skill.")
                    .font(.caption).foregroundStyle(.secondary)
                    .frame(maxWidth: .infinity, alignment: .leading)
                    .padding(10)
                    .background(.secondary.opacity(0.05), in: RoundedRectangle(cornerRadius: 6))
            } else {
                VStack(spacing: 0) {
                    ForEach(Array(skillFiles.enumerated()), id: \.element) { idx, url in
                        HStack(spacing: 8) {
                            Image(systemName: "doc")
                                .font(.caption).foregroundStyle(.secondary)
                            Text(url.lastPathComponent)
                                .font(.system(size: 12)).lineLimit(1)
                            Spacer()
                            if let size = AgentSkillFilesService.shared.fileSize(at: url) {
                                Text(formatBytes(size))
                                    .font(.caption2).foregroundStyle(.tertiary)
                            }
                            Button {
                                AgentSkillFilesService.shared.deleteFile(at: url)
                                skillFiles = AgentSkillFilesService.shared.listFiles(for: skillID)
                            } label: {
                                Image(systemName: "trash").font(.caption2)
                            }
                            .buttonStyle(.plain)
                            .foregroundStyle(.red)
                            .help("Remove file")
                        }
                        .padding(.horizontal, 10)
                        .padding(.vertical, 7)
                        if idx < skillFiles.count - 1 { Divider() }
                    }
                }
                .background(Color(nsColor: .textBackgroundColor), in: RoundedRectangle(cornerRadius: 6))
                .overlay(RoundedRectangle(cornerRadius: 6)
                    .stroke(Color(nsColor: .separatorColor), lineWidth: 1))
            }

            if hasLargeFile {
                HStack(spacing: 6) {
                    Image(systemName: "exclamationmark.triangle.fill").foregroundStyle(.orange)
                    Text("Large files inflate the system prompt and may hit token limits.")
                        .font(.caption)
                }
                .padding(8)
                .background(.orange.opacity(0.08), in: RoundedRectangle(cornerRadius: 6))
            }

            HStack(spacing: 6) {
                Image(systemName: "info.circle").foregroundStyle(.secondary).font(.caption2)
                Text("Text files are injected into the system prompt alongside the skill.")
                    .font(.caption2).foregroundStyle(.secondary)
            }
        }
    }

    // MARK: - Actions

    private func addFiles() {
        let panel = NSOpenPanel()
        panel.allowsMultipleSelection = true
        panel.canChooseFiles = true
        panel.canChooseDirectories = false
        panel.allowedContentTypes = [
            .plainText, .json, .xml, .commaSeparatedText,
            UTType(filenameExtension: "yaml") ?? .plainText,
            UTType(filenameExtension: "toml") ?? .plainText,
            UTType(filenameExtension: "md") ?? .plainText
        ]
        panel.message = "Select text data files (JSON, YAML, CSV, TXT, MD…)"
        guard panel.runModal() == .OK else { return }
        for url in panel.urls {
            try? AgentSkillFilesService.shared.addFile(from: url, to: skillID)
        }
        skillFiles = AgentSkillFilesService.shared.listFiles(for: skillID)
    }

    private func formatBytes(_ bytes: Int) -> String {
        if bytes < 1024 { return "\(bytes) B" }
        if bytes < 1_048_576 { return String(format: "%.1f KB", Double(bytes) / 1024) }
        return String(format: "%.1f MB", Double(bytes) / 1_048_576)
    }
}

#Preview {
    AgentSkillEditorSheet { _ in }
}
