import SwiftUI
import UIKit

// MARK: - SwiftUI Bridge

/// UIViewRepresentable wrapping the SwiftUI composer in a UIKit container.
/// Bottom constraint animated via UIView.animate inside keyboard notification handler
/// — same CA transaction as keyboard, zero gap guaranteed.
/// iOS < 26 only — iOS 26+ uses SwiftUI's native keyboard handling.
struct ComposerUIKitContainer<Content: View>: UIViewRepresentable {
    let content: Content
    let onHeightChange: (CGFloat) -> Void

    func makeUIView(context: Context) -> ComposerHostView {
        let view = ComposerHostView(
            content: AnyView(content),
            onHeightChange: onHeightChange
        )
        KeyboardTracker.shared.composerHostView = view
        // Match current keyboard state if already open.
        view.setKeyboardOffset(KeyboardTracker.shared.keyboardPadding)
        return view
    }

    func updateUIView(_ view: ComposerHostView, context: Context) {
        view.updateContent(AnyView(content))
        // NEVER touch constraints here — UIKit animation manages positioning.
    }

    static func dismantleUIView(_ view: ComposerHostView, coordinator: ()) {
        KeyboardTracker.shared.composerHostView = nil
    }
}

// MARK: - UIKit Host View

/// Full-overlay-size container. Hosting controller pinned to bottom edge.
/// `setKeyboardOffset(_:)` changes the bottom constraint — called from
/// UIView.animate block inside keyboard notification handler (same CA transaction).
/// `hitTest` passes through touches on transparent area above composer.
final class ComposerHostView: UIView {
    private let hostingController: UIHostingController<AnyView>
    private let onHeightChange: (CGFloat) -> Void
    private var lastReportedHeight: CGFloat = 0
    private var bottomConstraint: NSLayoutConstraint!

    init(content: AnyView, onHeightChange: @escaping (CGFloat) -> Void) {
        self.onHeightChange = onHeightChange
        hostingController = UIHostingController(rootView: content)
        super.init(frame: .zero)

        backgroundColor = .clear
        hostingController.view.backgroundColor = .clear
        if #available(iOS 16.0, *) {
            hostingController.sizingOptions = .intrinsicContentSize
        }
        if #available(iOS 16.4, *) {
            hostingController.safeAreaRegions = []
        }
        hostingController.view.setContentHuggingPriority(.required, for: .vertical)
        hostingController.view.setContentCompressionResistancePriority(
            .required, for: .vertical
        )

        addSubview(hostingController.view)
        hostingController.view.translatesAutoresizingMaskIntoConstraints = false

        bottomConstraint = hostingController.view.bottomAnchor.constraint(
            equalTo: bottomAnchor
        )

        NSLayoutConstraint.activate([
            hostingController.view.leadingAnchor.constraint(equalTo: leadingAnchor),
            hostingController.view.trailingAnchor.constraint(equalTo: trailingAnchor),
            bottomConstraint,
        ])
    }

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

    func updateContent(_ content: AnyView) {
        hostingController.rootView = content
        // Force hosting controller to re-measure after content change —
        // nested UIViewRepresentable (ChatTextInput) size changes may not
        // propagate through the hosting controller automatically.
        hostingController.view.invalidateIntrinsicContentSize()
    }

    /// Moves composer up by `offset` points from the bottom.
    /// When called inside UIView.animate — creates CA animation in same transaction.
    /// When called directly (KVO) — immediate constraint update.
    func setKeyboardOffset(_ offset: CGFloat) {
        bottomConstraint.constant = -offset
        layoutIfNeeded()
    }

    /// Pass through touches on transparent area — only the composer handles taps.
    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        let result = super.hitTest(point, with: event)
        return result === self ? nil : result
    }

    override func layoutSubviews() {
        super.layoutSubviews()
        let height = hostingController.view.bounds.height
        guard height > 0, abs(height - lastReportedHeight) > 1 else { return }
        lastReportedHeight = height
        DispatchQueue.main.async { [onHeightChange, height] in
            onHeightChange(height)
        }
    }
}
