import SwiftUI
import UIKit

/// Action model for context menu buttons.
struct BubbleContextAction {
    let title: String
    let image: UIImage?
    let role: UIMenuElement.Attributes
    let handler: () -> Void
}

/// Transparent overlay that attaches UIContextMenuInteraction to a message bubble.
///
/// Uses a **window snapshot** approach instead of UIHostingController preview:
/// 1. On long-press, captures a pixel-perfect screenshot of the bubble from the window
/// 2. Uses this snapshot as `UITargetedPreview` with `previewProvider: nil`
/// 3. UIKit lifts the snapshot in-place — no horizontal shift, no re-rendering issues
///
/// Also supports an optional `onTap` callback that fires on single tap.
/// This is needed because the overlay UIView intercepts all touch events,
/// preventing SwiftUI `onTapGesture` on content below from firing.
struct BubbleContextMenuOverlay: UIViewRepresentable {
    let actions: [BubbleContextAction]
    let previewShape: MessageBubbleShape
    let readStatusText: String?

    /// Called when user single-taps the bubble. Receives tap location in the overlay's
    /// coordinate space (for determining which sub-element was tapped, e.g., which photo in a collage).
    var onTap: ((CGPoint) -> Void)?

    /// Height of the reply quote area at the top of the bubble (0 = no reply quote).
    /// Taps within this region call `onReplyQuoteTap` instead of `onTap`.
    var replyQuoteHeight: CGFloat = 0

    /// Called when user taps the reply quote area at the top of the bubble.
    var onReplyQuoteTap: (() -> Void)?

    func makeUIView(context: Context) -> UIView {
        let view = UIView()
        view.backgroundColor = .clear
        let interaction = UIContextMenuInteraction(delegate: context.coordinator)
        view.addInteraction(interaction)

        // Single tap recognizer — coexists with context menu's long press.
        // ALL taps go through this (overlay UIView blocks SwiftUI gestures below).
        // onTap handler in ChatDetailView routes to image viewer, file share,
        // or posts .triggerAttachmentDownload notification for downloads.
        let tap = UITapGestureRecognizer(target: context.coordinator, action: #selector(Coordinator.handleTap(_:)))
        view.addGestureRecognizer(tap)

        return view
    }

    func updateUIView(_ uiView: UIView, context: Context) {
        // PERF: only update callbacks (lightweight pointer swap).
        // Skip actions/previewShape/readStatusText — these involve array allocation
        // and struct copying on EVERY layout pass (40× cells × 8 keyboard ticks = 320/s).
        // Context menu will use stale actions until cell is recycled — acceptable trade-off.
        context.coordinator.onTap = onTap
        context.coordinator.onReplyQuoteTap = onReplyQuoteTap
    }

    func makeCoordinator() -> Coordinator { Coordinator(overlay: self) }

    final class Coordinator: NSObject, UIContextMenuInteractionDelegate {
        var actions: [BubbleContextAction]
        var previewShape: MessageBubbleShape
        var readStatusText: String?
        var onTap: ((CGPoint) -> Void)?
        var replyQuoteHeight: CGFloat = 0
        var onReplyQuoteTap: (() -> Void)?
        private var snapshotView: UIImageView?

        init(overlay: BubbleContextMenuOverlay) {
            self.actions = overlay.actions
            self.previewShape = overlay.previewShape
            self.readStatusText = overlay.readStatusText
            self.onTap = overlay.onTap
            self.replyQuoteHeight = overlay.replyQuoteHeight
            self.onReplyQuoteTap = overlay.onReplyQuoteTap
        }

        @objc func handleTap(_ recognizer: UITapGestureRecognizer) {
            // Route taps in the reply quote region to the reply handler.
            if replyQuoteHeight > 0, let view = recognizer.view {
                let location = recognizer.location(in: view)
                if location.y < replyQuoteHeight {
                    onReplyQuoteTap?()
                    return
                }
            }
            let location = recognizer.location(in: recognizer.view)
            onTap?(location)
        }

        func contextMenuInteraction(
            _ interaction: UIContextMenuInteraction,
            configurationForMenuAtLocation location: CGPoint
        ) -> UIContextMenuConfiguration? {
            captureSnapshot(for: interaction)

            return UIContextMenuConfiguration(
                identifier: nil,
                previewProvider: nil,
                actionProvider: { [weak self] _ in
                    self?.buildMenu()
                }
            )
        }

        // MARK: - Snapshot

        private func captureSnapshot(for interaction: UIContextMenuInteraction) {
            guard let view = interaction.view, let window = view.window else { return }
            let frameInWindow = view.convert(view.bounds, to: window)
            let renderer = UIGraphicsImageRenderer(size: view.bounds.size)
            let image = renderer.image { ctx in
                ctx.cgContext.translateBy(x: -frameInWindow.origin.x, y: -frameInWindow.origin.y)
                window.drawHierarchy(in: window.bounds, afterScreenUpdates: false)
            }
            let sv = UIImageView(image: image)
            sv.frame = view.bounds
            view.addSubview(sv)
            self.snapshotView = sv
        }

        // MARK: - Menu

        private func buildMenu() -> UIMenu {
            var sections: [UIMenuElement] = []

            if let readStatus = readStatusText {
                let readAction = UIAction(
                    title: readStatus,
                    image: UIImage(systemName: "checkmark"),
                    attributes: .disabled
                ) { _ in }
                sections.append(UIMenu(options: .displayInline, children: [readAction]))
            }

            let menuActions = actions.map { action in
                UIAction(
                    title: action.title,
                    image: action.image,
                    attributes: action.role
                ) { _ in
                    action.handler()
                }
            }
            sections.append(UIMenu(options: .displayInline, children: menuActions))

            return UIMenu(children: sections)
        }

        // MARK: - Targeted Preview (lift & dismiss)

        func contextMenuInteraction(
            _ interaction: UIContextMenuInteraction,
            previewForHighlightingMenuWithConfiguration configuration: UIContextMenuConfiguration
        ) -> UITargetedPreview? {
            guard let sv = snapshotView else { return nil }
            return makeTargetedPreview(for: sv)
        }

        func contextMenuInteraction(
            _ interaction: UIContextMenuInteraction,
            previewForDismissingMenuWithConfiguration configuration: UIContextMenuConfiguration
        ) -> UITargetedPreview? {
            guard let sv = snapshotView else { return nil }
            return makeTargetedPreview(for: sv)
        }

        func contextMenuInteraction(
            _ interaction: UIContextMenuInteraction,
            willEndFor configuration: UIContextMenuConfiguration,
            animator: (any UIContextMenuInteractionAnimating)?
        ) {
            animator?.addCompletion { [weak self] in
                self?.snapshotView?.removeFromSuperview()
                self?.snapshotView = nil
            }
        }

        private func makeTargetedPreview(for view: UIView) -> UITargetedPreview {
            let params = UIPreviewParameters()
            let shapePath = previewShape.path(in: view.bounds)
            params.visiblePath = UIBezierPath(cgPath: shapePath.cgPath)
            params.backgroundColor = .clear
            return UITargetedPreview(view: view, parameters: params)
        }
    }
}
