/**
 * HistoryDAO.swift
 * 
 * Data Access Object (DAO) for History Core Data entity
 * Provides helper methods for recording recovery and operation history
 * 
 * @author Matthew Raymer
 * @version 1.0.0
 * @created 2025-12-08
 */

import Foundation
import CoreData

/**
 * Extension providing DAO methods for History entity
 * 
 * This extension adds helper methods for recording operation history
 * including recovery operations, errors, and metrics.
 */
extension History {
    
    // MARK: - Constants
    
    private static let TAG = "DNP-HISTORY-DAO"
    
    // MARK: - Create/Insert Methods
    
    /**
     * Create a new History entity in the given context
     * 
     * @param context Core Data managed object context
     * @param id Unique history identifier (UUID recommended)
     * @param refId Reference ID (e.g., notification ID, schedule ID)
     * @param kind History kind (e.g., "recovery", "fetch", "notify")
     * @param occurredAt When the event occurred
     * @param durationMs Duration in milliseconds
     * @param outcome Outcome string (e.g., "success", "failure", "skipped")
     * @param diagJson Diagnostic JSON string with additional details
     * @return Created History entity
     */
    static func create(
        in context: NSManagedObjectContext,
        id: String,
        refId: String? = nil,
        kind: String,
        occurredAt: Date,
        durationMs: Int32 = 0,
        outcome: String,
        diagJson: String? = nil
    ) -> History {
        let entity = History(context: context)
        
        entity.id = id
        entity.refId = refId
        entity.kind = kind
        entity.occurredAt = occurredAt
        entity.durationMs = durationMs
        entity.outcome = outcome
        entity.diagJson = diagJson
        
        print("\(Self.TAG): Created History record: kind=\(kind), outcome=\(outcome)")
        return entity
    }
    
    /**
     * Create history record from dictionary
     * 
     * @param context Core Data managed object context
     * @param dict Dictionary with history data
     * @return Created History entity or nil
     */
    static func create(
        in context: NSManagedObjectContext,
        from dict: [String: Any]
    ) -> History? {
        guard let id = dict["id"] as? String,
              let kind = dict["kind"] as? String,
              let outcome = dict["outcome"] as? String else {
            print("\(Self.TAG): Missing required fields")
            return nil
        }
        
        // Convert occurredAt from epoch milliseconds or Date
        let occurredAt: Date
        if let timeMillis = dict["occurredAt"] as? Int64 {
            occurredAt = DailyNotificationDataConversions.dateFromEpochMillis(timeMillis)
        } else if let timeDate = dict["occurredAt"] as? Date {
            occurredAt = timeDate
        } else {
            occurredAt = Date()
        }
        
        let entity = History(context: context)
        entity.id = id
        entity.refId = dict["refId"] as? String
        entity.kind = kind
        entity.occurredAt = occurredAt
        entity.durationMs = DailyNotificationDataConversions.int32FromInt(
            dict["durationMs"] as? Int ?? 0
        )
        entity.outcome = outcome
        
        // Convert diagJson from dictionary if needed
        if let diagDict = dict["diagJson"] as? [String: Any] {
            entity.diagJson = DailyNotificationDataConversions.jsonStringFromDictionary(diagDict)
        } else if let diagString = dict["diagJson"] as? String {
            entity.diagJson = diagString
        }
        
        return entity
    }
    
    /**
     * Record recovery history with metrics
     * 
     * @param context Core Data managed object context
     * @param scenario Recovery scenario
     * @param missedCount Number of missed notifications
     * @param rescheduledCount Number of rescheduled notifications
     * @param verifiedCount Number of verified notifications
     * @param errors Number of errors
     * @param startTime When recovery started
     * @param endTime When recovery ended
     * @return Created History entity
     */
    static func recordRecovery(
        in context: NSManagedObjectContext,
        scenario: String,
        missedCount: Int,
        rescheduledCount: Int,
        verifiedCount: Int,
        errors: Int,
        startTime: Date,
        endTime: Date
    ) -> History {
        let durationMs = Int32((endTime.timeIntervalSince(startTime) * 1000).rounded())
        
        let diagJson: [String: Any] = [
            "scenario": scenario,
            "missedCount": missedCount,
            "rescheduledCount": rescheduledCount,
            "verifiedCount": verifiedCount,
            "errors": errors,
            "durationMs": durationMs
        ]
        
        let diagJsonString = DailyNotificationDataConversions.jsonStringFromDictionary(diagJson) ?? "{}"
        
        return create(
            in: context,
            id: UUID().uuidString,
            kind: "recovery",
            occurredAt: endTime,
            durationMs: durationMs,
            outcome: errors > 0 ? "partial_success" : "success",
            diagJson: diagJsonString
        )
    }
    
