import SwiftUI

struct LatestArticlesView: View {
    typealias DisplayOption = ContentService.BannerPosition
    
    @EnvironmentObject private var contentService: ContentService
    @EnvironmentObject private var analyticsService: AnalyticsService
    @EnvironmentObject private var userService: UserService
    
    @State private var displayOption: DisplayOption = .latest
    @State private var isLoading: Bool = true
    
    @State private var posts: [Post] = []
    @State private var featurePost: Post?
    
    private var topicColor: Color {
        switch displayOption {
            
        case .topic(topicId: let topicId):
            guard let topic = contentService.topics.first(where: { $0.id == topicId }) else {
                fallthrough
            }
            
            return topic.backgroundColor
        default:
            return ColorName.tint.color
        }
    }
    
    private func setPosts() {
        switch displayOption {
            
        case .latest:
            self.posts = Array(contentService.latestPosts.dropFirst())
            self.featurePost = contentService.latestPosts.first
            self.pageToLoad = 2
            analyticsService.articleListWasViewed(listType: .latest)
            
        case .trending:
            self.posts = Array(contentService.trendingPosts.dropFirst())
            self.featurePost = contentService.trendingPosts.first
            self.pageToLoad = 2
            analyticsService.articleListWasViewed(listType: .trending)
            
        case .topic(topicId: let topicId):
            isLoading = true
            self.posts = []
            self.featurePost = nil
            analyticsService.articleListWasViewed(listType: .topic(topicId: topicId))
            
            Task {
                do {
                    self.posts = try await contentService.postsForTopic(topicId)
                    self.featurePost = nil
                    isLoading = false
                    self.pageToLoad = 2
                } catch {
                    isLoading = false
                }
            }
        default:
            self.posts = []
            self.featurePost = nil
        }
    }
    
    @State private var activePost: Post?
    @State private var activeCommentId: Int?
    
    @ViewBuilder var postDetailView: some View {
        if let post = activePost {
            PostDetailView(post: post, viewState: activeCommentId == nil ? .article : .conversation)
        } else {
            EmptyView()
        }
    }
    
    var body: some View {
        ZStack {
            let postDetailIsActive = Binding<Bool> {
                activePost != nil
            } set: { newValue in
                if !newValue {
                    activePost = nil
                }
            }

            NavigationLink(destination: postDetailView, isActive: postDetailIsActive) {
                EmptyView()
            }
            
            Text("No articles found.")
                .textStyle(.subheadline)
                .multilineTextAlignment(.center)
                .animation(.easeIn(duration: 2))
                .opacity((isLoading || !posts.isEmpty) ? 0 : 1)
            
            ProgressView(label: {
                Text("Loading articles...")
                    .textStyle(.caption1)
                }
            )
            .progressViewStyle(CircularProgressViewStyle())
            .opacity(isLoading ? 1 : 0)
            
            ScrollViewWithBanner(banner: banner, onRefresh: refreshContent, onLoadMore: loadMoreContent) { scrollProxy in
                PostListingView(featurePost: featurePost, posts: posts)
                    .onChange(of: displayOption, perform: { _ in
                        withAnimation {
                            scrollProxy.scrollTo("banner", anchor: .bottom)
                            setPosts()
                        }
                    })
            }
            .opacity(isLoading ? 0 : 1)
            
        }
        .overlay(
            BookmarksTipView()
        )
        .onAppear(perform: {
            activePost = nil
            activeCommentId = nil
        })
        .onReceive(userService.$openDeeplink, perform: { deeplink in
            if let deeplink = deeplink {
                handleDeeplink(deeplink)
            }
        })
        .onOpenURL { url in
            guard let deeplink = Deeplink(url: url) else {
                return
            }
            handleDeeplink(deeplink)
        }
        .onContinueUserActivity(NSUserActivityTypeBrowsingWeb, perform: { activity in
            guard let url = activity.webpageURL,
                let deeplink = Deeplink(url: url) else {
                return
            }
            handleDeeplink(deeplink)
        })
        .onReceive(contentService.$latestPosts.combineLatest(contentService.$topics), perform: { newPosts, newTopics in
            if !newPosts.isEmpty, !newTopics.isEmpty {
                isLoading = false
                DispatchQueue.main.async {
                    withAnimation {
                        setPosts()
                    }
                }
            }
        })
        .toolbar {
            ToolbarItem(placement: .navigationBarLeading) {
                ImageName.wordmark.image
                    .overlay(
                        ImageName.logo.image
                            .resizable()
                            .aspectRatio(contentMode: .fit)
                            .foregroundColor(topicColor)
                            .scaleEffect(0.72)
                            .offset(x: -21.5, y: 3.5)
                    )
            }
            
            ToolbarItem(placement: .navigationBarTrailing) {
                contextMenu()
            }
        }
    }
    
