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

import SwiftUI
import CoreData
import Charts

struct DayViewConstants {
    static let maxHeaderHeight = 200.0
    static let minHeaderHeight = 120.0
}

struct DayView: View {
    @Environment(\.managedObjectContext) private var viewContext

    @AppStorage(UserDefaultsStore.Keys.deleteEnable.rawValue, store: GroupUserDefaults.groupDefaults) private var deleteEnabled = true
    
    @AppStorage(UserDefaultsStore.Keys.theme.rawValue, store: GroupUserDefaults.groupDefaults) private var theme: Theme = .system

    @AppStorage(UserDefaultsStore.Keys.moodImages.rawValue, store: GroupUserDefaults.groupDefaults) private var imagePack: MoodImages = .FontAwesome
    @AppStorage(UserDefaultsStore.Keys.moodTint.rawValue, store: GroupUserDefaults.groupDefaults) private var moodTint: MoodTints = .Default
    @AppStorage(UserDefaultsStore.Keys.textColor.rawValue, store: GroupUserDefaults.groupDefaults) private var textColor: Color = DefaultTextColor.textColor

    // store a value that gets changed when user updates custom colors to update the view since the moodTint doesn't change
    @AppStorage(UserDefaultsStore.Keys.customMoodTintUpdateNumber.rawValue, store: GroupUserDefaults.groupDefaults) private var customMoodTintUpdateNumber: Int = 0
    
    // MARK: top header storage
    @AppStorage(UserDefaultsStore.Keys.contentViewCurrentSelectedHeaderViewBackDays.rawValue, store: GroupUserDefaults.groupDefaults) private var currentSelectedHeaderViewBackDays: Int = 30
    @AppStorage(UserDefaultsStore.Keys.contentViewHeaderTagViewOneViewType.rawValue, store: GroupUserDefaults.groupDefaults) private var firstSwichableHeaderViewType: MainSwitchableViewType = .total
    @AppStorage(UserDefaultsStore.Keys.contentViewHeaderTagViewTwoViewType.rawValue, store: GroupUserDefaults.groupDefaults) private var secondSwichableHeaderViewType: MainSwitchableViewType = .total
    @AppStorage(UserDefaultsStore.Keys.contentViewHeaderTag.rawValue, store: GroupUserDefaults.groupDefaults) private var switchableViewSelectedIndex = 1
    @AppStorage(UserDefaultsStore.Keys.currentSelectedHeaderViewViewType.rawValue, store: GroupUserDefaults.groupDefaults) private var currentSelectedHeaderViewViewType: MainSwitchableViewType = .total
    //
    
    // MARK: edit row properties
    @State private var showingSheet = false
    @State private var selectedEntry: MoodEntry?
    //
    
    // MARK: ?? properties
    @State private var showTodayInput = true
    @State private var showUpdateEntryAlert = false
    @StateObject private var onboardingData = OnboardingDataDataManager.shared
    @StateObject private var filteredDays = DaysFilterClass.shared
    
    // MARK: header properties
    @State private var headerHeight: CGFloat = DayViewConstants.maxHeaderHeight
    @State private var headerViewType: MainSwitchableViewType = .total
    @State private var headerOpacity: Double = 1.0
    //
    
    @ObservedObject var viewModel: DayViewViewModel

