//
//  DailyNotificationModel.xcdatamodeld
//  DailyNotificationPlugin
//
//  Created by Matthew Raymer on 2025-09-22
//  Copyright © 2025 TimeSafari. All rights reserved.
//

import Foundation
import CoreData

/**
 * Core Data model for Daily Notification Plugin
 * Mirrors Android SQLite schema for cross-platform consistency
 * 
 * @author Matthew Raymer
 * @version 1.1.0
 * @created 2025-09-22 09:22:32 UTC
 */

// MARK: - ContentCache Entity
@objc(ContentCache)
public class ContentCache: NSManagedObject {
    
}

extension ContentCache {
    @nonobjc public class func fetchRequest() -> NSFetchRequest<ContentCache> {
        return NSFetchRequest<ContentCache>(entityName: "ContentCache")
    }
    
    @NSManaged public var id: String?
    @NSManaged public var fetchedAt: Date?
    @NSManaged public var ttlSeconds: Int32
    @NSManaged public var payload: Data?
    @NSManaged public var meta: String?
}

extension ContentCache: Identifiable {
    
}

// MARK: - Schedule Entity
@objc(Schedule)
public class Schedule: NSManagedObject {
    
}

extension Schedule {
    @nonobjc public class func fetchRequest() -> NSFetchRequest<Schedule> {
        return NSFetchRequest<Schedule>(entityName: "Schedule")
    }
    
    @NSManaged public var id: String?
    @NSManaged public var kind: String?
    @NSManaged public var cron: String?
    @NSManaged public var clockTime: String?
    @NSManaged public var enabled: Bool
    @NSManaged public var lastRunAt: Date?
    @NSManaged public var nextRunAt: Date?
    @NSManaged public var jitterMs: Int32
    @NSManaged public var backoffPolicy: String?
    @NSManaged public var stateJson: String?
}

extension Schedule: Identifiable {
    
}

// MARK: - Callback Entity
@objc(Callback)
public class Callback: NSManagedObject {
    
}

extension Callback {
    @nonobjc public class func fetchRequest() -> NSFetchRequest<Callback> {
        return NSFetchRequest<Callback>(entityName: "Callback")
    }
    
    @NSManaged public var id: String?
    @NSManaged public var kind: String?
    @NSManaged public var target: String?
    @NSManaged public var headersJson: String?
    @NSManaged public var enabled: Bool
    @NSManaged public var createdAt: Date?
}

extension Callback: Identifiable {
    
}

// MARK: - History Entity
@objc(History)
public class History: NSManagedObject {
    
}

extension History {
    @nonobjc public class func fetchRequest() -> NSFetchRequest<History> {
        return NSFetchRequest<History>(entityName: "History")
    }
    
    @NSManaged public var id: String?
    @NSManaged public var refId: String?
    @NSManaged public var kind: String?
    @NSManaged public var occurredAt: Date?
    @NSManaged public var durationMs: Int32
    @NSManaged public var outcome: String?
    @NSManaged public var diagJson: String?
}

extension History: Identifiable {
    
}

// MARK: - NotificationContent Entity
@objc(NotificationContentEntity)
public class NotificationContentEntity: NSManagedObject {
    
}

extension NotificationContentEntity {
    @nonobjc public class func fetchRequest() -> NSFetchRequest<NotificationContentEntity> {
        return NSFetchRequest<NotificationContentEntity>(entityName: "NotificationContent")
    }
    
    @NSManaged public var id: String?
    @NSManaged public var pluginVersion: String?
    @NSManaged public var timesafariDid: String?
    @NSManaged public var notificationType: String?
    @NSManaged public var title: String?
    @NSManaged public var body: String?
    @NSManaged public var scheduledTime: Date?
    @NSManaged public var timezone: String?
    @NSManaged public var priority: Int32
    @NSManaged public var vibrationEnabled: Bool
    @NSManaged public var soundEnabled: Bool
    @NSManaged public var mediaUrl: String?
    @NSManaged public var encryptedContent: String?
    @NSManaged public var encryptionKeyId: String?
    @NSManaged public var createdAt: Date?
    @NSManaged public var updatedAt: Date?
    @NSManaged public var ttlSeconds: Int64
    @NSManaged public var deliveryStatus: String?
    @NSManaged public var deliveryAttempts: Int32
    @NSManaged public var lastDeliveryAttempt: Date?
    @NSManaged public var userInteractionCount: Int32
    @NSManaged public var lastUserInteraction: Date?
    @NSManaged public var metadata: String?
}

extension NotificationContentEntity: Identifiable {
    
}

