//
//  ContentView.swift
//  Shared
//
//  Created by Trey Tartt on 1/5/22.
//

import SwiftUI
import CoreData
import Charts

struct ContentViewConstants {
    static let maxHeaderHeight = 200.0
    static let minHeaderHeight = 88.0
}

struct ContentView: View {
    @Environment(\.managedObjectContext) private var viewContext
    @AppStorage(UserDefaultsStore.Keys.needsOnboarding.rawValue, store: GroupUserDefaults.groupDefaults) private var needsOnboarding = true
    @AppStorage(UserDefaultsStore.Keys.deleteEnable.rawValue, store: GroupUserDefaults.groupDefaults) private var deleteEnabled = true
    @AppStorage(UserDefaultsStore.Keys.mainViewTopHeaderIndex.rawValue, store: GroupUserDefaults.groupDefaults) private var mainViewTopHeaderIndex = 0
    @AppStorage(UserDefaultsStore.Keys.theme.rawValue, store: GroupUserDefaults.groupDefaults) private var theme: Theme = .system

    @State private var showingSheet = false
    @State private var showTodayInput = true
    @State private var selectedEntry: MoodEntry?
    @State private var showUpdateEntryAlert = false
    @State private var headerHeight: CGFloat = ContentViewConstants.maxHeaderHeight
    
    let minHeaderHeight = ContentViewConstants.minHeaderHeight
    let maxHeaderHeight = ContentViewConstants.maxHeaderHeight
    @State private var headerOpacity: Double = 1.0
    
    @ObservedObject var viewModel = ContentModeViewModel()
    
    init(){
        UIPageControl.appearance().currentPageIndicatorTintColor = UIColor.label
        UIPageControl.appearance().pageIndicatorTintColor = UIColor.black.withAlphaComponent(0.2)
        UITabBar.appearance().backgroundColor = theme.secondaryBGColor
    }
    
    var body: some View {
        TabView {
            mainView
                .tabItem {
                    Label(String(localized: "content_view_tab_main"), systemImage: "list.dash")
                }
            
            FilterView()
                .tabItem {
                    Label(String(localized: "content_view_tab_filter"), systemImage: "calendar.circle")
                }
            
            GraphView()
                .tabItem {
                    Label(String(localized: "content_view_tab_stats"), systemImage: "chart.line.uptrend.xyaxis")
                }
        }.sheet(isPresented: $needsOnboarding, onDismiss: {
            
        }, content: {
            OnboardingMain(onboardingData: viewModel.savedOnboardingData,
                           updateBoardingDataClosure: { onboardingData in
                needsOnboarding = false
                viewModel.updateOnboardingData(onboardingData: onboardingData)
            })
        }).alert(String(localized: "content_view_fill_in_missing_entry"), isPresented: $showUpdateEntryAlert) {
            ForEach(Mood.allValues) { mood in
                Button(mood.strValue, action: {
                    if let selectedEntry = selectedEntry {
                        viewModel.update(entry: selectedEntry, toMood: mood)
                    }
                    showUpdateEntryAlert = false
                })
            }
            
            if let selectedEntry = selectedEntry,
               deleteEnabled,
               selectedEntry.mood != .missing {
                Button(String(localized: "content_view_delete_entry"), action: {
                    viewModel.update(entry: selectedEntry, toMood: Mood.missing)
                    showUpdateEntryAlert = false
                })
            }
            
            Button(String(localized: "content_view_fill_in_missing_entry_cancel"), role: .cancel,  action: {
                selectedEntry = nil
                showUpdateEntryAlert = false
            })
        }
    }
    
    private var settingsButtonView: some View {
        HStack {
            Spacer()
            Button(action: {
                showingSheet.toggle()
            }, label: {
                Image(systemName: "gear")
                    .foregroundColor(Color(UIColor.darkGray))
                    .font(.system(size: 20))
            }).sheet(isPresented: $showingSheet) {
                SettingsView(editedDataClosure: {
                    withAnimation{
                        viewModel.updateData()
                    }
                }, updateBoardingDataClosure: { onboardingData in
                    viewModel.updateOnboardingData(onboardingData: onboardingData)
                })
            }.padding(.trailing)
        }
    }
    
