import IndieSync
import SwiftData
import SwiftUI

/// Browse notes by project — the manual-organization counterpart to the
/// automatic context ranking, and a sibling to `TagsView`. Unlike tags a note
/// carries at most one project, so browsing here is filing rather than
/// cross-cutting search.
struct ProjectsView: View {
    @Query(sort: \NoteEntity.createdAt, order: .reverse) private var notes: [NoteEntity]

    @AppStorage(ProjectsNaming.storageKey) private var namingRaw = ProjectsNaming.projects.rawValue
    private var naming: ProjectsNaming { ProjectsNaming(rawValue: namingRaw) ?? .projects }

    var body: some View {
        NavigationStack {
            List {
                ForEach(projectCounts, id: \.project) { entry in
                    NavigationLink(value: entry.project) {
                        HStack {
                            Label(entry.project, systemImage: "folder")
                            Spacer()
                            Text("\(entry.count)")
                                .foregroundStyle(.secondary)
                                .monospacedDigit()
                        }
                    }
                }
            }
            .navigationTitle(naming.plural)
            .navigationDestination(for: String.self) { project in
                ProjectNotesView(project: project)
            }
            .overlay {
                if projectCounts.isEmpty {
                    ContentUnavailableView(
                        "No \(naming.plural.lowercased()) yet",
                        systemImage: "folder",
                        description: Text("Swipe a note in the list to file it under a \(naming.singular.lowercased()).")
                    )
                }
            }
        }
    }

    private var projectCounts: [(project: String, count: Int)] {
        var counts: [String: Int] = [:]
        for note in notes where note.isLive {
            guard let project = note.project else { continue }
            counts[project, default: 0] += 1
        }
        return counts
            .map { (project: $0.key, count: $0.value) }
            .sorted { $0.count == $1.count ? $0.project < $1.project : $0.count > $1.count }
    }
}

struct ProjectNotesView: View {
    let project: String

    @Environment(SyncEngine.self) private var syncEngine
    @Query(sort: \NoteEntity.createdAt, order: .reverse) private var notes: [NoteEntity]

    @State private var composing = false
    @State private var editingNote: NoteEntity?
    @State private var assigningNote: NoteEntity?

    @AppStorage(ProjectsNaming.storageKey) private var namingRaw = ProjectsNaming.projects.rawValue
    private var naming: ProjectsNaming { ProjectsNaming(rawValue: namingRaw) ?? .projects }

    var body: some View {
        List {
            ForEach(notes.filter { $0.isLive && $0.project == project }) { note in
                row(note)
            }
        }
        .navigationTitle(project)
        .toolbar {
            ToolbarItem(placement: .primaryAction) {
                Button {
                    composing = true
                } label: {
                    Label("New Note", systemImage: "square.and.pencil")
                }
            }
        }
        .sheet(isPresented: $composing) {
            NoteEditorView(mode: .create, defaultProject: project)
        }
        .sheet(item: $editingNote) { note in
            NoteEditorView(mode: .edit(note))
        }
        .sheet(item: $assigningNote) { note in
            AssignProjectSheet(note: note)
        }
    }

    private func row(_ note: NoteEntity) -> some View {
        Button {
            editingNote = note
        } label: {
            NoteRowView(note: note)
                .frame(maxWidth: .infinity, alignment: .leading)
                .contentShape(Rectangle())
        }
        .buttonStyle(.plain)
        .swipeActions(edge: .leading) {
            Button {
                assigningNote = note
            } label: {
                Label(naming.singular, systemImage: "folder")
            }
            .tint(.indigo)
        }
        .swipeActions(edge: .trailing) {
            Button(role: .destructive) {
                delete(note)
            } label: {
                Label("Delete", systemImage: "trash")
            }
        }
    }

    private func delete(_ note: NoteEntity) {
        guard let id = ULID(string: note.id) else { return }
        Task {
            try? await syncEngine.deleteNote(id: id)
        }
    }
}