// MARK: - NotificationDelivery Entity
@objc(NotificationDelivery)
public class NotificationDelivery: NSManagedObject {
    
}

extension NotificationDelivery {
    @nonobjc public class func fetchRequest() -> NSFetchRequest<NotificationDelivery> {
        return NSFetchRequest<NotificationDelivery>(entityName: "NotificationDelivery")
    }
    
    @NSManaged public var id: String?
    @NSManaged public var notificationId: String?
    @NSManaged public var notificationContent: NotificationContentEntity?
    @NSManaged public var timesafariDid: String?
    @NSManaged public var deliveryTimestamp: Date?
    @NSManaged public var deliveryStatus: String?
    @NSManaged public var deliveryMethod: String?
    @NSManaged public var deliveryAttemptNumber: Int32
    @NSManaged public var deliveryDurationMs: Int64
    @NSManaged public var userInteractionType: String?
    @NSManaged public var userInteractionTimestamp: Date?
    @NSManaged public var userInteractionDurationMs: Int64
    @NSManaged public var errorCode: String?
    @NSManaged public var errorMessage: String?
    @NSManaged public var deviceInfo: String?
    @NSManaged public var networkInfo: String?
    @NSManaged public var batteryLevel: Int32
    @NSManaged public var dozeModeActive: Bool
    @NSManaged public var exactAlarmPermission: Bool
    @NSManaged public var notificationPermission: Bool
    @NSManaged public var metadata: String?
}

extension NotificationDelivery: Identifiable {
    
}

// MARK: - NotificationConfig Entity
@objc(NotificationConfig)
public class NotificationConfig: NSManagedObject {
    
}

extension NotificationConfig {
    @nonobjc public class func fetchRequest() -> NSFetchRequest<NotificationConfig> {
        return NSFetchRequest<NotificationConfig>(entityName: "NotificationConfig")
    }
    
    @NSManaged public var id: String?
    @NSManaged public var timesafariDid: String?
    @NSManaged public var configType: String?
    @NSManaged public var configKey: String?
    @NSManaged public var configValue: String?
    @NSManaged public var configDataType: String?
    @NSManaged public var isEncrypted: Bool
    @NSManaged public var encryptionKeyId: String?
    @NSManaged public var createdAt: Date?
    @NSManaged public var updatedAt: Date?
    @NSManaged public var ttlSeconds: Int64
    @NSManaged public var isActive: Bool
    @NSManaged public var metadata: String?
}

extension NotificationConfig: Identifiable {
    
}

// MARK: - Persistence Controller
// Phase 2: CoreData integration for advanced features
// All entities now available: ContentCache, Schedule, Callback, History,
// NotificationContent, NotificationDelivery, NotificationConfig
class PersistenceController {
    // MARK: - Schema Versioning
    
    /// Current schema version (incremented when schema changes)
    /// This is a logical contract for observability, not a migration gate.
    /// CoreData auto-migration remains authoritative.
    private static let SCHEMA_VERSION = 1
    
    // Lazy initialization
    private static var _shared: PersistenceController?
    static var shared: PersistenceController {
        if _shared == nil {
            _shared = PersistenceController()
        }
        return _shared!
    }
    
    let container: NSPersistentContainer?
    private var initializationError: Error?
    
    init(inMemory: Bool = false) {
        var tempContainer: NSPersistentContainer? = nil
        
        do {
            tempContainer = NSPersistentContainer(name: "DailyNotificationModel")
            
            if inMemory {
                tempContainer?.persistentStoreDescriptions.first?.url = URL(fileURLWithPath: "/dev/null")
            }
            
            // Configure persistent store options
            let description = tempContainer?.persistentStoreDescriptions.first
            description?.shouldMigrateStoreAutomatically = true
            description?.shouldInferMappingModelAutomatically = true
            
            var loadError: Error? = nil
            tempContainer?.loadPersistentStores { storeDescription, error in
                if let error = error as NSError? {
                    loadError = error
                    print("DNP-PLUGIN: CoreData store load error: \(error.localizedDescription)")
                    print("DNP-PLUGIN: Error domain: \(error.domain), code: \(error.code)")
                    if let failureReason = error.userInfo[NSLocalizedFailureReasonErrorKey] as? String {
                        print("DNP-PLUGIN: Failure reason: \(failureReason)")
                    }
                } else {
                    print("DNP-PLUGIN: CoreData store loaded successfully")
                    print("DNP-PLUGIN: Store URL: \(storeDescription.url?.absoluteString ?? "unknown")")
                    
                    // Set initial schema version metadata (for new stores)
                    // Metadata must be set using the coordinator after the store is loaded
                    if !inMemory, 
                       let coordinator = tempContainer?.persistentStoreCoordinator,
                       let store = coordinator.persistentStores.first,
                       let metadata = store.metadata,
                       metadata["schema_version"] == nil {
                        var newMetadata = metadata
                        newMetadata["schema_version"] = PersistenceController.SCHEMA_VERSION
                        coordinator.setMetadata(newMetadata, for: store)
                    }
                }
            }
            
            if let error = loadError {
                self.initializationError = error
                self.container = nil
            } else {
                // Configure view context
                if let context = tempContainer?.viewContext {
                    context.automaticallyMergesChangesFromParent = true
                    context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
                }
                self.container = tempContainer
                
                // Check schema version (after container is initialized)
                checkSchemaVersion()
                
                // Verify all entities are available (after container is initialized)
                if let context = tempContainer?.viewContext {
                    verifyEntities(in: context)
                }
            }
        } catch {
            print("DNP-PLUGIN: Failed to initialize CoreData container: \(error.localizedDescription)")
            self.initializationError = error
            self.container = nil
        }
    }
    