    var body: some View {
        mainView
            .onAppear(perform: {
                EventLogger.log(event: "show_home_view")
            })
            .sheet(isPresented: $showingSheet) {
                SettingsView()
            }
            .alert(DayViewViewModel.updateTitleHeader(forEntry: selectedEntry),
                   isPresented: $showUpdateEntryAlert) {
                ForEach(Mood.allValues) { mood in
                    Button(mood.strValue, action: {
                        if let selectedEntry = selectedEntry {
                            viewModel.update(entry: selectedEntry, toMood: mood)
                        }
                        showUpdateEntryAlert = false
                        selectedEntry = nil
                    })
                }
                
                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
                })
            }
    }
    
    // MARK: functions that do view type work
    func calcuateViewAlpha() {
        let perc = (((Double(headerHeight) - DayViewConstants.minHeaderHeight) * 100) / (DayViewConstants.maxHeaderHeight - DayViewConstants.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
    }
        
    // MARK: Views
    public var mainView: some View {
        VStack {
            settingsButtonView
            if viewModel.hasNoData {
                Spacer()
                EmptyHomeView(viewModel: viewModel)
                Spacer()
            } else {
                ZStack {
                    
                    Text(String(customMoodTintUpdateNumber))
                        .hidden()
                    
                    VStack {
                        headerView
                        Spacer()
                    }
                    .opacity(headerOpacity)

                    VStack {
                        SmallRollUpHeaderView(entries: getBackEntries(),
                                              viewType: $currentSelectedHeaderViewViewType)
                            .frame(height: DayViewConstants.minHeaderHeight)
                            .padding([.trailing, .leading])
                            .background(
                                theme.currentTheme.secondaryBGColor
                            )
                            .cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight])

                        Spacer()
                    }
                    .opacity(1 - headerOpacity)
                }
                .frame(height: headerHeight + 20)
                
                listView
                    .padding(.top, -25)
            }
        }
        .padding()
        .padding(.bottom, 5)
        .background(
            theme.currentTheme.bg
                .edgesIgnoringSafeArea(.all)
        )
        .onReceive(NotificationCenter.default.publisher(for: UIApplication.willEnterForegroundNotification)) { _ in
            PersistenceController.shared.fillInMissingDates()
            viewModel.updateData()
        }
    }
    
    private var settingsButtonView: some View {
        HStack {
            Spacer()
            Button(action: {
                showingSheet.toggle()
            }, label: {
                Image(systemName: "gear")
                    .foregroundColor(Color(UIColor.darkGray))
                    .font(.system(size: 20))
            }).padding(.trailing)
        }
    }
    
    private var headerView: some View {
        VStack {
            if ShowBasedOnVoteLogics.isMissingCurrentVote(onboardingData: onboardingData.savedOnboardingData) {
                AddMoodHeaderView(addItemHeaderClosure: { (mood, date) in
                    viewModel.add(mood: mood, forDate: date, entryType: .header)
                })
                    .frame(height: headerHeight)
                    .frame(minWidth: 0, maxWidth: .infinity)
            } else {
                // selection hre doesn't work ...
                TabView(selection: $switchableViewSelectedIndex) {
                    SwitchableView(daysBack: 30,
                                   viewType: $firstSwichableHeaderViewType,
                                   headerTypeChanged: { viewType in
                        firstSwichableHeaderViewType = viewType
                        currentSelectedHeaderViewViewType = firstSwichableHeaderViewType
                    })
                        .tag(1)
                        .frame(height: headerHeight)
                        .frame(minWidth: 0, maxWidth: .infinity)
                        .contentShape(Rectangle())

                    SwitchableView(daysBack: 7,
                                   viewType: $secondSwichableHeaderViewType,
                                   headerTypeChanged: { viewType in
                        secondSwichableHeaderViewType = viewType
                        currentSelectedHeaderViewViewType = secondSwichableHeaderViewType
                    })
                        .tag(2)
                        .frame(height: headerHeight)
                        .frame(minWidth: 0, maxWidth: .infinity)
                        .contentShape(Rectangle())
                }
                .tabViewStyle(.page)
                .onChange(of: switchableViewSelectedIndex) { value in
                    if value == 1 {
                        currentSelectedHeaderViewBackDays = 30
                        currentSelectedHeaderViewViewType = firstSwichableHeaderViewType
                    }

                    if value == 2 {
                        currentSelectedHeaderViewBackDays = 7
                        currentSelectedHeaderViewViewType = secondSwichableHeaderViewType
                    }
                }
            }
        }
    }
    
    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(
            theme.currentTheme.secondaryBGColor
        )
        .coordinateSpace(name: "scroll")
        .onPreferenceChange(ViewOffsetKey.self) { value in
            if viewModel.numberOfItems > 10 {
                calculateHeight(minHeight: DayViewConstants.minHeaderHeight,
                                maxHeight: DayViewConstants.maxHeaderHeight,
                                yOffset: value)
            }
        }
        .cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight])
    }
    
    func getBackEntries() -> [MoodEntry] {
        var daysAgo = Calendar.current.date(byAdding: .day, value: -self.currentSelectedHeaderViewBackDays, to: Date())!
        daysAgo = Calendar.current.date(bySettingHour: 0, minute: 0, second: 0, of: daysAgo)!
        
        return PersistenceController.shared.getData(startDate: daysAgo, endDate: Date(), includedDays: [1,2,3,4,5,6,7])
    }
}

// view that make up the list body
extension DayView {
    private func SectionHeaderView(month: Int, year: Int) -> some View {
        Text("\(Random.monthName(fromMonthInt: month)) \(String(year))")
            .font(.title)
            .foregroundColor(textColor)
            .frame(maxWidth: .infinity, alignment: .leading)
            .padding()
            .background(
                theme.currentTheme.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
                if filteredDays.currentFilters.contains(Int(entry.weekDay)) {
                    EntryListView(entry: entry)
                        .contentShape(Rectangle())
                        .onTapGesture(perform: {
                            selectedEntry = entry
                            showUpdateEntryAlert = true
                        })
                }
            }
        }
    }
}

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 DayView_Previews: PreviewProvider {
    static var previews: some View {
        DayView(viewModel: DayViewViewModel(addMonthStartWeekdayPadding: false)).environment(\.managedObjectContext, PersistenceController.shared.viewContext)
            .onAppear(perform: {
                PersistenceController.shared.populateMemory()
            })
        
        DayView(viewModel: DayViewViewModel(addMonthStartWeekdayPadding: false))
            .preferredColorScheme(.dark)
            .environment(\.managedObjectContext, PersistenceController.shared.viewContext)
    }
}