    @ViewBuilder func contextMenu() -> some View {
        Menu {
            
            Picker(selection: $displayOption, label: Text("Sorted by")) {
                Text(titleForDisplayOption(.latest))
                    .tag(DisplayOption.latest)
                
                Text(titleForDisplayOption(.trending))
                    .tag(DisplayOption.trending)
                
                ForEach(contentService.topics, id: \.self) { topic in
                    Text(titleForDisplayOption(.topic(topicId: topic.id)))
                        .tag(DisplayOption.topic(topicId: topic.id))
                }
            }
            
        } label: {
            HStack(alignment: .center, spacing: 4) {
                Text(titleForDisplayOption(displayOption))
                    .textStyle(.headline, color: ColorName.tint.color)
                    .multilineTextAlignment(.trailing)
                    .frame(minWidth: 100, alignment: .trailing)
                    .transition(.scale.combined(with: .opacity))
                    .id(displayOption)
                ImageName.System.chevronDown.image
                    .textStyle(.caption2)
            }

        }
    }
    
    private func titleForDisplayOption(_ displayOption: DisplayOption) -> String {
        switch displayOption {
            
        case .latest:
            return "Latest"
        case .trending:
            return "Trending"
        case .topic(topicId: let topicId):
            guard let topic = contentService.topics.first(where: { $0.id == topicId}) else {
                return "Topic"
            }
            return topic.name
            
        default:
            return ""
        }
    }
    
    private var banner: Banner? {
        contentService.bannerForPosition(displayOption)
    }
    
    private func refreshContent() {
        switch displayOption {
        case .topic:
            setPosts()
        default:
            contentService.refreshContent()
        }
    }
    
    @State private var pageToLoad: Int = 1
    @State private var isLoadingMore: Bool = false
    private func loadMoreContent() throws {
        
        Task {
            guard !isLoadingMore, pageToLoad > 1 else {
                return
            }
            
            let newPosts: [Post]
            
            switch displayOption {
            case .latest:
                isLoadingMore = true
                newPosts = try await contentService.latestPosts(page: pageToLoad)
            
            case .trending:
                isLoadingMore = true
                newPosts = try await contentService.trendingPosts(page: pageToLoad)
                
            case .topic(topicId: let topicId):
                isLoadingMore = true
                newPosts = try await contentService.postsForTopic(topicId, page: pageToLoad)
                
            default:
                return
            }
            
            DispatchQueue.main.async {
                self.posts = (self.posts + newPosts).unique()
                self.pageToLoad += 1
                isLoadingMore = false
            }
            
        }
    }
    
    private func handleDeeplink(_ deeplink: Deeplink) {
        switch deeplink {
        case .topic(let name):
            if let topic = contentService.topics.first(where: { $0.name.lowercased() == name.lowercased() }) {
                self.displayOption = .topic(topicId: topic.id)
                return
            }
        case .post(let id, let commentId):
            Task {
                do {
                    let post = try await contentService.hydratePost(id)
                    activeCommentId = commentId
                    activePost = post
                } catch {
                    activePost = nil
                    activeCommentId = nil
                }
            }
        }
    }
}

#if DEBUG
struct LatestArticlesView_Previews: PreviewProvider {
    static var previews: some View {
        LatestArticlesView()
    }
}
#endif
