import SwiftUI

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

private struct OffsetPreferenceKey: PreferenceKey {
    static var defaultValue: CGPoint = .zero
    static func reduce(value: inout CGPoint, nextValue: () -> CGPoint) { }
}

extension OffsetPreferenceKey {
    static func negativeScrollOffset(value: CGPoint) -> CGFloat {
        value.y >= 0 ? value.y : 0
    }
    
    static func positiveScrollOffset(value: CGPoint) -> CGFloat {
        value.y >= 0 ? value.y/2 : value.y/1.5
    }
    
    static func shouldRefresh(value: CGPoint) ->  Bool {
        value.y > refreshPoint
    }
    
    static let refreshPoint: CGFloat = 80
}

struct ScrollViewWithBanner<Content: View>: View {
    @EnvironmentObject var analyticsService: AnalyticsService
    
    var banner: Banner?
    var onRefresh: (() -> Void)? = nil
    var onLoadMore: (() throws -> Void)? = nil
    
    @ViewBuilder var content: ((ScrollViewProxy) -> Content)
   
    private let bannerHeight: CGFloat = 220
    
    @State private var readyToRefresh: Bool = false
    @State private var isPerformingRefresh: Bool = false
    private let columns: [GridItem] = [GridItem(.adaptive(minimum: 300))]
    
    @State private var scrollHeight: CGFloat = 0
    @State private var isLoadingMore: Bool = false
    
    var body: some View {
        
            
            ScrollViewReader { scrollProxy in
                ScrollView {
                    VStack(spacing: 0) {
                        
                        GeometryReader { proxy in
                            Color.clear
                                .preference(
                                    key: OffsetPreferenceKey.self,
                                    value: proxy.frame(
                                        in: .named("ScrollViewOrigin")
                                    ).origin
                                )
                        }
                        .frame(height: banner != nil ? 220 : 0)
                        .backgroundPreferenceValue(OffsetPreferenceKey.self) { (value: OffsetPreferenceKey.Value) in
                            if let banner = banner {
                                ZStack {
                                    BannerView(banner: banner)
                                        .frame(height: 220 + OffsetPreferenceKey.negativeScrollOffset(value: value))
                                        .offset(y: 0 - OffsetPreferenceKey.positiveScrollOffset(value: value))
                                        .onAppear {
                                            analyticsService.bannerAdWasShown(bannerId: banner.name)
                                        }
                                    
                                    ColorName.background.color
                                        .opacity(isPerformingRefresh ? 0.6 : 0)
                                        .frame(height: 220 + OffsetPreferenceKey.negativeScrollOffset(value: value))
                                        .offset(y: 0 - OffsetPreferenceKey.positiveScrollOffset(value: value))
                                    
                                    ProgressView()
                                        .progressViewStyle(.circular)
                                        .opacity(isPerformingRefresh ? 1 : 0)
                                        .offset(y: 0 - OffsetPreferenceKey.positiveScrollOffset(value: value))
                                }
                            }
                        }
                        .onPreferenceChange(OffsetPreferenceKey.self) { (value: OffsetPreferenceKey.Value) in
                            if OffsetPreferenceKey.shouldRefresh(value: value) {
                                if !readyToRefresh {
                                    readyToRefresh = true
                                    DispatchQueue.main.asyncAfter(deadline: .now() + 0.33) {
                                        if readyToRefresh {
                                            refreshContent()
                                        }
                                        readyToRefresh = false
                                    }
                                }
                            } else {
                                readyToRefresh = false
                            }
                            
                            if scrollHeight > UIScreen.main.bounds.height,
                               0-value.y > scrollHeight*0.7,
                               onLoadMore != nil {
                                loadMoreContent()
                            }
                        }
                        .overlay(
                            VStack(spacing: 0) {
                                if let banner = banner {
                                    Button {
                                        if let url = banner.link {
                                            UIApplication.shared.open(url)
                                        }
                                        analyticsService.bannerAdWasTapped(bannerId: banner.name)
                                    } label: {
                                        Rectangle()
                                            .fill(Color.clear)
                                            .frame(height: 220)
                                    }
                                    .id("banner")
                                }
                            }
                        )
                        
                        content(scrollProxy)
                    }
                    .background(
                        GeometryReader { proxy in
                            Color.clear
                                .preference(
                                    key: LimitPreferenceKey.self,
                                    value: proxy.size.height
                                )
                        }
                        .onPreferenceChange(LimitPreferenceKey.self) { (value: LimitPreferenceKey.Value) in
                            scrollHeight = value
                        }
                    )
                    
                }
                .coordinateSpace(name: "ScrollViewOrigin")
                .background(
                    ColorName.background.color()
                        .ignoresSafeArea()
                )
                
            }
        
    }
    
    private func refreshContent() {
        guard let onRefresh = onRefresh else {
            return
        }

        if isPerformingRefresh {
            return
        }
        
        withAnimation {
            isPerformingRefresh = true
        }
        onRefresh()
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.75) {
            withAnimation {
                isPerformingRefresh = false
            }
        }
    }
    
    private func loadMoreContent() {
        if isLoadingMore {
            return
        }
        
        guard let onLoadMore = onLoadMore else {
            return
        }
        
        isLoadingMore = true
        
        do {
            try onLoadMore()
            isLoadingMore = false
        } catch {
            print(error)
        }
        
    }
}

#if DEBUG
struct ScrollViewWithBanner_Previews: PreviewProvider {
    static var previews: some View {
        ScrollViewWithBanner(banner: nil, onRefresh: {
            // Nothing
        }) { _ in
            Text("Hello World!")
        }
    }
}
#endif
