//
//  TitleTextView.swift

//
//  Created by Leon on 2025/7/20.
//

import UIKit
import SnapKit

private let defaultHeightForTextView: CGFloat = 140.0

class TitleTextView: UIView, UITextViewDelegate {
    // MARK: - Properties
    var textDidChanged: ((CLTextView) -> Void)?
    var textDidEndEditing: ((CLTextView) -> Void)?
    var textDidBeginEditing: ((CLTextView) -> Void)?
    var textShouldBeginEditing: ((CLTextView) -> Void)?
    
    var maxLimit: Int = 0 {
        didSet {
            if maxLimit > 0 {
                countLabel.isHidden = false
                countLabel.text = "0/\(maxLimit)"
                textView.limit.maxCharacterNumber = maxLimit
                textView.snp.updateConstraints { make in
                    make.bottom.equalTo(textContainer).offset(-48)
                }
            }
        }
    }
    
    var minLimit: Int = 0
    
    var titleLabel: UILabel
    var textView: CLTextView
    var supportLabel: UILabel
    
    var placeholder: String? {
        didSet {
            textView.placeholder = placeholder
        }
    }
    
    /// 同时更新countLabel
    var defaultText: String?{
        didSet{
            textView.text = defaultText
            
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {[weak self] in
                guard let self = self else{return}
                self.subTextDidChange(self.textView)
            }
            
        }
    }
    var errorMsg: String?
    
    /// 配置固定高度
    var constHeightOfTextView: CGFloat = 0
    
    private var stackV: UIStackView
    private var textContainer: UIView
    private var countLabel: UILabel
    private var optionLabel: EPOptionFlagLabel?
    private var queryButton: EPIconTertiaryButton?
    private var lastText: String?
    private var location: Int = 0
    private var length: Int = 0
    private var focusBorderColor: UIColor
    private var errorBorderColor: UIColor
    private var nowBorderColor: UIColor
    private var errorBorderShowMode: Bool = false
    private var realDefaultHeightForTextView: CGFloat
    private var tapQueryButton: (() -> Void)?
    
    // MARK: - Initialization
    override init(frame: CGRect) {
        titleLabel = UILabel()
        textView = CLTextView()
        supportLabel = UILabel()
        stackV = UIStackView()
        textContainer = UIView()
        countLabel = UILabel()
        focusBorderColor = .c.cpvn
        errorBorderColor = .c.civn
        nowBorderColor = .clear
        realDefaultHeightForTextView = defaultHeightForTextView
        
        super.init(frame: frame)
        initialViews()
    }
    
    @available(*, unavailable)
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    // MARK: - Setup
    private func initialViews() {
        // Configure stack view
        stackV.axis = .vertical
        stackV.alignment = .leading
        stackV.spacing = 16
        addSubview(stackV)
        stackV.snp.makeConstraints { make in
            make.top.bottom.leading.trailing.equalTo(self)
        }
        
        // Configure title label
        titleLabel.font = CLSystemToken.font(token: .tlm)//EPSystemToken.typography(.txtLabelM).font
        titleLabel.textColor = .c.ctpn
        
        // Configure text container
        textContainer.backgroundColor = .c.csen
        textContainer.layer.cornerRadius = CLSystemToken.radius(token: .rs)
        textContainer.layer.borderWidth = CLSystemToken.border(token: .bs)//EPSystemToken.border(.borderS)
        textContainer.layer.borderColor = UIColor.clear.cgColor
        
        // Configure text view
        textView.delegate = self
        textView.backgroundColor = .clear
        textView.font = CLSystemToken.font(token: .tbl)//EPSystemToken.typography(.txtBodyL).font
        textView.placeholderLabel.font = CLSystemToken.font(token: .tbl)
        //textView.iqPlaceholderLabel?.font = EPSystemToken.typography(.txtBodyL).font
        textView.placeholderTextColor = .c.cttn
        textView.textColor = .c.ctpn
    
        textContainer.addSubview(textView)
        textView.snp.makeConstraints { make in
            make.top.equalTo(textContainer).offset(8)
            make.leading.equalTo(textContainer).offset(16)
            make.trailing.equalTo(textContainer).offset(-16)
            make.bottom.equalTo(textContainer).offset(-16)
            make.height.greaterThanOrEqualTo(realDefaultHeightForTextView)
            make.height.equalTo(defaultHeightForTextView)
        }
        
        // Update text view height based on placeholder size
        
        
        textView.updateByPlaceholderSize { [weak self] rect in
            guard let self = self, rect.size.height > defaultHeightForTextView else { return }
            self.realDefaultHeightForTextView = rect.size.height
            self.textView.snp.updateConstraints { make in
                make.height.greaterThanOrEqualTo(rect.size.height)
            }
        }
        
        // Configure count label
        countLabel.font = CLSystemToken.font(token: .tbs)//EPSystemToken.typography(.txtBodyS).font
        countLabel.textColor = .c.ctsn//EPSystemToken.color(.txtSecondaryNormal)
        
        textContainer.addSubview(countLabel)
        countLabel.snp.makeConstraints { make in
            make.trailing.equalTo(textContainer).offset(-16)
            make.bottom.equalTo(textContainer).offset(-16)
        }
        
        // Configure support label
        supportLabel.numberOfLines = 0
        supportLabel.font = .t.tbs//EPSystemToken.typography(.txtBodyS).font
        supportLabel.textColor = .c.ctsn// EPSystemToken.color(.txtSecondaryNormal)
        supportLabel.isHidden = true
        
        // Add subviews to stack
        stackV.addArrangedSubview(titleLabel)
        stackV.addArrangedSubview(textContainer)
        stackV.addArrangedSubview(supportLabel)
        
        textContainer.snp.makeConstraints { make in
            make.left.right.equalTo(stackV)
        }
        
        setupNotStrictCheck()
    }
    