    private func weekdayName(fromDate date: Date) -> String {
        let weekday = Calendar.current.component(.weekday, from: date)
        let calendar = Calendar.current
        let dayIndex = ((weekday - 1) + (calendar.firstWeekday - 1)) % 7
        return calendar.weekdaySymbols[dayIndex]
    }
    
    private func monthName(fromMonthInt: Int) -> String {
        let monthName = DateFormatter().monthSymbols[fromMonthInt-1]
        return monthName
    }
    
    private func dayFormat(fromDate date: Date) -> String {
        let components = Calendar.current.dateComponents([.day], from: date)
        let day = components.day!
        
        let formatter = NumberFormatter()
        formatter.numberStyle = .ordinal
        return formatter.string(from: NSNumber(integerLiteral: day)) ?? ""
    }
    
    func calcuateViewAlpha() {
        let perc = (((Double(headerHeight) - minHeaderHeight) * 100) / (maxHeaderHeight - minHeaderHeight)) / 100
        headerOpacity = perc
    }
    
    func calculateHeight(minHeight: CGFloat, maxHeight: CGFloat, yOffset: CGFloat) {
        let newValue = maxHeight + yOffset
        
        calcuateViewAlpha()
        // If scrolling up, yOffset will be a negative number
        if newValue < minHeight {
            // SCROLLING UP
            // Never go smaller than our minimum height
            headerHeight = minHeight
            return
        }
        
        if newValue > maxHeight {
            // SCROLLING UP
            // Never go smaller than our minimum height
            headerHeight = maxHeight
            return
        }
        
        // SCROLLING DOWN
        headerHeight = newValue
    }
    
    private var listView: some View {
        ScrollView {
            LazyVStack(spacing: 5, pinnedViews: [.sectionHeaders]) {
                ForEach(viewModel.grouped.sorted(by: {
                    $0.key > $1.key
                }), id: \.key) { year, months in
                    
                    // for reach month
                    ForEach(months.sorted(by: {
                        $0.key > $1.key
                    }), id: \.key) { month, entries in
                       Section(header: SectionHeaderView(month: month, year: year)) {
                            monthListView(month: month, year: year, entries: entries)
                        }
                    }
                }
            }.background(
              GeometryReader { proxy in
                    let offset = proxy.frame(in: .named("scroll")).minY
                    Color.clear.preference(key: ViewOffsetKey.self, value: offset)
                }
            )
        }
        .background(
            Color(theme.secondaryBGColor)
        )
        .coordinateSpace(name: "scroll")
        .onPreferenceChange(ViewOffsetKey.self) { value in
            if viewModel.numberOfItems > 10 {
                calculateHeight(minHeight: ContentViewConstants.minHeaderHeight,
                                maxHeight: ContentViewConstants.maxHeaderHeight,
                                yOffset: value)
            }
        }
        .cornerRadius(10, corners: [.topLeft, .topRight])
    }
    
    private func SectionHeaderView(month: Int, year: Int) -> some View {
        Text("\(monthName(fromMonthInt: month)) \(String(year))")
            .font(.title)
            .foregroundColor(Color(UIColor.label))
            .frame(maxWidth: .infinity, alignment: .leading)
            .padding()
            .background(
                Color(theme.secondaryBGColor)
            )
    }
    
    private func monthListView(month: Int, year: Int, entries: [MoodEntry]) -> some View {
        VStack {
            // for reach all entries
            ForEach(entries.sorted(by: {
                return $0.forDate! > $1.forDate!
            }), id: \.self) { entry in
                entryListView(entry: entry)
                    .contentShape(Rectangle())
                    .onTapGesture(perform: {
                        selectedEntry = entry
                        showUpdateEntryAlert = true
                    })
            }
        }
    }

