import AppKit
import SwiftUI

// MARK: - The chip row

/// A comment's or the draft's `attachments/`, as **chips** (05-card-window.md ▸ The comments column:
/// "attachment chips when its `attachments/` is non-empty (Quick Look, the sidebar section's
/// pattern)").
///
/// ### Chips, not rows — and the same parts
///
/// The sidebar's inventory is a vertical list because it is a *complete* listing of a card's files in
/// a narrow column. A comment's files are a handful of things said in passing, so they wrap
/// horizontally under the text that mentions them. What does not change is the anatomy — the small
/// QuickLook thumbnail with its Finder-icon fallback, the middle-truncated filename, Space/click to
/// Quick Look — because that is what "the sidebar section's pattern" names, and a user who has
/// learned the sidebar has learned this.
///
/// ### The one difference that is a rule
///
/// > Chips on an authoring surface carry remove (to the **system** Trash — the sidebar row's rule); a
/// > posted comment's chips are read-only, Quick Look only — Edit the comment to change its files.
///
/// `onRemove` is that sentence: `nil` is a posted comment's chip and there is no remove affordance at
/// all — not a disabled one, because the file is not un-removable, it is simply not removable *here*.
struct CommentAttachmentChips: View {

    let names: [String]
    /// Where each name lives — the pane resolves it, since only it knows which authoring surface (or
    /// which posted comment) these belong to.
    let url: (String) -> URL?
    let thumbnails: AttachmentThumbnailCache
    /// `nil` on a posted comment's read-only chips; the remove write on an authoring surface's.
    var onRemove: ((String) -> Void)?

    @Environment(\.displayScale) private var displayScale

    private var pointSize: CGFloat { CardWindowMetrics.bodyPointSize }

    var body: some View {
        // Wrapping, because a chip row is horizontal and a comment can carry more files than fit:
        // `Layout`-free wrapping through a flexible `WrappingHStack` would be a new layout to own, so
        // this leans on SwiftUI's own — a `LazyVGrid` with adaptive columns wraps and needs nothing.
        LazyVGrid(
            columns: [GridItem(
                .adaptive(minimum: CardWindowMetrics.commentChipMinimumWidth(bodyPointSize: pointSize)),
                spacing: CardWindowMetrics.previewPadding(bodyPointSize: pointSize),
                alignment: .leading
            )],
            alignment: .leading,
            spacing: CardWindowMetrics.previewPadding(bodyPointSize: pointSize)
        ) {
            ForEach(names, id: \.self) { name in
                chip(name)
            }
        }
        .frame(maxWidth: .infinity, alignment: .leading)
    }

    @ViewBuilder
    private func chip(_ name: String) -> some View {
        let fileURL = url(name)
        let side = CardWindowMetrics.attachmentThumbnailSide(bodyPointSize: pointSize)
        let padding = CardWindowMetrics.previewPadding(bodyPointSize: pointSize)

        HStack(spacing: padding) {
            CommentChipThumbnail(
                url: fileURL,
                side: side,
                thumbnails: thumbnails,
                displayScale: displayScale
            )
            .frame(width: side, height: side)

            Text(name)
                .font(.caption)
                .lineLimit(1)
                .truncationMode(.middle)

            if let onRemove {
                Button {
                    onRemove(name)
                } label: {
                    Image(systemName: "xmark.circle.fill")
                        .font(.caption)
                        .foregroundStyle(.secondary)
                }
                .buttonStyle(.plain)
                .help("Remove")
                .accessibilityLabel("Remove \(name)")
            }
        }
        .padding(.horizontal, padding)
        .padding(.vertical, padding / 2)
        .background(.quaternary, in: Capsule(style: .continuous))
        .contentShape(Capsule(style: .continuous))
        // Quick Look on click, the chip being small enough that a select-then-Space dance would be
        // ceremony over a thing you can already point at. The panel's ←/→ then walk this surface's
        // files, exactly as Space over the sidebar walks the card's.
        .onTapGesture {
            quickLook(name)
        }
        .contextMenu {
            Button("Open") {
                guard let fileURL else { return }
                NSWorkspace.shared.open(fileURL)
            }
            Button("Reveal in Finder") {
                guard let fileURL else { return }
                NSWorkspace.shared.activateFileViewerSelecting([fileURL])
            }
            if let onRemove {
                Divider()
                Button("Remove") { onRemove(name) }
            }
        }
        .help(name)
        .accessibilityElement(children: .combine)
        .accessibilityLabel(name)
    }

    private func quickLook(_ name: String) {
        guard let index = names.firstIndex(of: name) else { return }
        AttachmentQuickLook.shared.toggle(urls: names.compactMap(url), at: index)
    }
}

// MARK: - One chip's thumbnail

/// The generated QuickLook thumbnail once there is one, the file's Finder icon until then — and
/// forever, for anything QuickLook declines. `AttachmentRow`'s own fallback ladder, shared by being
/// written the same way rather than by being the same view: the sidebar's row is a row, this is a
/// chip, and only the picture is common.
private struct CommentChipThumbnail: View {

    let url: URL?
    let side: CGFloat
    let thumbnails: AttachmentThumbnailCache
    let displayScale: CGFloat

    private var slot: AttachmentThumbnailKey.Slot? {
        url.map { AttachmentThumbnailKey.Slot(path: $0.path, side: side) }
    }

    var body: some View {
        content
            .task(id: url?.path) {
                guard let slot, let url else { return }
                await thumbnails.load(slot, url: url, scale: displayScale)
            }
    }

    @ViewBuilder
    private var content: some View {
        if let slot, let image = thumbnails.thumbnail(for: slot) {
            Image(decorative: image, scale: displayScale)
                .resizable()
                .aspectRatio(contentMode: .fit)
        } else if let url {
            Image(nsImage: thumbnails.icon(forFileAt: url))
                .resizable()
                .aspectRatio(contentMode: .fit)
        } else {
            Image(systemName: "doc")
                .foregroundStyle(.secondary)
        }
    }
}
