import SwiftUI

extension Comment.Source {
    var image: Image {
        switch self {
        case .wordpress:
            return ImageName.conversation.image
        case .facebook:
            return ImageName.facebookLogo.image
        }
    }
}

struct ConversationView: View {
    private enum ViewState: String, CaseIterable {
        case allComments = "Latest"
        case yourComments = "Yours"
        
        var title: String {
            rawValue.capitalized
        }
    }
    
    @EnvironmentObject var contentService: ContentService
    
    @State private var viewState: ViewState = .allComments
    
    var body: some View {
        ZStack {
            switch viewState {
            case .allComments:
                AllConversationView()
            case .yourComments:
                UserConversationView()
            }
        }
        .toolbar {
            ToolbarItem(placement: .principal) {
                let selection = Binding<Int> {
                    return ViewState.allCases.firstIndex(of: viewState) ?? 0
                } set: { newValue in
                    viewState = ViewState.allCases[newValue]
                }

                SegmentedControlView(selection: selection, items: ViewState.allCases.compactMap { $0.title })
                    .frame(width: UIDevice.current.userInterfaceIdiom == .pad ? 400 : 250 )
            }
        }
    }
}

struct AllConversationView: View {
    @EnvironmentObject var contentService: ContentService
        
    var body: some View {
        ZStack {
            if contentService.conversationPosts.isEmpty {
                ProgressView(label: {
                    Text("Loading articles...")
                        .textStyle(.caption1)
                    }
                ).progressViewStyle(CircularProgressViewStyle())
            }
            
            ScrollViewWithBanner(banner: contentService.bannerForPosition(.conversation)) {
                contentService.refreshContent()

            } content: { _ in
                VStack(alignment: .leading, spacing: 6) {
                    
                    ForEach(contentService.conversationPosts, id: \.self) { post in
                        PostConversations(post: post, showExcerpt: true, isLast: contentService.conversationPosts.last == post)
                    }
                    
                }
                .padding()
            }
        }
    }
}

struct PostConversations: View {
    @EnvironmentObject var userService: UserService
    @EnvironmentObject var analyticsService: AnalyticsService
    
    enum CommentSource {
        case loading
        case provided([Comment])
        case usePostLatest
    }
    
    var post: Post
    var showHeader: Bool = true
    var showExcerpt: Bool = false
    var commentSource: CommentSource = .usePostLatest
    var isLast: Bool = true
    
    @State private var awaitingLoginForComment: Bool = false
    @State private var presentCommentSheet: Bool = false
    
    private var commentsToDisplay: [Comment] {
        switch commentSource {
        case .loading:
            return []
        case .provided(let array):
            return array
        case .usePostLatest:
            return post.latestComments
        }
    }
    
    private var conversationButtonTitle: String {
        if case .signedIn = userService.signedInState {
            if commentsToDisplay.isEmpty {
                return "Start the conversation"
            } else {
                return "Join the conversation"
            }
            
        } else {
            if commentsToDisplay.isEmpty {
                return "Sign in to start the conversation"
            } else {
                return "Sign in to join the conversation"
            }
        }
    }
    
    private func conversationButtonAction() {
        if case .signedIn = userService.signedInState {
            presentCommentSheet = true
        } else {
            awaitingLoginForComment = true
            userService.signInWithViewController()
        }
    }
    
    var body: some View {
        VStack(alignment: .center, spacing: 0) {
            if showHeader {
                NavigationLink {
                    PostDetailView(post: post, viewState: .conversation)
                } label: {
                    PostHeaderView(summary: post.summary)
                }
                .buttonStyle(FlatButtonStyle())
                .padding(.bottom, 24)
            }
            
            if case .loading = commentSource {
                HStack {
                    Spacer()
                    
                    ProgressView(label: {
                        Text("Loading comments...")
                            .textStyle(.caption1)
                    }
                    ).progressViewStyle(CircularProgressViewStyle())
                    
                    Spacer()
                }
                
            } else {
                if !showHeader {
                    ConversationButton(postId: post.id,  hasComments: !commentsToDisplay.isEmpty)
                    .padding(.bottom, 24)
                }
                
                ForEach(commentsToDisplay, id: \.self) { comment in
                    NavigationLink {
                        CommentDetailView(comment: comment, postId: post.id)
                    } label: {
                        CommentView(comment: comment, seperatorStyle: comment == commentsToDisplay.first ? .standard : .indented, showExcerpt: showExcerpt)
                    }
                    .buttonStyle(FlatButtonStyle())
                }
                
                if showHeader {
                    ConversationButton(postId: post.id,  hasComments: !commentsToDisplay.isEmpty)
                    .padding(.bottom, 12)
                    .padding(.top, 24)
                    
                    NavigationLink {
                        PostDetailView(post: post, viewState: .conversation)
                    } label: {
                        Text("Read all comments")
                    }
                    .buttonStyle(SecondaryButtonStyle())
                    .padding(.bottom, 24)
                }
                
                
            }
            
            if !isLast {
                Rectangle()
                    .fill(ColorName.separator.color)
                    .frame(height: 1)
                    .padding(.bottom, 26)
            }
        }
        .onAppear {
            if !showHeader {
                analyticsService.articleCommentsWereViewed(postId: post.id)
            }
        }
    }
}