    private func setupNotStrictCheck() {
        textView.autocapitalizationType = .none
        textView.autocorrectionType = .no
    }
    
    // MARK: - Public Methods
    func showErrorMsg(_ string: String) {
        supportLabel.isHidden = false
        supportLabel.textColor = .c.civn//EPSystemToken.color(.importantVariantNormal)
        supportLabel.text = string
        textContainer.layer.borderColor = errorBorderColor.cgColor
        errorBorderShowMode = true
        setNeedsLayout()
    }
    
    func hideErrorMsg() {
        supportLabel.isHidden = true
        supportLabel.textColor = .c.ctsn
        textContainer.layer.borderColor = textView.isFirstResponder ? focusBorderColor.cgColor : UIColor.clear.cgColor
        errorBorderShowMode = false
        setNeedsLayout()
    }
    
//    func configDefaultText(_ txt: String?) {
//        guard let txt = txt else { return }
//        textView.text = txt
//        if !txt.removingWhitespaceAndNewlines.isEmpty {
//            textViewDidChange(textView)
//        }
//    }
    
    func setupTextSync(_ text: String?) {
        textView.text = text
        subTextDidChange(textView)
    }
    
    func titleAppendOptionalLabel() {
        if optionLabel == nil {
            optionLabel = {
                let v = EPOptionFlagLabel()
                addSubview(v )
                v.snp.makeConstraints { make in
                    make.leading.equalTo(titleLabel.snp.trailing).offset(8)
                    make.centerY.equalTo(titleLabel)
                }
                return v
            }()
        }
        optionLabel?.isHidden = false
    }
    