    /**
     * Record recovery failure
     * 
     * @param context Core Data managed object context
     * @param error Error that occurred
     * @param scenario Recovery scenario (if known)
     * @return Created History entity
     */
    static func recordRecoveryFailure(
        in context: NSManagedObjectContext,
        error: Error,
        scenario: String? = nil
    ) -> History {
        var errorInfo: [String: Any] = [
            "error": error.localizedDescription,
            "errorType": String(describing: type(of: error))
        ]
        
        // Add scenario if provided
        if let scenario = scenario {
            errorInfo["scenario"] = scenario
        }
        
        // Add error details if available
        if let nsError = error as NSError? {
            errorInfo["errorCode"] = nsError.code
            errorInfo["errorDomain"] = nsError.domain
            if let userInfo = nsError.userInfo as? [String: Any] {
                errorInfo["userInfo"] = userInfo
            }
        }
        
        let diagJsonString = DailyNotificationDataConversions.jsonStringFromDictionary(errorInfo) ?? "{}"
        
        return create(
            in: context,
            id: UUID().uuidString,
            kind: "recovery",
            occurredAt: Date(),
            outcome: "failure",
            diagJson: diagJsonString
        )
    }
    
    // MARK: - Read/Query Methods
    
    /**
     * Fetch History by ID
     * 
     * @param context Core Data managed object context
     * @param id History ID
     * @return History entity or nil
     */
    static func fetch(
        by id: String,
        in context: NSManagedObjectContext
    ) -> History? {
        let request: NSFetchRequest<History> = History.fetchRequest()
        request.predicate = NSPredicate(format: "id == %@", id)
        request.fetchLimit = 1
        
        do {
            let results = try context.fetch(request)
            return results.first
        } catch {
            print("\(Self.TAG): Error fetching by id: \(error.localizedDescription)")
            return nil
        }
    }
    
    /**
     * Query by kind
     * 
     * @param context Core Data managed object context
     * @param kind History kind
     * @return Array of History entities
     */
    static func query(
        by kind: String,
        in context: NSManagedObjectContext
    ) -> [History] {
        let request: NSFetchRequest<History> = History.fetchRequest()
        request.predicate = NSPredicate(format: "kind == %@", kind)
        request.sortDescriptors = [NSSortDescriptor(key: "occurredAt", ascending: false)]
        
        do {
            return try context.fetch(request)
        } catch {
            print("\(Self.TAG): Error querying by kind: \(error.localizedDescription)")
            return []
        }
    }
    
    /**
     * Query by refId
     * 
     * @param context Core Data managed object context
     * @param refId Reference ID
     * @return Array of History entities
     */
    static func queryByRefId(
        _ refId: String,
        in context: NSManagedObjectContext
    ) -> [History] {
        let request: NSFetchRequest<History> = History.fetchRequest()
        request.predicate = NSPredicate(format: "refId == %@", refId)
        request.sortDescriptors = [NSSortDescriptor(key: "occurredAt", ascending: false)]
        
        do {
            return try context.fetch(request)
        } catch {
            print("\(Self.TAG): Error querying by refId: \(error.localizedDescription)")
            return []
        }
    }
    
    /**
     * Query by outcome
     * 
     * @param context Core Data managed object context
     * @param outcome Outcome string
     * @return Array of History entities
     */
    static func queryByOutcome(
        _ outcome: String,
        in context: NSManagedObjectContext
    ) -> [History] {
        let request: NSFetchRequest<History> = History.fetchRequest()
        request.predicate = NSPredicate(format: "outcome == %@", outcome)
        request.sortDescriptors = [NSSortDescriptor(key: "occurredAt", ascending: false)]
        
        do {
            return try context.fetch(request)
        } catch {
            print("\(Self.TAG): Error querying by outcome: \(error.localizedDescription)")
            return []
        }
    }
}

