//
//  LNIMChatTextInputView.swift
//  Lanu
//
//  Created by OneeChan on 2025/12/4.
//

import Foundation
import UIKit
import SnapKit


protocol LNIMChatTextInputViewDelegate: AnyObject {
    func onVoiceInputClick()
}


private enum LNIMChatTextInputType {
    case none
    case keyboard
    case emoji
}


class LNIMChatTextInputView: UIView {
    private var hideEmojiConstraint: Constraint?
    
    private let emojiButton = UIButton()
    private let emojiPanel = LNIMChatEmojiPanel()
    private var emojiHeight: Constraint?
    
    private let placeholderLabel = UILabel()
    private let inputField = LNAutoSizeTextView()
    
    private let cameraButton = UIButton()
    private var hideCameraConstraint: Constraint?
    
    private let sendButton = UIButton()
    
    weak var delegate: LNIMChatTextInputViewDelegate?
    weak var viewModel: LNIMChatViewModel?
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        
        setupViews()
        adjustViewsForInputTypeChanged(type: .none)
        
        LNEventDeliver.addObserver(self)
    }
    
    func hideInput() {
        adjustViewsForInputTypeChanged(type: .none)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

extension LNIMChatTextInputView {
    private func showImageSelectPanel() {
        let handler: (UIImage?, URL?) -> Void = { [weak self] image, _ in
            guard let self else { return }
            guard let image else { return }
            viewModel?.sendImageMessage(image: image)
        }
        
        LNBottomSheetMenu.showImageSelectMenu(view: self, handler: handler)
    }
}

extension LNIMChatTextInputView: UITextViewDelegate {
    func textViewDidBeginEditing(_ textView: UITextView) {
        if inputField.inputView == nil {
            adjustViewsForInputTypeChanged(type: .keyboard)
        }
    }
    
    func textViewDidChange(_ textView: UITextView) {
        placeholderLabel.isHidden = textView.text.isEmpty != true
        
        let inputEmpty = textView.text?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty != false
        if inputEmpty {
            hideCameraConstraint?.update(priority: .low)
        } else {
            hideCameraConstraint?.update(priority: .high)
        }
        sendButton.setImage(inputEmpty ? .icImChatVoice : .icImChatSend, for: .normal)
        cameraButton.isEnabled = inputEmpty
        UIView.animate(withDuration: 0.25) { [weak self] in
            guard let self else { return }
            cameraButton.alpha = inputEmpty ? 1.0 : 0.0
            layoutIfNeeded()
        }
    }
    
    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
        let currentText = textField.text ?? ""
                
        guard let range = Range(range, in: currentText) else { return false }
        let newText = currentText.replacingCharacters(in: range, with: string)
        if newText.count < currentText.count {
            return true
        }
        
        return newText.count <= LNIMManager.maxMessageInput
    }
}

extension LNIMChatTextInputView: LNKeyboardNotify {
    func onKeyboardWillShow(curInput: UIView?, keyboardHeight: CGFloat) {
        guard curInput == inputField else { return }
        
        emojiHeight?.update(offset: keyboardHeight)
    }
    
    func onKeyboardShow(curInput: UIView?, keyboardHeight: CGFloat) {
        guard curInput == inputField else { return }
        layoutIfNeeded()
    }
}

extension LNIMChatTextInputView: LNIMChatEmojiPanelDelegate {
    func onIMChatEmojiPanelDidClickDelete(view: LNIMChatEmojiPanel) {
        guard inputField.textStorage.length > 0 else { return }
        inputField.textStorage.deleteCharacters(in: .init(location: inputField.textStorage.length - 1, length: 1))
        textViewDidChange(inputField)
    }
    
    func onIMChatEmojiPanel(view: LNIMChatEmojiPanel, didSelectEmoji emoji: LNEmojiData) {
        let attachment = LNIMChatEmojiAttachment()
        attachment.font = inputField.font
        attachment.name = emoji.name
        attachment.image = LNIMEmojiManager.shared.getFaceFromCache(path: emoji.path)
        
        let emojiStr = NSAttributedString(attachment: attachment)

        let curRange = inputField.selectedRange
        if curRange.length > 0 {
            inputField.textStorage.deleteCharacters(in: curRange)
        }
        inputField.textStorage.insert(emojiStr, at: inputField.selectedRange.location)
        inputField.selectedRange = .init(location: inputField.selectedRange.location + 1, length: 0)
        
        textViewDidChange(inputField)
    }
}

extension LNIMChatTextInputView {
    private func adjustViewsForInputTypeChanged(type: LNIMChatTextInputType) {
        switch type {
        case .none:
            inputField.inputView = nil
            inputField.resignFirstResponder()
            
            emojiButton.setImage(.icImChatEmoji, for: .normal)
            
            hideEmojiConstraint?.update(priority: .high)
            emojiPanel.isHidden = true
            emojiPanel.reloadData() // 隐藏输入框便触发刷新
        case .keyboard:
            inputField.becomeFirstResponder()
            inputField.inputView = nil
            inputField.reloadInputViews()
            
            emojiButton.setImage(.icImChatEmoji, for: .normal)
            
            hideEmojiConstraint?.update(priority: .low)
            emojiPanel.isHidden = true
        case .emoji:
            inputField.becomeFirstResponder()
            inputField.inputView = UIView()
            inputField.reloadInputViews()
            
            emojiButton.setImage(.icImChatKeyboard, for: .normal)
            
            hideEmojiConstraint?.update(priority: .low)
            emojiPanel.isHidden = false
        }
    }
    
