import UIKit

// MARK: - Menu Item Model

struct TelegramContextMenuItem {
    let title: String
    let iconName: String
    let isDestructive: Bool
    let handler: () -> Void
}

// MARK: - Shared Builder

enum TelegramContextMenuBuilder {

    static func menuItems(
        for message: ChatMessage,
        actions: MessageCellActions,
        isSavedMessages: Bool,
        isSystemAccount: Bool
    ) -> [TelegramContextMenuItem] {
        var items: [TelegramContextMenuItem] = []

        let isAvatar = message.attachments.contains(where: { $0.type == .avatar })
        let canReplyForward = !isSavedMessages && !isSystemAccount && !isAvatar

        if canReplyForward {
            items.append(TelegramContextMenuItem(
                title: "Reply",
                iconName: "arrowshape.turn.up.left",
                isDestructive: false,
                handler: { actions.onReply(message) }
            ))
        }

        // Desktop parity: detect #group: invite strings and offer "Join/Open Group" action.
        if message.text.hasPrefix("#group:") {
            let isJoined: Bool
            if let parsed = GroupRepository.parseInviteStringPure(message.text) {
                isJoined = GroupRepository.shared.hasGroup(for: parsed.groupId)
            } else {
                isJoined = false
            }
            items.append(TelegramContextMenuItem(
                title: isJoined ? "Open Group" : "Join Group",
                iconName: isJoined ? "person.2" : "person.2.badge.plus",
                isDestructive: false,
                handler: {
                    if isJoined, let parsed = GroupRepository.parseInviteStringPure(message.text) {
                        actions.onGroupInviteOpen("#group:\(parsed.groupId)")
                    } else {
                        actions.onGroupInviteTap(message.text)
                    }
                }
            ))
        }

        if !message.text.isEmpty {
            items.append(TelegramContextMenuItem(
                title: "Copy",
                iconName: "doc.on.doc",
                isDestructive: false,
                handler: { actions.onCopy(message.text) }
            ))
        }

        if canReplyForward {
            items.append(TelegramContextMenuItem(
                title: "Forward",
                iconName: "arrowshape.turn.up.right",
                isDestructive: false,
                handler: { actions.onForward(message) }
            ))
        }

        items.append(TelegramContextMenuItem(
            title: "Select",
            iconName: "checkmark.circle",
            isDestructive: false,
            handler: { actions.onEnterSelection(message) }
        ))

        items.append(TelegramContextMenuItem(
            title: "Delete",
            iconName: "trash",
            isDestructive: true,
            handler: { actions.onDelete(message) }
        ))

        return items
    }
}

// MARK: - Context Menu Controller

/// Full-screen overlay replicating Telegram iOS context menu.
/// Telegram source: ContextController.swift, DefaultDarkPresentationTheme.swift
final class TelegramContextMenuController: UIView {

    // MARK: - Constants

    private static let menuWidth: CGFloat = 250
    private static let menuItemHeight: CGFloat = 44
    private static let menuGap: CGFloat = 8
    private static let panDismissThreshold: CGFloat = 100
    private static let panVelocityThreshold: CGFloat = 800

    // MARK: - Subviews

    /// Telegram: UIVisualEffectView(.dark) + UIColor(rgb: 0x000000, alpha: 0.6) dim
    private let backgroundBlurView = UIVisualEffectView(effect: UIBlurEffect(style: .dark))
    private let dimView = UIView()
    private let snapshotContainer = UIView()
    private let snapshotImageView = UIImageView()
    private let menuCard: TelegramContextMenuCardView

    // MARK: - State

    private let sourceFrame: CGRect
    private let isOutgoing: Bool
    private var onDismiss: (() -> Void)?
    private var isDismissing = false
    private var panStartSnapshotCenter: CGPoint = .zero
    private var panStartMenuCenter: CGPoint = .zero

    // MARK: - Init

    private init(
        snapshot: UIImage,
        sourceFrame: CGRect,
        bubblePath: UIBezierPath,
        items: [TelegramContextMenuItem],
        isOutgoing: Bool,
        onDismiss: (() -> Void)?
    ) {
        self.sourceFrame = sourceFrame
        self.isOutgoing = isOutgoing
        self.onDismiss = onDismiss
        self.menuCard = TelegramContextMenuCardView(items: items)
        super.init(frame: .zero)
        buildHierarchy(snapshot: snapshot, bubblePath: bubblePath)
    }

    @available(*, unavailable)
    required init?(coder: NSCoder) { fatalError() }

    // MARK: - Public API

