//
//  LibraryImport.swift
//  Zockerhoehle
//
//  Created by Julian-Steffen Müller on 20.11.19.
//  Copyright © 2019 Julian-Steffen Müller. All rights reserved.
//

import Foundation
import UIKit
import CoreData

// MARK: - Library Import

class LibraryImport {
    static func backupName(from filename : String) -> String? {
        if filename.hasPrefix("libExp_") && filename.hasSuffix(".json") {
            var backupName = filename
            backupName.removeFirst("libExp_".count)
            backupName.removeLast(".json".count)
            return backupName
        }else{
            return .none
        }
    }
    func backupFiles() -> [String] {
        do {
            guard let documentDirectory = try ICloudManager.backup_folder else { return [] }
            
            let filesInDocumentsDir = try FileManager.default.contentsOfDirectory(at: documentDirectory, includingPropertiesForKeys: .none, options: [])
            
            let backupFiles = filesInDocumentsDir.filter{
                $0.isFileURL
            }.map {
                $0.lastPathComponent
            }.filter {
                $0.hasPrefix("libExp_") && $0.hasSuffix(".json")
            }.sorted(by: { $0 > $1 })
            
            return backupFiles
            
        }catch let error {
            print(error)
            print("LibraryImport::backupFiles - Error reading files from Document directory!")
        }
        
        return []
    }
    
    private func resetDatabase() {
        CDManager.shared.viewContext.reset()
        CDManager.shared.viewContext.performAndWait {
            let deleteRequests =
                [NSBatchDeleteRequest(fetchRequest: Game.fetchRequest()),
                 NSBatchDeleteRequest(fetchRequest: Accessory.fetchRequest()),
                 NSBatchDeleteRequest(fetchRequest: Console.fetchRequest()),
                 NSBatchDeleteRequest(fetchRequest: GameSeries.fetchRequest())]
            
            let storeCoordinator = CDManager.shared.persistentContainer.persistentStoreCoordinator
            do {
                for deleteRequest in deleteRequests {
                    try storeCoordinator.execute(deleteRequest, with: CDManager.shared.viewContext)
                }
            }catch let error {
                print(error)
                print("LibraryImport::resetDatabase - Reset of Database failes!")
            }
        }
    }
    
    private func makeCDGame(from game: BHLGame, _ gameDict: inout [UUID : Game],  _ cdConsole: Console) {
        let cdGame = Game(context: CDManager.shared.viewContext)
        gameDict[game.uuid] = cdGame
        cdGame.name = game.name ?? "n/a"
        cdGame.uuid = game.uuid
        cdGame.inWishlist = game.inWishlist
        cdGame.isFinished = game.isFinished
        cdGame.finishedDate = game.finishedDate
        cdGame.lentTo = game.lentTo
        cdGame.cover_icloud_path = game.cover_icloud_path
        cdGame.pickupDescription = game.pickupDescription
        cdGame.isDigital = game.isDigital
        
        if let date = Date.from(string: game.createdAt) {
            cdGame.createdAt = date
        }else{
            print("Could not decode date '\(game.createdAt)' for game '\(cdGame.name)'")
        }
        
        cdConsole.addToGames(cdGame)
        cdGame.console = cdConsole
        print("Imported: \(cdGame.name) for \(cdConsole.name)")
    }
    
    private func makeCDAccessory(from accessory: BHLAccessory, _ accessoryDict: inout [UUID : Accessory], _ cdConsole: Console) {
        let cdAccessory = Accessory(context: CDManager.shared.viewContext)
        accessoryDict[accessory.uuid] = cdAccessory
        cdAccessory.name = accessory.name
        cdAccessory.uuid = accessory.uuid
        cdAccessory.lentTo = accessory.lentTo
        cdAccessory.color = accessory.color
        cdAccessory.manufacturer = accessory.manufacturer
        cdAccessory.inWishlist = accessory.inWishlist
        
        cdConsole.addToAccessories(cdAccessory)
        cdAccessory.console = cdConsole
    }
    
