import SwiftUI

struct WhatsOnView: View {
    private enum ViewState: String, CaseIterable {
        case calendar
        case editorial
        
        var title: String {
            rawValue.capitalized
        }
    }
    
    @EnvironmentObject var contentService: ContentService
    
    @State private var viewState: ViewState = .calendar
    
    private var validDates: [Date] {
        if showRecurringEvents {
            return Array(contentService.calendar.validDates.keys.sorted())
        }
        
        return validEvents.compactMap({ post in
            return post.events.first?.date
        })
        .unique()
        .sorted()
    }
    
    private var validEvents: [Post] {
        showRecurringEvents ? contentService.calendar.posts : contentService.calendar.posts.filter { $0.events.count == 1 }
    }
    
    private var topic: Topic? {
        contentService.topics.first(where: { $0.slug == "whats-on" })
    }
    @State private var posts: [Post] = []
    @State private var banner: Banner? = nil
    
    @State private var showCalendar: Bool = false
    @State private var selectedDate: Date? = nil
    @State private var showRecurringEvents: Bool = true
    
    private var showDatePicker: Bool {
        viewState == .calendar && showCalendar
    }
    
    var body: some View {
      
        ZStack(alignment: .top) {
            ColorName.background.color()
                .ignoresSafeArea()
            
            ScrollViewWithBanner(banner: banner,
                                 onRefresh: refreshContent,
                                 onLoadMore: loadMoreContent
            ) { scrollProxy in
                Color.clear
                    .frame(height: 0)
                    .onChange(of: viewState) { _ in
                        withAnimation {
                            scrollProxy.scrollTo("banner", anchor: .bottom)
                        }
                    }
                
                if viewState == .calendar {
                    EventListing(validDates: validDates,
                                 eventPosts: validEvents,
                                 selectedDate: $selectedDate,
                                 scrollProxy: scrollProxy)
                    
                } else {
                    PostListingView(posts: posts)
                }
            }
            .padding(.top, showDatePicker ? 110 : 0)
            
            DatePicker()
                .frame(height: showDatePicker ? 110 : 0)
                .clipped()
        }
        .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 )
            }
            ToolbarItem(placement: .navigationBarTrailing) {
                Button {
                    withAnimation {
                        showCalendar.toggle()
                        if !showCalendar {
                            showRecurringEvents = true
                            if let sDate = selectedDate,
                               let vDate = validDates.first,
                               !Calendar.current.isDate(sDate, inSameDayAs: vDate) {
                                self.selectedDate = validDates.first
                            }
                        }
                    }
                } label: {
                    ImageName.System.calendar.image
                        .foregroundColor(showCalendar ? ColorName.textOnTint.color : ColorName.tint.color)
                        .padding(2)
                        .background(RoundedRectangle(cornerRadius: 4).fill(showCalendar ? ColorName.tint.color : Color.clear ))
                }
                .opacity(viewState == .calendar ? 1 : 0)
            }
        }
        .onAppear {
            refreshContent()
        }
    }
        
    @ViewBuilder func DatePicker() -> some View {
        
        VStack(spacing: 0) {
            CalendarView(validDates: validDates, selectedDate: $selectedDate)
            
            Rectangle()
                .fill(ColorName.separator.color)
                .frame(height: 1)
                .opacity(0.5)
                .padding(.top, 3)
            
            Button {
                showRecurringEvents.toggle()
                self.selectedDate = validDates.first

            } label: {
                Text((showRecurringEvents ? "Hide" : "Show") + " recurring events")
                    .textStyle(.caption1, color: ColorName.tint.color)
                    .padding(10)
            }
        }
        .background(ColorName.background.color(variation: .secondary).ignoresSafeArea())
        .frame(height: 110)
    }
    
    private func refreshContent() {
        Task {
            do {
                guard let topicId = topic?.id else {
                    throw(RiotError.fetchError)
                }
                self.posts = try await contentService.postsForTopic(topicId)
                self.banner = contentService.bannerForPosition(ContentService.BannerPosition.topic(topicId: topicId))
                self.pageToLoad = 2
            } catch {
                self.posts = []
                self.banner = nil
            }
        }
    }
    
    @State private var pageToLoad: Int = 1
    @State private var isLoadingMore: Bool = false
    private func loadMoreContent() throws {
        
        Task {
            guard viewState == .editorial,
                  !isLoadingMore else {
                return
            }
            
            guard let topicId = topic?.id else {
                throw(RiotError.fetchError)
            }
            isLoadingMore = true
            let posts = try await contentService.postsForTopic(topicId, page: pageToLoad)
            DispatchQueue.main.async {
                self.posts = (self.posts + posts).unique()
                self.pageToLoad += 1
                isLoadingMore = false
            }
        }
    }
}

