//
//  PersistentStorage.swift
//  WOKA
//
//  Created by MacBook Pro on 05/08/24.
//

import Foundation
import CoreData

/**
 Hey there, I hope you enjoyed the video, if you have any questions then please feel free to ask I will be happy to answer them.
 Do share this with your iOS group on whatsapp or facebook or anyone who wants to learn iOS
 */

enum PersistentStorageEnum : String{
    case UserClicks
    case click_counts
    case category_id
    case post_id
    case post_type
}

enum PostType: Int {
    case series = 1
    case season = 2
    case episode = 3
    case video = 4
    // case paint = 5
    case game = 6
    case audio = 7
    case karaokeVideo = 8
    case shopProduct = 9
    // case parentalVideo = 10
    // case article = 11
    case liveTV = 12
    case FM = 13
    case teaser = 14
    case others = 15
    case home = 16
}

struct UserClickData {
    let clickCounts: Int
    let categoryId: Int
    let postId: Int
    let postType: Int
}

// MARK: - Clicks
struct ClicksAnalytics : Codable {
    let postID, postType, numberOfClicks, deviceType: Int?
    let categoryID: Int?

    enum CodingKeys: String, CodingKey {
        case postID = "post_id"
        case postType = "post_type"
        case numberOfClicks = "number_of_clicks"
        case deviceType = "device_type"
        case categoryID = "category_id"
    }
}

// MARK: - ADs Impressions & Clicks
struct AdsClickImpressionsData : Codable {
    let adID, noOfClick, noOfOmpression: Int

    enum CodingKeys: String, CodingKey {
        case adID = "ad_id"
        case noOfClick = "no_of_click"
        case noOfOmpression = "no_of_impression"
    }
}

//struct UserVideoViewData {
//    let postId: Int
//    let postType: Int
//    let watchedDuration: Double
//    let categoryId: Int
//}

final class PersistentStorage
{
    
    private init(){}
    static let shared = PersistentStorage()
    
    // MARK: - Core Data stack
    
    lazy var persistentContainer: NSPersistentContainer = {
        
        let container = NSPersistentContainer(name: "WOKA")
        container.loadPersistentStores(completionHandler: { (storeDescription, error) in
            if let error = error as NSError? {
                
                fatalError("Unresolved errorsss \(error), \(error.userInfo)")
            }
        })
        return container
    }()
    
    lazy var context = persistentContainer.viewContext
    // MARK: - Core Data Saving support
    
    func saveContext() {
        if context.hasChanges {
            do {
                try context.save()
            } catch {
                let nserror = error as NSError
                fatalError("Unresolved errorsss \(nserror), \(nserror.userInfo)")
            }
        }
    }
    
    func createData(data : UserClickData){
        
        //We need to create a context from this container
        let managedContext = PersistentStorage.shared.context
        
        let share = UserClicks(context: managedContext)
        share.click_counts = Int64(data.clickCounts)
        share.category_id = Int64(data.categoryId)
        share.post_id = Int64(data.postId)
        share.post_type = Int64(data.postType)
        
        do {
            try managedContext.save()
            retrieveData(postID: data.postId, catID: data.categoryId, postType: data.postType)
        } catch let error as NSError {
            print("Could not save. \(error), \(error.userInfo)")
        }
    }
    
    func checkIfExist( key : PersistentStorageEnum , clicksData : UserClickData) {
        
        let managedContext = PersistentStorage.shared.context
        let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: PersistentStorageEnum.UserClicks.rawValue)
        //        fetchRequest.fetchLimit =  1
        //        fetchRequest.predicate = NSPredicate(format: "id == %d" ,id)
        fetchRequest.predicate = NSPredicate(format: "\(key.rawValue) == %@ AND post_id == %@" ,clicksData.postType.toString(),clicksData.postId.toString())
        