struct RiotProgressView: View {
    enum ProgressState: String {
        case ready
        case loading
        case success
        case error
    }
    
    var progressState: ProgressState = .ready
    var systemImageName: String? {
        switch progressState {
        case .success:
            return ImageName.System.success.name
        case .error:
            return ImageName.System.fail.name
        default:
            return nil
        }
    }
    
    @State private var shouldAnimate = false
    
    var body: some View {
        
        ZStack {
            ProgressView(label: {
                Text("Loading articles...")
                    .textStyle(.caption1)
                }
            )
            .progressViewStyle(CircularProgressViewStyle())
            .opacity(progressState == .loading ? 1 : 0)
            
            if let imageName = systemImageName {
                Image(systemName: imageName)
                    .textStyle(.largeTitle, color: ColorName.tint.color)
            }
            
        }
        .animation(.easeInOut, value: progressState)
        
    }
    
    
}

struct AddCommentView: View {
    @Environment(\.presentationMode) var presentationMode
    @EnvironmentObject var userService: UserService
    @EnvironmentObject var analyticsService: AnalyticsService
    
    var postId: Int
    var parentCommentId: Int?
    
    @State private var interfaceState: RiotProgressView.ProgressState = .ready
    @State private var comment: String = ""
    
    private var guidelineText: String {
        "Please read our guidelines on how to write constructive, thoughtful and positive comments. You will appear smarter, and are less likely to be moderated."
    }
    
    private var title: String {
        parentCommentId == nil ? "Your Comment" : "Your Reply"
    }
    
    var body: some View {
        NavigationView {
            ZStack {
                
                VStack {
                    TextEditor(text: $comment)
                        .textStyle(.body)
                        .padding(6)
                        .background(
                            RoundedRectangle(cornerRadius: 6)
                                .fill(ColorName.background.color(variation: .secondary))
                        )
                        .padding()
                    
                    Rectangle()
                        .fill(ColorName.separator.color)
                        .frame(height: 1)
                    
                    NavigationLink {
                        GuidelinesView()
                    } label: {
                        Text(guidelineText)
                            .textStyle(.caption1)
                    }
                    .buttonStyle(FlatButtonStyle())
                    .padding(.horizontal)

                }
                .opacity(interfaceState == .ready ? 1 : 0)
                
                if interfaceState != .ready {
                    RiotProgressView(progressState: interfaceState)
                        .onTapGesture {
                            if interfaceState == .error {
                                interfaceState = .ready
                            }
                        }
                }
            }
            .padding(.vertical)
            .background(ColorName.background.color().ignoresSafeArea())
            .navigationBarTitleDisplayMode(.inline)
            .navigationTitle(title)
            .toolbar {
                ToolbarItem(placement: .cancellationAction) {
                    Button {
                        cancel()
                    } label: {
                        Text("Cancel")
                            .textStyle(.body, color: ColorName.tint.color)
                    }

                }
                ToolbarItem(placement: .confirmationAction) {
                    Button {
                        withAnimation {
                            save()
                        }
                    } label: {
                        Text("Add")
                            .textStyle(.bodyBold, color: ColorName.tint.color)
                    }
                }
            }
        }
        .background(ColorName.background.color(variation: .secondary).ignoresSafeArea())
        .navigationViewStyle(StackNavigationViewStyle())
    }
    
    private func cancel() {
        presentationMode.wrappedValue.dismiss()
    }
    
    private func save() {
        interfaceState = .loading
        
        Task {
            do {
                let response = try await userService.addCommentToPost(comment, postId: postId, inReplyToComment: parentCommentId)
                analyticsService.commentWasPosted(postId: postId, commentId: response.id, inReplyTo: parentCommentId)
                interfaceState = .success
                
                DispatchQueue.main.asyncAfter(deadline: .now() + 0.75) {
                    presentationMode.wrappedValue.dismiss()
                }
                
            } catch {
                interfaceState = .error
            }
        }
    }
}

struct GuidelinesView: View {
    var body: some View {
        ScrollView {
            Text("""
        We believe that passionate debate between intelligent people who disagree creates better outcomes.
        
        Riotact’s comment utopia would be to see every article with 10+ comments from a variety of users who respond to the content of the article in a meaningful, positive and constructive way.
        
        For full details of our policies, including our moderation policy, see our terms and conditions.
        """
            )
            .textStyle(.body)
            .multilineTextAlignment(.leading)
            .padding()
        }
        .background(
            ColorName.background.color()
                .ignoresSafeArea()
        )
        .navigationTitle("Guidelines")
    }
}


