import UIKit

// MARK: - ComposerViewDelegate

@MainActor
protocol ComposerViewDelegate: AnyObject {
    func composerDidTapSend(_ composer: ComposerView)
    func composerDidTapAttach(_ composer: ComposerView)
    func composerTextDidChange(_ composer: ComposerView, text: String)
    func composerFocusDidChange(_ composer: ComposerView, isFocused: Bool)
    func composerHeightDidChange(_ composer: ComposerView, height: CGFloat)
    func composerDidCancelReply(_ composer: ComposerView)
    func composerUserDidType(_ composer: ComposerView)
    func composerKeyboardHeightDidChange(_ composer: ComposerView, height: CGFloat)
}

// MARK: - ComposerView

/// Pure UIKit composer — replaces the SwiftUI UIHostingController.
/// Eliminates UIHostingController sizing bugs during keyboard transitions.
/// Frame-based layout (Telegram pattern), no Auto Layout inside.
final class ComposerView: UIView, UITextViewDelegate {

    weak var delegate: ComposerViewDelegate?

    // MARK: - Public State

    var currentText: String { textView.text ?? "" }

    private(set) var currentHeight: CGFloat = 0

    // MARK: - Subviews

    // Attach button (glass circle, 42×42)
    private let attachButton = UIButton(type: .system)
    private let attachGlass = TelegramGlassUIView(frame: .zero)

    // Input container (glass rounded rect)
    private let inputContainer = UIView()
    private let inputGlass = TelegramGlassUIView(frame: .zero)

    // Reply bar
    private let replyBar = UIView()
    private let replyBlueBar = UIView()
    private let replyTitleLabel = UILabel()
    private let replySenderLabel = UILabel()
    private let replyPreviewLabel = UILabel()
    private let replyCancelButton = UIButton(type: .system)
    private var isReplyVisible = false

    // Text input (reuses ChatInputTextView from ChatTextInput.swift)
    private let textView = ChatInputTextView()

    // Emoji button
    private let emojiButton = UIButton(type: .system)

    // Send button (blue capsule, 38×36)
    private let sendButton = UIButton(type: .system)
    private let sendCapsule = UIView()

    // Mic button (glass circle, 42×42)
    private let micButton = UIButton(type: .system)
    private let micGlass = TelegramGlassUIView(frame: .zero)

    // MARK: - Layout Constants

    private let horizontalPadding: CGFloat = 16
    private let buttonSize: CGFloat = 42
    private let innerSpacing: CGFloat = 6
    private let innerPadding: CGFloat = 3
    private let minInputContainerHeight: CGFloat = 42
    private let sendButtonWidth: CGFloat = 38
    private let sendButtonHeight: CGFloat = 36
    private let textViewMinHeight: CGFloat = 36
    private let maxLines: CGFloat = 5
    private let topPadding: CGFloat = 6
    private let bottomPadding: CGFloat = 12

    // MARK: - State

    private var textViewHeight: CGFloat = 36
    private var isMultiline = false
    private var isSendVisible = false
    private var isUpdatingText = false

    // MARK: - Init

    override init(frame: CGRect) {
        super.init(frame: frame)
        backgroundColor = .clear
        setupSubviews()
    }

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

    // MARK: - Setup