    /**
     * Check if CoreData is available
     */
    var isAvailable: Bool {
        return container != nil && initializationError == nil
    }
    
    /**
     * Get the main view context
     * 
     * @return NSManagedObjectContext or nil if not available
     */
    var viewContext: NSManagedObjectContext? {
        return container?.viewContext
    }
    
    /**
     * Create a new background context for async operations
     * 
     * @return NSManagedObjectContext or nil if not available
     */
    func newBackgroundContext() -> NSManagedObjectContext? {
        return container?.newBackgroundContext()
    }
    
    /**
     * Save the view context
     * 
     * @return true if saved successfully, false otherwise
     */
    func save() -> Bool {
        guard let context = viewContext else {
            print("DNP-PLUGIN: Cannot save - CoreData not available")
            return false
        }
        
        if context.hasChanges {
            do {
                try context.save()
                print("DNP-PLUGIN: CoreData context saved successfully")
                return true
            } catch {
                print("DNP-PLUGIN: Error saving CoreData context: \(error.localizedDescription)")
                context.rollback()
                return false
            }
        }
        return true
    }
    
    /**
     * Check and log schema version
     * 
     * Schema version is a logical contract, not a forced migration trigger.
     * CoreData auto-migration remains authoritative; version mismatches are
     * logged, not blocked.
     */
    private func checkSchemaVersion() {
        guard let store = container?.persistentStoreCoordinator.persistentStores.first else {
            return
        }
        
        // store.metadata is optional, so we need to unwrap it
        guard let metadata = store.metadata else {
            print("DNP-PLUGIN: Store metadata is nil, using default schema version")
            return
        }
        
        let currentVersion = metadata["schema_version"] as? Int ?? 1
        let expectedVersion = PersistenceController.SCHEMA_VERSION
        
        if currentVersion != expectedVersion {
            print("DNP-PLUGIN: Schema version mismatch - current: \(currentVersion), expected: \(expectedVersion)")
            print("DNP-PLUGIN: CoreData auto-migration will handle schema changes")
            
            // Update metadata for future reference (does not trigger migration)
            // Use the coordinator to set metadata
            if let coordinator = container?.persistentStoreCoordinator {
                var newMetadata = metadata
                newMetadata["schema_version"] = expectedVersion
                coordinator.setMetadata(newMetadata, for: store)
                // Note: Metadata persists on next store save
            }
        } else {
            print("DNP-PLUGIN: Schema version verified: \(currentVersion)")
        }
    }
    
    /**
     * Verify all entities are available in the model
     * 
     * @param context Managed object context
     */
    private func verifyEntities(in context: NSManagedObjectContext) {
        guard let model = context.persistentStoreCoordinator?.managedObjectModel else {
            print("DNP-PLUGIN: Cannot verify entities - no managed object model")
            return
        }
        
        let entityNames = [
            "ContentCache",
            "Schedule",
            "Callback",
            "History",
            "NotificationContent",
            "NotificationDelivery",
            "NotificationConfig"
        ]
        
        var missingEntities: [String] = []
        for entityName in entityNames {
            if model.entitiesByName[entityName] == nil {
                missingEntities.append(entityName)
            }
        }
        
        if missingEntities.isEmpty {
            print("DNP-PLUGIN: All \(entityNames.count) entities verified in CoreData model")
        } else {
            print("DNP-PLUGIN: WARNING - Missing entities: \(missingEntities.joined(separator: ", "))")
        }
    }
}