struct UserConversationView: View {
    @EnvironmentObject var contentService: ContentService
    @EnvironmentObject var userService: UserService
    
    private func relatedPost(comment: Comment) -> Post? {
        hydratedPosts.first(where: { "\($0.id)" == comment.post })
    }
    
    @State private var hydratedPosts: [Post] = []
    
    private func loadHydratedPosts() {
        Task {
            let postIds: [Int] = userService.userComments?.posts.compactMap{ Int($0.id) } ?? []
            
            let posts = try await postIds.asyncMap { postId -> Post in
                return try await contentService.hydratePost(postId)
            }
            
            self.hydratedPosts = posts
        }
    }
    
    var body: some View {
        ZStack(alignment: .center) {
            ColorName.background.color()
                .ignoresSafeArea()
            
            switch contentService.signedInState {
            case .unknown, .signedOut, .error:
                VStack(alignment: .center, spacing: 26) {
                    Text("Sign in or sign up to make comments and follow the conversation.")
                        .textStyle(.subheadline)
                        .multilineTextAlignment(.center)
                    
                    Button(action: { userService.signInWithViewController() }) {
                        Text("Sign In")
                    }
                    .buttonStyle(SecondaryButtonStyle())
                
                }
                .padding()
                
            case .signedIn(_):
                if let comments = userService.userComments?.comments, !comments.isEmpty {
                    ZStack {
                        if hydratedPosts.isEmpty {
                            ProgressView(label: {
                                Text("Loading your comments...")
                                    .textStyle(.caption1)
                            }
                            ).progressViewStyle(CircularProgressViewStyle())
                        }
                        
                        ScrollView {
                            VStack(alignment: .leading, spacing: 6) {
                                ForEach(comments, id: \.self) { comment in
                                    
                                    if let post = relatedPost(comment: comment) {
                                        PostConversations(post: post, showExcerpt: true, commentSource: .provided([comment]))
                                        
                                        if comment != comments.last {
                                            Rectangle()
                                                .fill(ColorName.separator.color)
                                                .frame(height: 1)
                                                .padding(.bottom, 26)
                                        }
                                    }
                                }
                                
                                Spacer()
                            }
                            .padding()
                        }
                    }
                } else {
                    Text("You haven't made any comments yet.")
                        .textStyle(.subheadline)
                        .multilineTextAlignment(.center)
                }
            }
        }
        .onAppear {
            loadHydratedPosts()
        }
    }
}

struct CommentView: View {
    enum SeparatorStyle: Int {
        case standard = 0
        case indented
        case none
    }
    var comment: Comment
    var seperatorStyle: SeparatorStyle = .indented
    var showExcerpt: Bool = false
    var showReplyCount: Bool = true
    
    var body: some View {
        
        VStack(alignment: .leading, spacing: 3) {
            if seperatorStyle != .none {
                Rectangle()
                    .fill(ColorName.separator.color)
                    .frame(height: 1)
                    .padding(.bottom, 14)
                    .padding(.leading, seperatorStyle == .indented ? 36 : 0)
            }
            
            HStack(alignment: .top, spacing: 12) {
                Text(comment.source.image)
                    .textStyle(.headline, color: ColorName.tint.color)
                    .frame(width: 24)
                
                VStack(alignment: .leading, spacing: 3) {
                
                    HStack {
                        Text(comment.name)
                            .textStyle(.headline)
                            .multilineTextAlignment(.leading)
                        
                        Spacer()
                        
                        if let date = comment.date {
                            Text(RelativeDateTimeFormatter().localizedString(for: date, relativeTo: Date()) )
                                .textStyle(.caption2)
                        }
                    }
                    .padding(.bottom, 4)
                    
                    if showExcerpt {
                        Text(comment.excerpt.trimmingCharacters(in: .whitespacesAndNewlines))
                            .textStyle(.body)
                            .multilineTextAlignment(.leading)
                    } else {
                        Text(comment.content.trimmingCharacters(in: .whitespacesAndNewlines))
                            .textStyle(.body)
                            .multilineTextAlignment(.leading)
                    }
                    
                    if showReplyCount, comment.children.count > 0 {
                        Text(replyTitle)
                            .textStyle(.subheadline, color: ColorName.tint.color)
                            .padding(.top, 13)
                    }
                }
                .padding(.bottom, 25)
            }
        }

    }
    
    private var replyTitle: String {
        let replyCount = comment.children.count
        guard replyCount > 0 else {
            return ""
        }
        return "\(replyCount) \(replyCount == 1 ? "Reply" : "Replies")"
    }
}

#if DEBUG
struct ConversationView_Previews: PreviewProvider {
    static var previews: some View {
        ConversationView()
            .environmentObject(ContentService.preview)
    }
}
#endif