    private func makeCDConsole(from console : BHLConsole) -> Console {
        let cdConsole = Console(context: CDManager.shared.viewContext)
        cdConsole.name = console.name ?? "n/a"
        cdConsole.uuid = console.uuid
        cdConsole.manufacturer = console.manufacturer
        cdConsole.shortName = console.shortName
        cdConsole.logo_icloud_path = console.logo_icloud_path
        cdConsole.releaseDate = console.releaseDate
        
        return cdConsole
    }
    
    private func makeCDGameSeries(from gameSeries: BHLGameSeries, _ gameDict: inout [UUID : Game]) {
        let cdGameSeries = GameSeries(context: CDManager.shared.viewContext)
        cdGameSeries.name = gameSeries.name
        cdGameSeries.cover_icloud_path = gameSeries.cover_icloud_path
        
        for uuid in gameSeries.games {
            if let game = gameDict[uuid] {
                cdGameSeries.addToGames(game)
                game.series = cdGameSeries
            }
        }
    }
    
    func importLIB(from filename: String) {
        do {
            guard let file = ICloudManager.backup_folder?.appendingPathComponent(filename) else {
                print("LibraryImport:importLIB Could not get icloud backup folder")
                return
            }
            
            if file.isFileURL {
                let library = try JSONDecoder().decode(BHLibrary.self, from: Data(contentsOf: file))
                resetDatabase()
                
                var gameDict = [UUID:Game]()
                var accessoryDict = [UUID:Accessory]()
                
                for console in library.consoles {
                    let cdConsole = makeCDConsole(from: console)
                    
                    print("CONSOLE: \(cdConsole.name ?? "n/a") with \(console.games.count) games")
                    for uuid in console.games {
                        
                        if let game = library.games.first(where: {$0.uuid == uuid}) {
                            makeCDGame(from: game, &gameDict, cdConsole)
                        }else{
                            print("LibraryImport::importLIB - Game with UUID '\(uuid)' not found")
                        }
                    }
                    
                    for uuid in console.accessories {
                        if let accessory = library.accessories.first(where: {$0.uuid == uuid}) {
                            makeCDAccessory(from: accessory, &accessoryDict, cdConsole)
                        }else{
                            print("LibraryImport::importLIB - Accessory with UUID '\(uuid)' not found")
                        }
                    }
                }
                
                for gameSeries in library.gameSeries {
                    makeCDGameSeries(from: gameSeries, &gameDict)
                }
            }
                
        }catch let error {
            print(error)
            print("LibraryImport::importLIB - Error while importing!")
        }
    }
}

// MARK: - BHLLibrary Classes

struct BHLibrary : Decodable {
    let games : [BHLGame]
    let consoles : [BHLConsole]
    let gameSeries : [BHLGameSeries]
    let accessories : [BHLAccessory]
}

struct BHLGame : Decodable {
    let uuid : UUID
    let name : String?
    let lentTo : String?
    let isDigital : Bool
    let inWishlist : Bool
    let isFinished : Bool
    let finishedDate : Date?
    let notes : String?
    let createdAt : String
    let pickupDescription : String?
    let publisher : String?
    let console : UUID
    let cover_icloud_path : String?
}

struct BHLAccessory : Decodable {
    let uuid : UUID
    let name : String
    let color : String?
    let manufacturer : String?
    let inWishlist : Bool
    let lentTo : String?
    let console : UUID?
}

struct BHLGameSeries : Decodable {
    let uuid : UUID
    let name : String
    let games : [UUID]
    let cover_icloud_path : String?
}

struct BHLConsole : Decodable {
    let uuid : UUID
    let name : String?
    let logo_icloud_path : String?
    let manufacturer : String?
    let releaseDate : Date
    let shortName : String?
    let accessories : [UUID]
    let games : [UUID]
}