    private func setupViews() {
        backgroundColor = .fill
        
        let inputMenu = buildInputMenu()
        addSubview(inputMenu)
        inputMenu.snp.makeConstraints { make in
            make.top.equalToSuperview().inset(10)
            make.leading.equalToSuperview().offset(12)
            hideEmojiConstraint = make.bottom.equalToSuperview().offset(-safeBottomInset).priority(.high).constraint
        }
        
        sendButton.setImage(.icImChatVoice, for: .normal)
        sendButton.setContentHuggingPriority(.defaultHigh, for: .horizontal)
        sendButton.addAction(UIAction(handler: { [weak self] _ in
            guard let self else { return }
            let text = inputField.textStorage
            if text.length == 0 {
                delegate?.onVoiceInputClick()
                adjustViewsForInputTypeChanged(type: .none)
            } else {
                viewModel?.sendTextMessage(text: text.toEmojiContent)
                inputField.text = nil
                textViewDidChange(inputField)
            }
        }), for: .touchUpInside)
        addSubview(sendButton)
        sendButton.snp.makeConstraints { make in
            make.centerY.equalTo(inputMenu)
            make.trailing.equalToSuperview().offset(-12)
            make.leading.equalTo(inputMenu.snp.trailing).offset(10)
        }
        
        let emoji = buildEmojiView()
        addSubview(emoji)
        emoji.snp.makeConstraints { make in
            make.horizontalEdges.equalToSuperview()
            make.top.equalTo(inputMenu.snp.bottom).offset(10)
            make.bottom.equalToSuperview().priority(.medium)
            emojiHeight = make.height.equalTo(336).constraint
        }
    }
    
    private func buildInputMenu() -> UIView {
        let container = UIView()
        container.backgroundColor = .fill_2
        container.layer.cornerRadius = 19
        container.clipsToBounds = true
        
        emojiButton.setImage(.icImChatEmoji, for: .normal)
        emojiButton.setContentHuggingPriority(.defaultHigh, for: .horizontal)
        emojiButton.addAction(UIAction(handler: { [weak self] _ in
            guard let self else { return }
            if inputField.inputView != nil {
                adjustViewsForInputTypeChanged(type: .keyboard)
            } else {
                adjustViewsForInputTypeChanged(type: .emoji)
            }
            inputField.reloadInputViews()
        }), for: .touchUpInside)
        container.addSubview(emojiButton)
        emojiButton.snp.makeConstraints { make in
            make.bottom.equalToSuperview().offset(-8)
            make.leading.equalToSuperview().offset(10)
        }
        
        inputField.font = .body_m
        inputField.textColor = .text_5
        inputField.backgroundColor = .clear
        inputField.delegate = self
        container.addSubview(inputField)
        inputField.snp.makeConstraints { make in
            make.verticalEdges.equalToSuperview().inset(3.5)
            make.leading.equalTo(emojiButton.snp.trailing).offset(4)
            hideCameraConstraint = make.trailing.equalToSuperview().offset(-10).priority(.low).constraint
        }
        
        placeholderLabel.text = .init(key: "A00084")
        placeholderLabel.font = .body_m
        placeholderLabel.textColor = .text_2
        container.insertSubview(placeholderLabel, belowSubview: inputField)
        placeholderLabel.snp.makeConstraints { make in
            make.centerY.equalTo(inputField)
            make.leading.equalTo(inputField).offset(4)
        }
        
        cameraButton.setImage(.icImChatCamera, for: .normal)
        cameraButton.addAction(UIAction(handler: { [weak self] _ in
            guard let self else { return }
            adjustViewsForInputTypeChanged(type: .none)
            showImageSelectPanel()
        }), for: .touchUpInside)
        container.addSubview(cameraButton)
        cameraButton.snp.makeConstraints { make in
            make.bottom.equalToSuperview().offset(-8)
            make.leading.equalTo(inputField.snp.trailing).offset(4)
            make.trailing.equalToSuperview().offset(-10).priority(.medium)
        }
        return container
    }
    
    private func buildEmojiView() -> UIView {
        emojiPanel.isHidden = true
        emojiPanel.delegate = self
        
        return emojiPanel
    }
}

#if DEBUG

import SwiftUI

struct LNIMChatTextInputViewPreview: UIViewRepresentable {
    func makeUIView(context: Context) -> some UIView {
        let container = UIView()
        container.backgroundColor = .lightGray
        
        let view = LNIMChatTextInputView()
        container.addSubview(view)
        view.snp.makeConstraints { make in
            make.leading.trailing.bottom.equalToSuperview()
        }
        
        return container
    }
    
    func updateUIView(_ uiView: UIViewType, context: Context) { }
}

#Preview(body: {
    LNIMChatTextInputViewPreview()
})
#endif