    @MainActor
    static func present(
        snapshot: UIImage,
        sourceFrame: CGRect,
        bubblePath: UIBezierPath,
        items: [TelegramContextMenuItem],
        isOutgoing: Bool,
        onDismiss: (() -> Void)? = nil
    ) {
        guard !items.isEmpty else { return }
        guard let scene = UIApplication.shared.connectedScenes
            .first(where: { $0.activationState == .foregroundActive }) as? UIWindowScene,
              let window = scene.windows.first(where: \.isKeyWindow)
        else { return }

        // Telegram always dismisses keyboard before showing context menu
        window.endEditing(true)

        let overlay = TelegramContextMenuController(
            snapshot: snapshot,
            sourceFrame: sourceFrame,
            bubblePath: bubblePath,
            items: items,
            isOutgoing: isOutgoing,
            onDismiss: onDismiss
        )
        overlay.frame = window.bounds
        overlay.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        window.addSubview(overlay)
        overlay.performPresentation()
    }

    // MARK: - Build Hierarchy

    private func buildHierarchy(snapshot: UIImage, bubblePath: UIBezierPath) {
        // 1. Full-screen blur (Telegram: custom zoom blur + .systemMaterialDark)
        backgroundBlurView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        addSubview(backgroundBlurView)

        // 2. Dim overlay on top of blur (Telegram: UIColor(rgb: 0x000000, alpha: 0.6))
        dimView.backgroundColor = UIColor(white: 0, alpha: 0.6)
        dimView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        addSubview(dimView)

        // 2. Snapshot container
        snapshotContainer.backgroundColor = .clear
        snapshotImageView.image = snapshot
        snapshotImageView.contentMode = .scaleToFill

        let mask = CAShapeLayer()
        mask.path = bubblePath.cgPath
        snapshotImageView.layer.mask = mask

        snapshotContainer.layer.shadowColor = UIColor.black.cgColor
        snapshotContainer.layer.shadowOpacity = 0.3
        snapshotContainer.layer.shadowRadius = 12
        snapshotContainer.layer.shadowOffset = CGSize(width: 0, height: 4)
        snapshotContainer.layer.shadowPath = bubblePath.cgPath

        snapshotContainer.addSubview(snapshotImageView)
        addSubview(snapshotContainer)

        // 3. Menu card
        menuCard.onItemSelected = { [weak self] in
            self?.performDismissal()
        }
        addSubview(menuCard)

        // 4. Gestures
        let tap = UITapGestureRecognizer(target: self, action: #selector(tapDismiss(_:)))
        tap.delegate = self
        addGestureRecognizer(tap)

        let pan = UIPanGestureRecognizer(target: self, action: #selector(panDismiss(_:)))
        addGestureRecognizer(pan)
    }

    // MARK: - Layout

    /// Positions snapshot + menu card. MUST be called with identity transforms.
    private func layoutContent() {
        let windowInsets = window?.safeAreaInsets ?? .zero
        let safeTop = max(windowInsets.top, 20)
        let safeBottom = max(windowInsets.bottom, 20)

        // Snapshot at original bubble position
        snapshotContainer.frame = sourceFrame
        snapshotImageView.frame = snapshotContainer.bounds

        // Menu dimensions
        let menuW = Self.menuWidth
        let menuH = CGFloat(menuCard.itemCount) * Self.menuItemHeight
        let gap = Self.menuGap

        // Vertical: prefer below, then above, then shift
        let belowSpace = bounds.height - safeBottom - sourceFrame.maxY
        let aboveSpace = sourceFrame.minY - safeTop

        var menuY: CGFloat
        var menuAbove = false

        if belowSpace >= menuH + gap {
            menuY = sourceFrame.maxY + gap
        } else if aboveSpace >= menuH + gap {
            menuY = sourceFrame.minY - gap - menuH
            menuAbove = true
        } else {
            // Shift snapshot up to make room below
            let shift = (menuH + gap) - belowSpace
            snapshotContainer.frame.origin.y -= shift
            menuY = snapshotContainer.frame.maxY + gap
        }

        // Horizontal: align with bubble side
        let menuX: CGFloat
        if isOutgoing {
            menuX = min(sourceFrame.maxX - menuW, bounds.width - menuW - 8)
        } else {
            menuX = max(sourceFrame.minX, 8)
        }

        menuCard.frame = CGRect(x: menuX, y: menuY, width: menuW, height: menuH)

        // Anchor for scale animation — scale from the edge nearest to the bubble
        let anchorX: CGFloat = isOutgoing ? 1.0 : 0.0
        let anchorY: CGFloat = menuAbove ? 1.0 : 0.0
        setAnchorPointPreservingPosition(CGPoint(x: anchorX, y: anchorY), for: menuCard)
    }

    /// Changes anchor point without moving the view.
    private func setAnchorPointPreservingPosition(_ anchor: CGPoint, for view: UIView) {
        let oldAnchor = view.layer.anchorPoint
        let delta = CGPoint(
            x: (anchor.x - oldAnchor.x) * view.bounds.width,
            y: (anchor.y - oldAnchor.y) * view.bounds.height
        )
        view.layer.anchorPoint = anchor
        view.layer.position = CGPoint(
            x: view.layer.position.x + delta.x,
            y: view.layer.position.y + delta.y
        )
    }

    // MARK: - Presentation

    private func performPresentation() {
        // Layout with identity transforms (CRITICAL — frame + transform interaction)
        backgroundBlurView.frame = bounds
        dimView.frame = bounds
        layoutContent()

        // Set initial pre-animation state
        backgroundBlurView.alpha = 0
        dimView.alpha = 0
        snapshotContainer.alpha = 0
        snapshotContainer.transform = CGAffineTransform(scaleX: 0.96, y: 0.96)
        menuCard.alpha = 0
        menuCard.transform = CGAffineTransform(scaleX: 0.01, y: 0.01)

        // Telegram spring: mass 5, stiffness 900, damping 88
        let spring = UISpringTimingParameters(
            mass: 5.0, stiffness: 900.0, damping: 88.0, initialVelocity: .zero
        )

        let animator = UIViewPropertyAnimator(duration: 0, timingParameters: spring)
        animator.addAnimations {
            self.backgroundBlurView.alpha = 1
            self.dimView.alpha = 1
            self.snapshotContainer.alpha = 1
            self.snapshotContainer.transform = .identity
            self.menuCard.alpha = 1
            self.menuCard.transform = .identity
        }
        animator.startAnimation()
    }

    // MARK: - Dismissal

    private func performDismissal() {
        guard !isDismissing else { return }
        isDismissing = true

        UIView.animate(withDuration: 0.2, delay: 0, options: .curveEaseIn) {
            self.backgroundBlurView.alpha = 0
            self.dimView.alpha = 0
            self.snapshotContainer.alpha = 0
            self.menuCard.alpha = 0
            self.menuCard.transform = CGAffineTransform(scaleX: 0.01, y: 0.01)
        } completion: { [weak self] _ in
            self?.onDismiss?()
            self?.removeFromSuperview()
        }
    }

    // MARK: - Gestures

    @objc private func tapDismiss(_ g: UITapGestureRecognizer) {
        performDismissal()
    }

    @objc private func panDismiss(_ g: UIPanGestureRecognizer) {
        switch g.state {
        case .began:
            panStartSnapshotCenter = snapshotContainer.center
            panStartMenuCenter = menuCard.center
        case .changed:
            let dy = max(g.translation(in: self).y, 0) * 0.6
            snapshotContainer.center = CGPoint(x: panStartSnapshotCenter.x, y: panStartSnapshotCenter.y + dy)
            menuCard.center = CGPoint(x: panStartMenuCenter.x, y: panStartMenuCenter.y + dy)
            dimView.alpha = max(1 - dy / 300, 0.3)
        case .ended, .cancelled:
            if g.translation(in: self).y > Self.panDismissThreshold || g.velocity(in: self).y > Self.panVelocityThreshold {
                performDismissal()
            } else {
                UIView.animate(withDuration: 0.3, delay: 0, usingSpringWithDamping: 0.8, initialSpringVelocity: 0) {
                    self.snapshotContainer.center = self.panStartSnapshotCenter
                    self.menuCard.center = self.panStartMenuCenter
                    self.dimView.alpha = 1
                }
            }
        default: break
        }
    }
}

// MARK: - UIGestureRecognizerDelegate

extension TelegramContextMenuController: UIGestureRecognizerDelegate {
    func gestureRecognizer(_ g: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
        guard g is UITapGestureRecognizer else { return true }
        let loc = touch.location(in: self)
        return !snapshotContainer.frame.contains(loc) && !menuCard.frame.contains(loc)
    }
}

// MARK: - Snapshot Helper

extension TelegramContextMenuController {
    static func captureSnapshot(of sourceView: UIView) -> (image: UIImage, frame: CGRect)? {
        guard let window = sourceView.window else { return nil }
        let frameInWindow = sourceView.convert(sourceView.bounds, to: window)
        let renderer = UIGraphicsImageRenderer(size: sourceView.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)
        }
        return (image, frameInWindow)
    }
}