    func titleAppendQuestionIcon(_ block: @escaping () -> Void) {
        if queryButton == nil {
            tapQueryButton = block
            queryButton = EPIconTertiaryButton(radius: .round, iconSize: .xxs, iconCode: .question)
            queryButton?.touchAreaInsets = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
            queryButton?.addTarget(self, action: #selector(tapQueryBtn), for: .touchUpInside)
            addSubview(queryButton!)
            queryButton?.snp.makeConstraints { make in
                make.leading.equalTo(titleLabel.snp.trailing).offset(8)
                make.centerY.equalTo(titleLabel)
                make.trailing.lessThanOrEqualTo(self).offset(-8)
            }
        }
        queryButton?.isHidden = false
    }
    
    func setupMaxShowLineAndTruncatingTail(_ line: Int) {
        textView.isScrollEnabled = false
        textView.textContainer.maximumNumberOfLines = line
        textView.textContainer.lineBreakMode = .byTruncatingTail
    }
    
    func setupTest() {
        titleLabel.text = "Label.M"
        countLabel.text = "0/100"
        maxLimit = 100
    }
    
    // MARK: - Functions
    
    private func refreshViewsAboutMinLimitOrmaxLimit(){
        if maxLimit > 0 {
            countLabel.text = "\(textView.text.count)/\(maxLimit)"
        }
        
        if minLimit > 0{
            if textView.text.count < minLimit && textView.text.count > 0{
                if let msg = errorMsg{
                    showErrorMsg(msg)
                }else{
                    dlog("⚠️error msg未设置[TitleTextView]")
                }
            }else{
                hideErrorMsg()
            }
        }
    }
    
    private func refreshTextViewsHeight(){
        let calHeight = textView.sizeThatFits(CGSize(width: textView.bounds.width, height: .greatestFiniteMagnitude)).height
        //print("calHeight: \(calHeight), realDefaultHeightForTextView: \(realDefaultHeightForTextView)")
        if constHeightOfTextView > 0 && !(textView.text.isEmpty) {
            textView.snp.updateConstraints { make in
                make.height.equalTo(constHeightOfTextView)
            }
        } else if calHeight > realDefaultHeightForTextView {
            if textView.bounds.height != calHeight {
                textView.snp.updateConstraints { make in
                    make.height.equalTo(calHeight)
                }
            }
        } else {
            if textView.bounds.height != realDefaultHeightForTextView {
                textView.snp.updateConstraints { make in
                    make.height.equalTo(realDefaultHeightForTextView)
                }
            }
        }
        layoutIfNeeded()
    }
    
    // MARK: - Actions
    @objc private func tapQueryBtn() {
        tapQueryButton?()
    }
    
    // MARK: - Overrides
    override func becomeFirstResponder() -> Bool {
        textView.becomeFirstResponder()
    }
    
    // MARK: - UITextViewDelegate
    func subTextDidChange(_ textView: CLTextView) {
        errorBorderShowMode = false
        nowBorderColor = focusBorderColor
        if textView.isFirstResponder {
            textContainer.layer.borderColor = nowBorderColor.cgColor
        }
        
        // Handle text length limiting
        if textView.markedTextRange == nil {
            let nsTextContent = textView.text ?? ""
            let existTextNum = nsTextContent.count
            
            if existTextNum > maxLimit && maxLimit > 0 {
                // Convert maxLimit - 1 to String.Index
                if let stringIndex = nsTextContent.index(nsTextContent.startIndex, offsetBy: maxLimit - 1, limitedBy: nsTextContent.endIndex) {
                    let range = nsTextContent.rangeOfComposedCharacterSequence(at: stringIndex)
                    var str = String(nsTextContent.prefix(maxLimit))
                    
                    // Convert range.upperBound to an Int offset for comparison
                    let upperBoundOffset = nsTextContent.distance(from: nsTextContent.startIndex, to: range.upperBound)
                    if upperBoundOffset > maxLimit {
                        str = String(nsTextContent.prefix(upTo: range.lowerBound))
                    }
                    
                    textView.text = str
                } else {
                    // Handle out-of-bounds case
                    textView.text = ""
                }
            }

            lastText = textView.text
        } else {
            let markRange = textView.markedTextRange
            let beginning = textView.beginningOfDocument
            let selectionStart = markRange?.start
            let selectionEnd = markRange?.end
            location = selectionStart != nil ? textView.offset(from: beginning, to: selectionStart!) : 0
            length = (selectionStart != nil && selectionEnd != nil) ? textView.offset(from: selectionStart!, to: selectionEnd!) : 0
        }
        
        refreshViewsAboutMinLimitOrmaxLimit()
        
        refreshTextViewsHeight()
    }
    
    func textViewDidChange(_ textView: UITextView) {
        subTextDidChange(textView as! CLTextView)
        textDidChanged?(self.textView)
    }
    
    func textViewDidBeginEditing(_ textView: UITextView) {
        if !errorBorderShowMode {
            textContainer.layer.borderColor = focusBorderColor.cgColor
        }
        textDidBeginEditing?(self.textView)
    }
    
    func textViewDidEndEditing(_ textView: UITextView) {
        if !errorBorderShowMode {
            textContainer.layer.borderColor = UIColor.clear.cgColor
        }
        textDidEndEditing?(self.textView)
    }
    
    func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
        if text.isEmpty || maxLimit == 0 {
            return true
        }
        
        if textView.markedTextRange == nil {
            if (lastText?.count ?? 0) == maxLimit {
                return false
            }
            location = range.location
            length = text.count
        }
        return true
    }
    
    func textViewShouldBeginEditing(_ textView: UITextView) -> Bool {
        textShouldBeginEditing?(self.textView)
        return textShouldBeginEditing == nil
    }
}

// MARK: - Extensions
extension String {
    var removingWhitespaceAndNewlines: String {
        components(separatedBy: .whitespacesAndNewlines).joined()
    }
}