    private func entryListView(entry: MoodEntry) -> some View {
        HStack {
            entry.mood.icon
                .resizable()
                .aspectRatio(contentMode: .fit)
                .frame(width: 40, height: 40, alignment: .center)
                .foregroundColor(entry.mood.color)
            
            VStack {
                HStack {
                    Text(weekdayName(fromDate:entry.forDate!))
                        .font(.title3)
                        .foregroundColor(Color(UIColor.label))
                    Text(" - ")
                        .padding([.leading, .trailing], -10)
                    Text(dayFormat(fromDate:entry.forDate!))
                        .font(.title3)
                        .foregroundColor(Color(UIColor.label))
                    Spacer()
                }
                .multilineTextAlignment(.leading)
                
                Text(entry.moodValue == Mood.missing.rawValue ? String(localized: "mood_value_missing_tap_to_add") :  "\(entry.moodString)")
                    .font(.body)
                    .foregroundColor(Color(UIColor.systemGray))
                    .frame(maxWidth: .infinity, alignment: .leading)
            }
        }
    }
    
    private var headerView: some View {
        VStack {
            if viewModel.shouldShowVotingHeader() {
                AddMoodHeaderView(addItemHeaderClosure: { (mood, date) in
                    withAnimation {
                        viewModel.add(mood: mood, forDate: date)
                    }
                })
                    .frame(height: headerHeight)
                    .frame(minWidth: 0, maxWidth: .infinity)
            } else {
                // selection hre doesn't work ...
                TabView {
                    SwitchableView(daysBack: 30)
                        .frame(height: headerHeight)
                        .frame(minWidth: 0, maxWidth: .infinity)
                        .contentShape(Rectangle())
                    
                    SwitchableView(daysBack: 7)
                        .frame(height: headerHeight)
                        .frame(minWidth: 0, maxWidth: .infinity)
                        .contentShape(Rectangle())
                }
                .tabViewStyle(.page)
                .padding([.trailing, .leading])
            }
        }
    }
    
    private var emptyView: some View {
        ZStack {
            Color(theme.secondaryBGColor)
            
            VStack {
                Text(String(localized: "content_view_empty_title"))
                    .font(.title)
                    .foregroundColor(Color(UIColor.label))
                    .padding()
                
                Text(String(localized: "content_view_empty_title"))
                    .font(.body)
                    .foregroundColor(Color(UIColor.label))
                    .padding()
                AddMoodHeaderView(addItemHeaderClosure: { (mood, date) in
                    withAnimation {
                        viewModel.add(mood: mood, forDate: date)
                    }
                }, overrideDay: viewModel.shouldShowVotingHeader() ? .Today : .Previous)
            }
        }
        .fixedSize(horizontal: false, vertical: true)
        .cornerRadius(10, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight])
        .padding()
    }
    
    private var mainView: some View {
        VStack {
            settingsButtonView
            if viewModel.hasNoData {
                Spacer()
                emptyView
                Spacer()
            } else {
                ZStack {
                    VStack {
                        headerView
                        Spacer()
                    }
                    .opacity(headerOpacity)
                    
                    VStack {
                        SmallRollUpHeaderView(fakeData: false, backDays: 30)
                            .background(
                                Color(theme.secondaryBGColor)
                            )
                            .cornerRadius(10, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight])
                            .padding()
                            .padding([.top, .bottom], 5)
                        
                        Spacer()
                    }
                    .opacity(1 - headerOpacity)
                }
                .frame(height: headerHeight + 20)
                listView
                    .padding([.leading, .trailing])
                    .padding(.top, -25)
            }
        }.background(
            theme.bg
                .edgesIgnoringSafeArea(.all)
        )
    }
}

struct ViewOffsetKey: PreferenceKey {
    typealias Value = CGFloat
    static var defaultValue = CGFloat.zero
    static func reduce(value: inout Value, nextValue: () -> Value) {
        value += nextValue()
    }
}

private let itemFormatter: DateFormatter = {
    let formatter = DateFormatter()
    formatter.dateStyle = .short
    formatter.timeStyle = .medium
    return formatter
}()

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView().environment(\.managedObjectContext, PersistenceController.shared.viewContext)
            .onAppear(perform: {
                PersistenceController.shared.populateMemory()
            })
        
        ContentView()
            .preferredColorScheme(.dark)
            .environment(\.managedObjectContext, PersistenceController.shared.viewContext)
    }
}
