//
//  ContentView.swift
//  LeCountdown
//
//  Created by Laurent Morvillier on 20/01/2023.
//

import SwiftUI
import CoreData
import Combine
//import CloudKit

class BoringContext : ObservableObject {
    
    @Published var isShowingNewData = false
    @Published var error: Error?
    @Published var showDefaultAlert: Bool = false
    @Published var showPermissionAlert: Bool = false
    
}

class TimerSpot : Identifiable, Equatable {
    
    var id: Int16 { return self.order }
    
    var order: Int16
    var timer: AbstractTimer?
    
    init(order: Int16, timer: AbstractTimer? = nil) {
        self.order = order
        self.timer = timer
    }
    
    func setOrder(order: Int16) {
        self.order = order
        self.timer?.order = order
    }
    
    static func == (lhs: TimerSpot, rhs: TimerSpot) -> Bool {
        return lhs.order == rhs.order && lhs.timer?.stringId == rhs.timer?.stringId
    }

}

//class TimersModel : ObservableObject {
//
//    @Published var spots: [TimerSpot] = []
//
//}

struct ContentView<T : AbstractTimer>: View {

    @StateObject var boringContext: BoringContext = BoringContext()
    @EnvironmentObject var conductor: Conductor

    @Environment(\.managedObjectContext) private var viewContext
    
    #if os(iOS)
    @Environment(\.horizontalSizeClass) private var horizontalSizeClass
    #endif
    
    @FetchRequest(
        sortDescriptors: [NSSortDescriptor(keyPath: \T.order, ascending: true)],
        animation: .default)
    private var timers: FetchedResults<T>
    
//    var coreDataPublisher: NotificationCenter.Publisher { NotificationCenter.default
//        .publisher(for: .NSManagedObjectContextDidSave, object: viewContext) }
    
    @State private var isEditing: Bool = false
    
    fileprivate let itemSpacing: CGFloat = 10.0

    var body: some View {
        
        let columns: [GridItem] = self._columns()
        
        GeometryReader { reader in
            let width: CGFloat = reader.size.width / CGFloat(columns.count) - 15.0
            
            VStack {
                
                ScrollView {
                    
                    LazyVGrid(
                        columns: columns,
                        spacing: itemSpacing
                    ) {
                        
                        ReorderableForEach(items: Array(timers)) { timer in
                            
                            DialView(timer: timer, isEditingBinding: self.$isEditing, frameSize: width)
                                .environment(\.managedObjectContext, viewContext)
                                .environmentObject(Conductor.maestro)
                                .environmentObject(boringContext)
                        } moveAction: { from, to in
                            self._reorder(from: from, to: to)
                        }
                        
//                        if !self.isEditing {
//
//
//                        }
//                        else {
//
//                            ReorderableForEach(items: self.model.spots) { spot in
//
//                                if let timer = spot.timer {
//                                    DialView(timer: timer, isEditingBinding: self.$isEditing, frameSize: width)
//                                        .environment(\.managedObjectContext, viewContext)
//                                        .environmentObject(Conductor.maestro)
//                                        .environmentObject(boringContext)
//
//                                } else {
//
//                                    Color(white: 0.9)
//                                        .frame(width: width, height: 80.0)
//                                        .cornerRadius(20.0)
//                                }
//
//                            } moveAction: { from, to in
//                                self._reorderSpots(from: from, to: to)
//                            }
//                        }
                    }
                }.padding(.horizontal, itemSpacing)
                
                if !conductor.liveTimers.isEmpty {
                    LiveTimerListView()
                        .environment(\.managedObjectContext, viewContext)
                        .environmentObject(conductor)
                        .background(Color(white: 0.9))
                        .padding(.bottom, 40.0)
                        .cornerRadius(16.0, corners: [.topRight, .topLeft])
                }
            }
        }
        .navigationTitle("Home")
        .alert(boringContext.error?.localizedDescription ?? "missing error", isPresented: $boringContext.showDefaultAlert) {
            Button("OK", role: .cancel) { }
        }
        .alert("You need to accept notifications, please check your settings", isPresented: $boringContext.showPermissionAlert) {
            PermissionAlertView()
        }
        .sheet(isPresented: $boringContext.isShowingNewData, content: {
            self._newView(isPresented: $boringContext.isShowingNewData)
                .environment(\.managedObjectContext, viewContext)
        })
        .toolbar {
            ToolbarItem(placement: .navigationBarTrailing) {
                Button {
                    self.boringContext.isShowingNewData = true
                } label: {
                    HStack {
                        Image(systemName: "plus")
                    }
                }
            }
            ToolbarItem(placement: .navigationBarLeading) {
                Button {
                    withAnimation {
                        self.isEditing.toggle()
                    }
                } label: {
                    Text(self.isEditing ? "Done" : "Edit")
                }
            }
        }
//        .onReceive(cloudkitPublisher, perform: { _ in
//            print(">>>cloudkitPublisher")
//            withAnimation {
//                self._buildItemsList()
//            }
//        })
//        .onReceive(coreDataPublisher, perform: { _ in
//            withAnimation {
//                self._buildItemsList()
//            }
//        })
        .onAppear {
//            self._buildItemsList()
            self._askPermissions()
        }
        .onOpenURL { url in
            self._performActionIfPossible(url: url)
        }
        
    }
    