private struct EventListing: View {
    @EnvironmentObject var contentService: ContentService
    
    var validDates: [Date]
    var eventPosts: [Post]
    @Binding var selectedDate: Date?
    var scrollProxy: ScrollViewProxy?
    
    func titleForDate(_ date: Date) -> String {
        if Calendar.current.isDateInToday(date) {
            return "Today"
        } else
        if Calendar.current.isDateInTomorrow(date) {
            return "Tomorrow"
        }
        
        return DateFormatter.calendar.string(from: date)
    }
    
    private func postsForDate(_ date: Date) -> [Post] {
        return eventPosts.filter { post in
            guard let _ = post.events.first(where: { Calendar.current.isDate($0.date, inSameDayAs: date) }) else {
                return false
            }
            return true
        }.sorted { lhs, rhs in
            guard let lhsEvent = lhs.events.first(where: { Calendar.current.isDate($0.date, inSameDayAs: date) }),
                  let rhsEvent = rhs.events.first(where: { Calendar.current.isDate($0.date, inSameDayAs: date) }) else {
                return false
            }
            
            if lhsEvent.timeStart == rhsEvent.timeStart {
                return (lhsEvent.timeEnd ?? "0") < (rhsEvent.timeEnd ?? "0")
            }
            return (lhsEvent.timeStart ?? "A") < (rhsEvent.timeStart ?? "A")
        }
    }

    var body: some View {
        LazyVStack(alignment: .center, spacing: 10, pinnedViews: [.sectionHeaders]) {
            
            ForEach(validDates, id: \.self) { date in
                Section {
                    EventsForDayList(date: date, posts: self.postsForDate(date))
                        .padding(.horizontal)
                        .padding(.bottom, 26)
                        .padding(.top, 7)
                } header: {
                    VStack(alignment: .leading, spacing: 0) {
                        HStack {
                            Text(titleForDate(date))
                                .textStyle(.headline)
                            
                            Spacer()
                        }
                        .padding(.vertical, 10)
                        
                        Rectangle()
                            .fill(ColorName.tint.color)
                            .frame(height:1)
                        
                    }
                    .padding(.horizontal)
                    .background(ColorName.background.color())
                }
                .id(DateFormatter.short.string(from: date))
            }
        }
        .background(ColorName.background.color())
        
        .onChange(of: selectedDate) { selectedDate in
            if let date = selectedDate, let scrollProxy = scrollProxy, !validDates.isEmpty {
                let scrollId = DateFormatter.short.string(from: date)
                withAnimation {
                    scrollProxy.scrollTo(scrollId, anchor: .top)
                }
            }
        }
    }
}

private struct EventsForDayList: View {
    
    var date: Date
    var posts: [Post]
    
    var body: some View {
        VStack(alignment: .leading, spacing: 9) {
            ForEach(posts, id: \.self) { post in
                NavigationLink {
                    EventPostDetailView(post: post)
                } label: {
                    EventPostSummaryView(post: post,
                                         date: date,
                                         location: post.eventForDate(date)?.location,
                                         isLast: post == posts.last)
                }
                .buttonStyle(FlatButtonStyle())
            }
        }
    }
}

private struct EventPostSummaryView: View {
    var post: Post
    var date: Date
    var location: String?
    var isLast: Bool = false

    var body: some View {
        VStack(alignment: .leading, spacing: 9) {
            
            HStack(alignment: .top, spacing: 13) {
                if let url = post.thumbnail.square {
                    URLImage(url: url)
                        .frame(width: 64, height: 64)
                        .clipped()
                    
                }
                
                VStack(alignment: .leading, spacing: 0) {
                    Text(post.title.rendered)
                        .textStyle(.subheadline, color: ColorName.text.color(variation: .primary))
                        .multilineTextAlignment(.leading)
                    
                    if let location = location {
                        Text(location)
                            .textStyle(.caption1, color: ColorName.text.color(variation: .tertiary))
                            .padding(.top, 10)
                            .fixedSize(horizontal: false, vertical: true)
                            .multilineTextAlignment(.leading)
                    }
                }
                
                Spacer()
                
                VStack(alignment: .leading, spacing: 0) {
                    if let timeStart = post.eventStartTimeForDate(date, withPrefix: true) {
                        Text(timeStart)
                            .textStyle(.caption1, color: ColorName.text.color(variation: .secondary))
                    }
                    if let timeEnd = post.eventEndTimeForDate(date) {
                        Text(timeEnd)
                            .textStyle(.caption1, color: ColorName.text.color(variation: .tertiary))
                    }
                }
            }
            
            if !isLast {
                Rectangle()
                    .fill(ColorName.separator.color)
                    .frame(height:1)
            }
        }
        
    }
}

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

