//
//  ReplyStatusView.swift
//  ComposeUI
//
//  Created by Shadowfacts on 3/25/23.
//

import SwiftUI
import Pachyderm
import TuskerComponents

struct ReplyStatusView: View {
    let status: any StatusProtocol
    let rowTopInset: CGFloat
    let globalFrameOutsideList: CGRect
    
    @EnvironmentObject private var controller: ComposeController
    @State private var displayNameHeight: CGFloat?
    @State private var contentHeight: CGFloat?
    
    private let horizSpacing: CGFloat = 8
    
    var body: some View {
        HStack(alignment: .top, spacing: horizSpacing) {
            GeometryReader(content: self.replyAvatarImage)
                .frame(width: 50)
            
            VStack(alignment: .leading, spacing: 0) {
                HStack {
                    controller.displayNameLabel(status.account, .body, 17)
                        .lineLimit(1)
                        .layoutPriority(1)
                    
                    Text(verbatim: "@\(status.account.acct)")
                        .font(.body.weight(.light))
                        .foregroundColor(.secondary)
                        .lineLimit(1)
                    
                    Spacer()
                }
                .background(GeometryReader { proxy in
                    Color.clear
                        .preference(key: DisplayNameHeightPrefKey.self, value: proxy.size.height)
                        .onPreferenceChange(DisplayNameHeightPrefKey.self) { newValue in
                            displayNameHeight = newValue
                        }
                })
                
                controller.replyContentView(status) { newHeight in
                    // otherwise, with long in-reply-to statuses, the main content text view position seems not to update
                    // and it ends up  partially behind the header
                    DispatchQueue.main.async {
                        contentHeight = newHeight
                    }
                }
                .frame(height: contentHeight ?? 0)
            }
        }
        .frame(minHeight: 50, alignment: .top)
    }
    
    private func replyAvatarImage(geometry: GeometryProxy) -> some View {
        // using a coordinate space declared outside of the List doesn't work, so we do the math ourselves
        let globalFrame = geometry.frame(in: .global)
        let scrollOffset = -(globalFrame.minY - globalFrameOutsideList.minY)

        // add rowTopInset so that the image is always at least rowTopInset away from the top
        var offset = scrollOffset + rowTopInset

        // offset can never be less than 0 (i.e., above the top of the in-reply-to content)
        offset = max(offset, 0)
        
        // subtract 50, because we care about where the bottom of the view is but the offset is relative to the top of the view
        let maxOffset = max((contentHeight ?? 0) + (displayNameHeight ?? 0) - 50, 0)

        // once you scroll past the in-reply-to-content, the bottom of the avatar should be pinned to the bottom of the content
        offset = min(offset, maxOffset)

        return AvatarImageView(
            url: status.account.avatar,
            size: 50,
            style: controller.config.avatarStyle,
            fetchAvatar: controller.fetchAvatar
        )
        .offset(x: 0, y: offset)
        .accessibilityHidden(true)
    }

}

private struct DisplayNameHeightPrefKey: PreferenceKey {
    static var defaultValue: CGFloat = 0
    static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
        value = nextValue()
    }
}