        do {
            guard let result = try managedContext.fetch(fetchRequest) as? [UserClicks] else {return}
            if result.isEmpty{
                //create
                PersistentStorage.shared.createData(data: clicksData)
                print("create, In Exist")
            }else{
                //update
                let objectUpdate = result[0] as NSManagedObject
                print("Update, In Exist")
                objectUpdate.setValue(result.first!.click_counts + Int64(clicksData.clickCounts), forKey: "click_counts")
                do{
                    try managedContext.save()
                    retrieveData(postID: clicksData.postId, catID: clicksData.categoryId, postType: clicksData.postType)
                }
                catch
                {
                    print(error)
                }
            }
            //            result.forEach { clicks in
            //                print("Counts" , clicks.click_counts)
            //            }
        }catch let error as NSError {
            print("Could not fetch. \(error), \(error.userInfo)")
        }
    }
    
    func checkWebSeries(clicksData : UserClickData) {
        let managedContext = PersistentStorage.shared.context
        let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: PersistentStorageEnum.UserClicks.rawValue)
        fetchRequest.fetchLimit =  1
        fetchRequest.predicate = NSPredicate(format: "post_id == %@ AND category_id == %@" ,clicksData.postId.toString(), clicksData.categoryId.toString())
        
        do {
            guard let result = try managedContext.fetch(fetchRequest) as? [UserClicks] else {return}
            if result.isEmpty{
                //create
                PersistentStorage.shared.createData(data: clicksData)
                print("create Main")
            }else{
                //update
                let objectUpdate = result[0] as NSManagedObject
                print("Update Main")
                objectUpdate.setValue(result.first!.click_counts + Int64(clicksData.clickCounts), forKey: "click_counts")
                do{
                    try managedContext.save()
                    retrieveData(postID: clicksData.postId, catID: clicksData.categoryId, postType: clicksData.postType)
                }
                catch
                {
                    print(error)
                }
            }
        }catch let error as NSError {
            print("Could not fetch. \(error), \(error.userInfo)")
        }
    }
    
    func retrieveData(postID : Int?, catID : Int?, postType : Int) {
        
        //We need to create a context from this container
        let managedContext = PersistentStorage.shared.context
        
        let path = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
        //        debugPrint(path[0])
        let fetchRequest:NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "UserClicks")
        
        fetchRequest.predicate = NSPredicate(format: "post_id == %@ AND category_id == %@ AND post_type == %@" ,postID?.toString() ?? "0", catID?.toString() ?? "0" ,postType.toString())
        
        //        fetchRequests.fetchLimit = 1
        //        fetchRequests.sortDescriptors = [NSSortDescriptor.init(key: "uuid", ascending: false)]
        do {
            guard let result = try managedContext.fetch(fetchRequest) as? [UserClicks] else {return}
            result.forEach { clicks in
                print("ID:-" , PostType(rawValue: Int(clicks.post_type))!, "CatID:- ", clicks.category_id, "PostID:- ", clicks.post_id , "Count:-", clicks.click_counts)
            }
        }
        catch let error
        {
            debugPrint(error)
        }
    }
    
    func getAllData() {
        //We need to create a context from this container
        let managedContext = PersistentStorage.shared.context
        let fetchRequest:NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "UserClicks")
        fetchRequest.fetchLimit = 10
        do {
            guard let result = try managedContext.fetch(fetchRequest) as? [UserClicks] else {return}
            result.forEach { clicks in
                // device type 1- android , 2 - iOS
                print("ID:-" , PostType(rawValue: Int(clicks.post_type))!, "CatID:- ", clicks.category_id, "PostID:- ", clicks.post_id , "Count:-", clicks.click_counts)
            }
        }
        catch let error
        {
            debugPrint(error)
        }
    }
    
    func sendDataToServer() {
        
        //We need to create a context from this container
        let managedContext = PersistentStorage.shared.context
        
//        let path = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
        //        debugPrint(path[0])
        let fetchRequest:NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "UserClicks")
        fetchRequest.fetchLimit = 15
        do {
            guard let result = try managedContext.fetch(fetchRequest) as? [UserClicks] else {return}
            var userClicks = [ClicksAnalytics]()
            result.forEach { clicks in
                // device type 1- android , 2 - iOS
                userClicks.append(ClicksAnalytics(postID: Int(clicks.post_id), postType: Int(clicks.post_type), numberOfClicks: Int(clicks.click_counts), deviceType: 2, categoryID: Int(clicks.category_id)))
                
                print("ID:-" , PostType(rawValue: Int(clicks.post_type))!, "CatID:- ", clicks.category_id, "PostID:- ", clicks.post_id , "Count:-", clicks.click_counts)
            }
            NetworkManager.shareInstance.nwCallRawJSON(clicksData: userClicks) { isDone in
                if isDone{
                    self.deleteData(result)
                }
            }
        }
        catch let error
        {
            debugPrint(error)
        }
    }
        
    func deleteData(_ data: [UserClicks]) {
        let managedContext = PersistentStorage.shared.context

        data.forEach { clicks in
            managedContext.delete(clicks)
        }

        do {
            try managedContext.save()
            getAllData()
            print("Deleted data")
        } catch let error {
            debugPrint("Failed to delete data:", error)
        }
    }
    
    
    
    // MARK: - Handle Clicks For UserClicks
    
    func addOthersCount(){
        let userClicks = UserClickData(clickCounts: 1, categoryId: 0, postId: 0, postType: PostType.others.rawValue)
        PersistentStorage.shared.checkIfExist( key: .post_type,clicksData: userClicks)
    }
    
    func addKaraokeCount(postID : Int){
        let userClicks = UserClickData(clickCounts: 1, categoryId: 0, postId: postID, postType: PostType.karaokeVideo.rawValue)
        PersistentStorage.shared.checkIfExist( key: .post_type,clicksData: userClicks)
    }
    
    func addAudioCount(postID : Int){
        let userClicks = UserClickData(clickCounts: 1, categoryId: 0, postId: postID, postType: PostType.audio.rawValue)
        PersistentStorage.shared.checkIfExist( key: .post_type,clicksData: userClicks)
    }

    func addGamesCount(postID : Int, count : Int = 1){
        let userClicks = UserClickData(clickCounts: count, categoryId: 0, postId: postID, postType: PostType.game.rawValue)
        PersistentStorage.shared.checkIfExist( key: .post_type,clicksData: userClicks)
    }
    
    func addShopCount(postID : Int){
        let userClicks = UserClickData(clickCounts: 1, categoryId: 0, postId: postID, postType: PostType.shopProduct.rawValue)
        PersistentStorage.shared.checkIfExist( key: .post_type,clicksData: userClicks)
    }
    
    func addRadioCount(){
        guard let postID = AuthFunc.shareInstance.staticURLs?.liveFmData?.id else{return}
        let userClicks = UserClickData(clickCounts: 1, categoryId: 0, postId: postID, postType: PostType.FM.rawValue)
        PersistentStorage.shared.checkIfExist( key: .post_type,clicksData: userClicks)
    }
    
    func addLiveTVCount(){
        guard let postID = AuthFunc.shareInstance.staticURLs?.liveData?.first?.id else{return}
        let userClicks = UserClickData(clickCounts: 1, categoryId: 0, postId: postID, postType: PostType.liveTV.rawValue)
        PersistentStorage.shared.checkIfExist( key: .post_type,clicksData: userClicks)
    }
    
    func addTrailerCount(){
        let userClicks = UserClickData(clickCounts: 1, categoryId: 0, postId: 0, postType: PostType.teaser.rawValue)
        PersistentStorage.shared.checkIfExist( key: .post_type,clicksData: userClicks)
    }
    
    func addWebSeries(catID : Int, postID : Int, postType : PostType){
        let userClicks = UserClickData(clickCounts: 1, categoryId: catID, postId: postID, postType: postType.rawValue)
        PersistentStorage.shared.checkWebSeries(clicksData: userClicks)
    }
    // MARK: - Handling ADS
    
    func sendAdsData() {
        //create a context from this container
        let managedContext = PersistentStorage.shared.context
        
        let fetchRequest:NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "AdClicksImpressions")
        fetchRequest.fetchLimit = 15
        do {
            guard let result = try managedContext.fetch(fetchRequest) as? [AdClicksImpressions] else {return}
            var userImpressions = [AdsClickImpressionsData]()
            result.forEach { ads in
                // device type 1- android , 2 - iOS
                userImpressions.append(AdsClickImpressionsData(adID: Int(ads.ad_id), noOfClick: Int(ads.no_of_click), noOfOmpression: Int(ads.no_of_impression)))
                
                print("ID:-" , ads.ad_id, "No Of Clicks :- ", ads.no_of_click, "Impressions :- ", ads.no_of_impression)
            }
            NetworkManager.shareInstance.nwCallRawJSONAds(adsData: userImpressions) { isDone in
                if isDone{
                    self.deleteAdsData(result)
                }
            }
        }
        catch let error
        {
            debugPrint(error)
        }
    }
    
    func checkIfAdExist(adsData : AdsClickImpressionsData) {
        
        let managedContext = PersistentStorage.shared.context
        let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "AdClicksImpressions")
        fetchRequest.predicate = NSPredicate(format: "ad_id == %@" ,adsData.adID.toString())
        
        do {
            guard let result = try managedContext.fetch(fetchRequest) as? [AdClicksImpressions] else {return}
            if result.isEmpty{
                //create
                PersistentStorage.shared.createAdsData(data: adsData)
                print("create, In Exist")
            }else{
                //update
                let objectUpdate = result[0] as NSManagedObject
                print("Update, In Exist")
                objectUpdate.setValue(result.first!.no_of_click + Int64(adsData.noOfClick), forKey: "no_of_click")
                objectUpdate.setValue(result.first!.no_of_impression + Int64(adsData.noOfOmpression), forKey: "no_of_impression")
                do{
                    try managedContext.save()
                    retrieveAdsData(adID: adsData.adID)
                }
                catch
                {
                    print(error)
                }
            }
        }catch let error as NSError {
            print("Could not fetch. \(error), \(error.userInfo)")
        }
    }
    
    func createAdsData(data : AdsClickImpressionsData){
        
        //We need to create a context from this container
        let managedContext = PersistentStorage.shared.context
        
        let share = AdClicksImpressions(context: managedContext)
        share.ad_id = Int64(data.adID)
        share.no_of_click = Int64(data.noOfClick)
        share.no_of_impression = Int64(data.noOfOmpression)
        
        do {
            try managedContext.save()
            retrieveAdsData(adID: data.adID)
        } catch let error as NSError {
            print("Could not save. \(error), \(error.userInfo)")
        }
    }
    
    func retrieveAdsData(adID : Int?) {
        let managedContext = PersistentStorage.shared.context
        let fetchRequest:NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "AdClicksImpressions")
        
        fetchRequest.predicate = NSPredicate(format: "ad_id == %@",adID?.toString() ?? 0)
        do {
            guard let result = try managedContext.fetch(fetchRequest) as? [AdClicksImpressions] else {return}
            result.forEach { ads in
                print("ID:-" , ads.ad_id, "No Of Clicks :- ", ads.no_of_click, "Impressions :- ", ads.no_of_impression)
            }
        }
        catch let error
        {
            debugPrint(error)
        }
    }
    
    func addAdsCount(adID : Int, impressions : Int = 0, clicks : Int = 0){
        let adsData = AdsClickImpressionsData(adID: adID, noOfClick: clicks, noOfOmpression: impressions)
        PersistentStorage.shared.checkIfAdExist( adsData: adsData)
    }
    
    func deleteAdsData(_ data: [AdClicksImpressions]) {
        let managedContext = PersistentStorage.shared.context

        data.forEach { clicks in
            managedContext.delete(clicks)
        }

        do {
            try managedContext.save()
            getAllData()
            print("Deleted data")
        } catch let error {
            debugPrint("Failed to delete data:", error)
        }
    }
}