    fileprivate func _columnCount() -> Int {
        #if os(iOS)
        if horizontalSizeClass == .compact {
            return 2
        } else {
            return 3
        }
        #else
        return 3
        #endif
    }
    
    fileprivate func _columns() -> [GridItem] {
        return (0..<self._columnCount()).map { _ in GridItem(spacing: 10.0) }
    }
    
//    fileprivate func _buildItemsList() {
//
//        var spots: [TimerSpot] = []
//
//        let more: Int = self.isEditing ? 20 : 0 // add 20 empty spots when editing
//
//        let count = max(self.timers.count, Int(self.timers.last?.order ?? 0) + 1) + more
//
//        for i in 0..<count {
//            let timer = self.timers.first(where: { $0.order == i })
//            let spot = TimerSpot(order: Int16(i), timer: timer)
//            spots.append(spot)
//        }
//        self.model.spots = spots
//    }
    
//    fileprivate func _saveOrder() {
//        for (i, spot) in self.model.spots.enumerated() {
//            spot.setOrder(order: Int16(i))
//        }
//        do {
//            try viewContext.save()
//        } catch {
//            self.boringContext.error = error
//        }
//    }
    
    // MARK: - Subviews
    
    @ViewBuilder
    fileprivate func _newView(isPresented: Binding<Bool>) -> some View {
        switch T.self {
        case is Countdown.Type:
            NewCountdownView(isPresented: isPresented)
        case is Alarm.Type:
            NewAlarmView(isPresented: isPresented)
        case is Stopwatch.Type:
            NewStopwatchView(isPresented: isPresented)
        default:
            NewDataView(isPresented: isPresented)
        }
        
    }
    
    // MARK: - Business

//    fileprivate func _reorderSpots(from: IndexSet, to: Int) {
//        var spots: [TimerSpot] = self.model.spots
//        spots.move(fromOffsets: from, toOffset: to)
//        self.model.spots = spots
//    }
    
    fileprivate func _reorder(from: IndexSet, to: Int) {
        var timers: [AbstractTimer] = Array(self.timers)
        timers.move(fromOffsets: from, toOffset: to)
        for (i, countdown) in timers.enumerated() {
            countdown.order = Int16(i)
        }
        do {
            try viewContext.save()
        } catch {
            Logger.error(error)
            self.boringContext.error = error
        }
    }
    
    fileprivate func _askPermissions() {
        UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .criticalAlert]) { success, error in
            print("requestAuthorization > success = \(success), error = \(String(describing: error))")
        }
    }
    
    fileprivate func _performActionIfPossible(url: URL) {
        
        // hide new window if launching a timer
        self.boringContext.isShowingNewData = false
        
        print("_performActionIfPossible")
        
        let urlString = url.absoluteString
        if let timer = viewContext.object(stringId: urlString) as? AbstractTimer {
            
            if timer is T {
                print("performAction")

                TimerRouter.performAction(timer: timer) { result in
                    switch result {
                    case .success:
                        break
                    case .failure(let failure):
                        switch failure {
                        case TimerError.notificationAuthorizationMissing:
                            self.boringContext.showPermissionAlert = true
                        default:
                            self.boringContext.error = failure
                            self.boringContext.showDefaultAlert = true
                        }
                    }
                }

            }
            
//            print("Start countdown: \(countdown.name ?? ""), \(countdown.duration)")
//            self._launchCountdown(countdown)
        } else {
            print("timer not found with id = \(urlString)")
        }
        
    }
    
}

struct MainToolbarView: View {
    
    var isShowingNewData: Binding<Bool>
    
    var body: some View {
        Button {
            self.isShowingNewData.wrappedValue = true
        } label: {
            HStack {
                Image(systemName: "timer")
                Text("countdown")
            }
        }
        Button {
            self.isShowingNewData.wrappedValue = true
        } label: {
            HStack {
                Image(systemName: "stopwatch")
                Text("stopwatch")
            }
        }
        Button {
            self.isShowingNewData.wrappedValue = true
        } label: {
            HStack {
                Image(systemName: "alarm")
                Text("alarm")
            }
        }

    }
}

fileprivate extension Countdown {
    
    var endDate: Date? {
        return Conductor.maestro.currentCountdowns[self.stringId]?.end
    }
    
    var isLive: Bool {
        return endDate != nil
    }
    
    var colorForStatus: Color {
        if isLive {
            return Color(red: 0.9, green: 1.0, blue: 0.95)
        } else {
            return Color(red: 0.9, green: 0.95, blue: 1.0)
        }
    }
    
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView<Countdown>().environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)                    .environmentObject(Conductor.maestro)

    }
}
