//
//  AttachmentDescriptionTextView.swift
//  ComposeUI
//
//  Created by Shadowfacts on 3/12/23.
//

import SwiftUI

private var placeholder: some View {
    Text("Describe for the visually impaired…")
}

struct InlineAttachmentDescriptionView: View {
    @ObservedObject private var attachment: DraftAttachment
    private let minHeight: CGFloat
    
    @State private var height: CGFloat?
    
    init(attachment: DraftAttachment, minHeight: CGFloat) {
        self.attachment = attachment
        self.minHeight = minHeight
    }
    
    var body: some View {
        ZStack(alignment: .topLeading) {
            if attachment.attachmentDescription.isEmpty {
                placeholder
                    .font(.body)
                    .foregroundColor(.secondary)
                    .offset(x: 4, y: 8)
            }
            
            WrappedTextView(
                text: $attachment.attachmentDescription,
                backgroundColor: .clear,
                textDidChange: self.textDidChange
            )
            .frame(height: height ?? minHeight)
        }
    }
    
    private func textDidChange(_ textView: UITextView) {
        height = max(minHeight, textView.contentSize.height)
    }
}

struct FocusedAttachmentDescriptionView: View {
    @ObservedObject var attachment: DraftAttachment
    
    var body: some View {
        ZStack(alignment: .topLeading) {
            WrappedTextView(
                text: $attachment.attachmentDescription,
                backgroundColor: .secondarySystemBackground,
                textDidChange: nil
            )
            .edgesIgnoringSafeArea([.bottom, .leading, .trailing])
            
            if attachment.attachmentDescription.isEmpty {
                placeholder
                    .font(.body)
                    .foregroundColor(.secondary)
                    .offset(x: 4, y: 8)
                    .allowsHitTesting(false)
            }
        }
    }
}

private struct WrappedTextView: UIViewRepresentable {
    typealias UIViewType = UITextView
    
    @Binding var text: String
    let backgroundColor: UIColor
    let textDidChange: (((UITextView) -> Void))?
    
    @Environment(\.isEnabled) private var isEnabled
    
    func makeUIView(context: Context) -> UITextView {
        let view = UITextView()
        view.delegate = context.coordinator
        view.backgroundColor = backgroundColor
        view.font = .preferredFont(forTextStyle: .body)
        view.adjustsFontForContentSizeCategory = true
        view.textContainer.lineBreakMode = .byWordWrapping
        return view
    }
    
    func updateUIView(_ uiView: UITextView, context: Context) {
        uiView.text = text
        uiView.isEditable = isEnabled
        context.coordinator.textView = uiView
        context.coordinator.text = $text
        context.coordinator.didChange = textDidChange
        if let textDidChange {
            // wait until the next runloop iteration so that SwiftUI view updates have finished and
            // the text view knows its new content size
            DispatchQueue.main.async {
                textDidChange(uiView)
            }
        }
    }
    
    func makeCoordinator() -> Coordinator {
        Coordinator(text: $text, didChange: textDidChange)
    }
    
    class Coordinator: NSObject, UITextViewDelegate, TextViewCaretScrolling {
        weak var textView: UITextView?
        var text: Binding<String>
        var didChange: ((UITextView) -> Void)?
        var caretScrollPositionAnimator: UIViewPropertyAnimator?
        
        init(text: Binding<String>, didChange: ((UITextView) -> Void)?) {
            self.text = text
            self.didChange = didChange
            
            super.init()
            
            NotificationCenter.default.addObserver(self, selector: #selector(keyboardDidShow), name: UIResponder.keyboardDidShowNotification, object: nil)
        }
        
        @objc private func keyboardDidShow() {
            guard let textView,
                  textView.isFirstResponder else {
                return
            }
            ensureCursorVisible(textView: textView)
        }
        
        func textViewDidChange(_ textView: UITextView) {
            text.wrappedValue = textView.text
            didChange?(textView)
            
            ensureCursorVisible(textView: textView)
        }
    }
}