    private func setupSubviews() {
        // --- Attach button ---
        attachGlass.isCircle = true
        attachGlass.isUserInteractionEnabled = false
        attachButton.addSubview(attachGlass)
        let attachIcon = makeIconLayer(
            pathData: TelegramIconPath.paperclip,
            viewBox: CGSize(width: 21, height: 24),
            targetSize: CGSize(width: 21, height: 24),
            color: .white
        )
        attachButton.layer.addSublayer(attachIcon)
        attachButton.tag = 1 // for icon centering in layoutSubviews
        attachButton.addTarget(self, action: #selector(attachTapped), for: .touchUpInside)
        addSubview(attachButton)

        // --- Input container ---
        inputContainer.backgroundColor = .clear
        inputContainer.clipsToBounds = true
        inputGlass.fixedCornerRadius = 21
        inputGlass.isUserInteractionEnabled = false
        inputContainer.addSubview(inputGlass)
        addSubview(inputContainer)

        // --- Reply bar (hidden by default) ---
        replyBar.alpha = 0
        replyBar.isHidden = true

        replyBlueBar.backgroundColor = UIColor(red: 0, green: 0x8B/255.0, blue: 1.0, alpha: 1)
        replyBlueBar.layer.cornerRadius = 1.0
        replyBar.addSubview(replyBlueBar)

        replyTitleLabel.font = .systemFont(ofSize: 14, weight: .medium)
        replyTitleLabel.textColor = .white
        replyTitleLabel.text = "Reply to "
        replyBar.addSubview(replyTitleLabel)

        replySenderLabel.font = .systemFont(ofSize: 14, weight: .medium)
        replySenderLabel.textColor = UIColor(red: 0, green: 0x8B/255.0, blue: 1.0, alpha: 1)
        replyBar.addSubview(replySenderLabel)

        replyPreviewLabel.font = .systemFont(ofSize: 14, weight: .regular)
        replyPreviewLabel.textColor = .white
        replyPreviewLabel.lineBreakMode = .byTruncatingTail
        replyBar.addSubview(replyPreviewLabel)

        let xImage = UIImage(systemName: "xmark", withConfiguration: UIImage.SymbolConfiguration(pointSize: 12, weight: .medium))
        replyCancelButton.setImage(xImage, for: .normal)
        replyCancelButton.tintColor = UIColor(white: 1, alpha: 0.6)
        replyCancelButton.addTarget(self, action: #selector(replyCancelTapped), for: .touchUpInside)
        replyBar.addSubview(replyCancelButton)

        inputContainer.addSubview(replyBar)

        // --- Text view ---
        textView.delegate = self
        textView.font = .systemFont(ofSize: 17, weight: .regular)
        textView.textColor = .white
        textView.backgroundColor = .clear
        textView.tintColor = UIColor(red: 36/255.0, green: 138/255.0, blue: 230/255.0, alpha: 1)
        textView.isScrollEnabled = false
        textView.textContainerInset = UIEdgeInsets(top: 7, left: 2, bottom: 7, right: 0)
        textView.textContainer.lineFragmentPadding = 0
        textView.autocapitalizationType = .sentences
        textView.autocorrectionType = .default
        textView.keyboardAppearance = .dark
        textView.returnKeyType = .default

        textView.placeholderLabel.text = "Message"
        textView.placeholderLabel.font = .systemFont(ofSize: 17, weight: .regular)
        textView.placeholderLabel.textColor = UIColor.white.withAlphaComponent(0.35)

        textView.trackingView.onHeightChange = { [weak self] height in
            guard let self else { return }
            self.delegate?.composerKeyboardHeightDidChange(self, height: height)
        }

        inputContainer.addSubview(textView)

        // --- Emoji button ---
        let emojiIcon = makeIconLayer(
            pathData: TelegramIconPath.emojiMoon,
            viewBox: CGSize(width: 19, height: 19),
            targetSize: CGSize(width: 19, height: 19),
            color: UIColor(white: 1, alpha: 0.6)
        )
        emojiButton.layer.addSublayer(emojiIcon)
        emojiButton.tag = 2
        emojiButton.addTarget(self, action: #selector(emojiTapped), for: .touchUpInside)
        inputContainer.addSubview(emojiButton)

        // --- Send button ---
        sendCapsule.backgroundColor = UIColor(red: 0, green: 0x8B/255.0, blue: 1.0, alpha: 1)
        sendCapsule.isUserInteractionEnabled = false
        sendButton.addSubview(sendCapsule)

        let sendIcon = makeIconLayer(
            pathData: TelegramIconPath.sendPlane,
            viewBox: CGSize(width: 22, height: 19),
            targetSize: CGSize(width: 22, height: 19),
            color: .white
        )
        sendButton.layer.addSublayer(sendIcon)
        sendButton.tag = 3
        sendButton.addTarget(self, action: #selector(sendTapped), for: .touchUpInside)
        sendButton.alpha = 0
        sendButton.transform = CGAffineTransform(scaleX: 0.74, y: 0.74)
        inputContainer.addSubview(sendButton)

        // --- Mic button ---
        micGlass.isCircle = true
        micGlass.isUserInteractionEnabled = false
        micButton.addSubview(micGlass)
        let micIcon = makeIconLayer(
            pathData: TelegramIconPath.microphone,
            viewBox: CGSize(width: 18, height: 24),
            targetSize: CGSize(width: 18, height: 24),
            color: .white
        )
        micButton.layer.addSublayer(micIcon)
        micButton.tag = 4
        micButton.addTarget(self, action: #selector(micTapped), for: .touchUpInside)
        addSubview(micButton)
    }

    // MARK: - Public API

    func setText(_ text: String) {
        guard text != textView.text else { return }
        isUpdatingText = true
        textView.text = text
        textView.placeholderLabel.isHidden = !text.isEmpty
        isUpdatingText = false
        recalculateTextHeight()
        updateSendMicVisibility(animated: false)
    }

    func setReply(senderName: String?, previewText: String?) {
        let shouldShow = senderName != nil
        guard shouldShow != isReplyVisible else { return }
        isReplyVisible = shouldShow

        if shouldShow {
            replySenderLabel.text = senderName
            replyPreviewLabel.text = previewText ?? ""
            replyBar.isHidden = false
        }

        UIView.animate(withDuration: 0.15, delay: 0, options: .curveEaseOut) {
            self.replyBar.alpha = shouldShow ? 1 : 0
            self.replyBar.transform = shouldShow ? .identity : CGAffineTransform(translationX: 0, y: 20)
        } completion: { _ in
            if !shouldShow {
                self.replyBar.isHidden = true
                self.replyBar.transform = .identity
            }
        }

        setNeedsLayout()
        // Report height change after layout
        DispatchQueue.main.async { [weak self] in
            self?.layoutIfNeeded()
            self?.reportHeightIfChanged()
        }
    }

    func setFocused(_ focused: Bool) {
        if focused, !textView.isFirstResponder {
            DispatchQueue.main.async { [weak self] in
                self?.textView.becomeFirstResponder()
            }
        } else if !focused, textView.isFirstResponder {
            DispatchQueue.main.async { [weak self] in
                self?.textView.resignFirstResponder()
            }
        }
    }

    // MARK: - Layout

    override func layoutSubviews() {
        super.layoutSubviews()
        let w = bounds.width
        guard w > 0 else { return }

        // Reply bar height (8 top + 36 bar + 2 bottom = tight gap to text)
        let replyH: CGFloat = isReplyVisible ? 46 : 0

        // Text row height = textViewHeight (clamped)
        let textRowH = textViewHeight

        // Input container inner height = padding + reply + text row + padding
        let inputInnerH = innerPadding + replyH + textRowH + innerPadding
        let inputContainerH = max(minInputContainerHeight, inputInnerH)

        // Main bar height
        let mainBarH = max(buttonSize, inputContainerH)

        // Total height
        let totalH = topPadding + mainBarH + bottomPadding

        // Attach button
        let attachX = horizontalPadding
        let attachY = topPadding + mainBarH - buttonSize
        attachButton.frame = CGRect(x: attachX, y: attachY, width: buttonSize, height: buttonSize)
        attachGlass.frame = attachButton.bounds

        // Center icon in attach button
        centerIconLayer(in: attachButton, iconSize: CGSize(width: 21, height: 24))

        // Mic button
        let showMic = !isSendVisible
        let micX = w - horizontalPadding - buttonSize
        let micY = topPadding + mainBarH - buttonSize
        micButton.frame = CGRect(x: micX, y: micY, width: buttonSize, height: buttonSize)
        micGlass.frame = micButton.bounds
        centerIconLayer(in: micButton, iconSize: CGSize(width: 18, height: 24))

        // Input container
        let inputX = attachX + buttonSize + innerSpacing
        let micWidth: CGFloat = showMic ? (buttonSize + innerSpacing) : 0
        let inputW = w - inputX - horizontalPadding - micWidth
        let inputY = topPadding + mainBarH - inputContainerH
        inputContainer.frame = CGRect(x: inputX, y: inputY, width: inputW, height: inputContainerH)
        inputGlass.frame = inputContainer.bounds

        let cornerRadius: CGFloat = isMultiline ? 16 : 21
        inputContainer.layer.cornerRadius = cornerRadius
        inputContainer.layer.cornerCurve = .continuous
        inputGlass.fixedCornerRadius = cornerRadius
        inputGlass.applyCornerRadius()

        // Reply bar inside input container
        let replyX: CGFloat = 6
        let replyW = inputW - replyX - 4
        replyBar.frame = CGRect(x: replyX, y: innerPadding, width: replyW, height: replyH)
        layoutReplyBar(width: replyW, height: replyH)

        // Text view inside input container
        let textX: CGFloat = innerPadding + 6
        let textY = innerPadding + replyH
        let sendExtraW: CGFloat = isSendVisible ? sendButtonWidth : 0
        let emojiW: CGFloat = 20
        let emojiTrailing: CGFloat = 8 + sendExtraW
        let textW = inputW - textX - emojiW - emojiTrailing - innerPadding
        textView.frame = CGRect(x: textX, y: textY, width: max(0, textW), height: textRowH)

        // Emoji button
        let emojiX = textX + textW
        let emojiY = textY + textRowH - 36
        emojiButton.frame = CGRect(x: emojiX, y: emojiY, width: emojiW, height: 36)
        centerIconLayer(in: emojiButton, iconSize: CGSize(width: 19, height: 19))

        // Send button
        let sendX = inputW - innerPadding - sendButtonWidth
        let sendY = textY + textRowH - sendButtonHeight
        sendButton.frame = CGRect(x: sendX, y: sendY, width: sendButtonWidth, height: sendButtonHeight)
        sendCapsule.frame = sendButton.bounds
        sendCapsule.layer.cornerRadius = sendButtonHeight / 2
        centerIconLayer(in: sendButton, iconSize: CGSize(width: 22, height: 19))

        // Report height
        if abs(totalH - currentHeight) > 0.5 {
            currentHeight = totalH
            delegate?.composerHeightDidChange(self, height: totalH)
        }
    }

    private func layoutReplyBar(width: CGFloat, height: CGFloat) {
        guard height > 0 else { return }
        // barX=5 aligns blue line with text cursor: replyX(6)+5 = 11 = textX(9)+inset(2)
        let barX: CGFloat = 5
        let barY: CGFloat = 8
        replyBlueBar.frame = CGRect(x: barX, y: barY, width: 2, height: 36)

        let labelX: CGFloat = barX + 2 + 8 // blue line + spacing
        let titleSize = replyTitleLabel.sizeThatFits(CGSize(width: 200, height: 20))
        replyTitleLabel.frame = CGRect(x: labelX, y: barY + 2, width: titleSize.width, height: 17)

        let senderX = labelX + titleSize.width
        let senderW = width - senderX - 44
        replySenderLabel.frame = CGRect(x: senderX, y: barY + 2, width: max(0, senderW), height: 17)

        replyPreviewLabel.frame = CGRect(x: labelX, y: barY + 19, width: width - labelX - 44, height: 17)

        replyCancelButton.frame = CGRect(x: width - 44, y: (height - 44) / 2, width: 44, height: 44)
    }

    // MARK: - Icon Helpers

    private func makeIconLayer(
        pathData: String,
        viewBox: CGSize,
        targetSize: CGSize,
        color: UIColor
    ) -> CAShapeLayer {
        var parser = SVGPathParser(pathData: pathData)
        let cgPath = parser.parse()

        let layer = CAShapeLayer()
        let sx = targetSize.width / viewBox.width
        let sy = targetSize.height / viewBox.height
        layer.path = cgPath.copy(using: [CGAffineTransform(scaleX: sx, y: sy)])
        layer.fillColor = color.cgColor
        layer.bounds = CGRect(origin: .zero, size: targetSize)
        return layer
    }

    private func centerIconLayer(in button: UIView, iconSize: CGSize) {
        for sublayer in button.layer.sublayers ?? [] {
            guard let shape = sublayer as? CAShapeLayer else { continue }
            CATransaction.begin()
            CATransaction.setDisableActions(true)
            shape.position = CGPoint(x: button.bounds.midX, y: button.bounds.midY)
            CATransaction.commit()
            break
        }
    }

    // MARK: - Text Height

    private func recalculateTextHeight() {
        let lineHeight = textView.font?.lineHeight ?? 20
        let insets = textView.textContainerInset
        let maxTextH = lineHeight * maxLines
        let maxTotalH = maxTextH + insets.top + insets.bottom

        let fittingSize = textView.sizeThatFits(
            CGSize(width: textView.bounds.width > 0 ? textView.bounds.width : (bounds.width - 150), height: .greatestFiniteMagnitude)
        )
        let newH = max(textViewMinHeight, min(fittingSize.height, maxTotalH))

        // Enable/disable scrolling
        let shouldScroll = fittingSize.height > maxTotalH
        if textView.isScrollEnabled != shouldScroll {
            textView.isScrollEnabled = shouldScroll
        }

        // Check multiline
        let singleLineH = lineHeight + insets.top + insets.bottom
        let threshold = singleLineH + lineHeight * 0.5
        let newMultiline = fittingSize.height > threshold
        if newMultiline != isMultiline {
            isMultiline = newMultiline
            UIView.animate(withDuration: 0.2, delay: 0, options: .curveEaseInOut) {
                self.setNeedsLayout()
                self.layoutIfNeeded()
            }
        }

        if abs(newH - textViewHeight) > 0.5 {
            textViewHeight = newH
            setNeedsLayout()
        }
    }

    private func reportHeightIfChanged() {
        let replyH: CGFloat = isReplyVisible ? 46 : 0
        let inputInnerH = innerPadding + replyH + textViewHeight + innerPadding
        let inputContainerH = max(minInputContainerHeight, inputInnerH)
        let mainBarH = max(buttonSize, inputContainerH)
        let totalH = topPadding + mainBarH + bottomPadding

        if abs(totalH - currentHeight) > 0.5 {
            currentHeight = totalH
            delegate?.composerHeightDidChange(self, height: totalH)
        }
    }

    // MARK: - Send / Mic Visibility

    private func updateSendMicVisibility(animated: Bool) {
        let hasContent = !(textView.text ?? "").isEmpty
        guard hasContent != isSendVisible else { return }
        isSendVisible = hasContent

        let duration: TimeInterval = animated ? 0.28 : 0
        let damping: CGFloat = 0.9

        UIView.animate(withDuration: duration, delay: 0, usingSpringWithDamping: damping, initialSpringVelocity: 0, options: .beginFromCurrentState) {
            self.sendButton.alpha = hasContent ? 1 : 0
            self.sendButton.transform = hasContent ? .identity : CGAffineTransform(scaleX: 0.74, y: 0.74)

            self.micButton.alpha = hasContent ? 0 : 1
            self.micButton.transform = hasContent ? CGAffineTransform(scaleX: 0.42, y: 0.78) : .identity

            self.setNeedsLayout()
            self.layoutIfNeeded()
        }
    }

    // MARK: - UITextViewDelegate

    func textViewDidBeginEditing(_ textView: UITextView) {
        delegate?.composerFocusDidChange(self, isFocused: true)
    }

    func textViewDidEndEditing(_ textView: UITextView) {
        delegate?.composerFocusDidChange(self, isFocused: false)
    }

    func textView(
        _ textView: UITextView,
        shouldChangeTextIn range: NSRange,
        replacementText text: String
    ) -> Bool {
        guard !text.isEmpty, text != "\n" else { return true }
        delegate?.composerUserDidType(self)
        return true
    }

    func textViewDidChange(_ textView: UITextView) {
        guard !isUpdatingText else { return }
        self.textView.placeholderLabel.isHidden = !(textView.text ?? "").isEmpty
        delegate?.composerTextDidChange(self, text: textView.text ?? "")
        recalculateTextHeight()
        updateSendMicVisibility(animated: true)
    }

    // MARK: - Actions

    @objc private func attachTapped() {
        delegate?.composerDidTapAttach(self)
    }

    @objc private func sendTapped() {
        delegate?.composerDidTapSend(self)
    }

    @objc private func emojiTapped() {
        // Emoji button = focus trigger (system emoji via 🌐 key)
        if !textView.isFirstResponder {
            textView.becomeFirstResponder()
        }
    }

    @objc private func micTapped() {
        // Mic = placeholder for voice messages, acts as send when there's content
        let text = (textView.text ?? "").trimmingCharacters(in: .whitespacesAndNewlines)
        if !text.isEmpty {
            delegate?.composerDidTapSend(self)
        } else {
            if !textView.isFirstResponder {
                textView.becomeFirstResponder()
            }
        }
    }

    @objc private func replyCancelTapped() {
        delegate?.composerDidCancelReply(self)
    }
}
