//
//  Entries.swift
//  SpeedKeys
//
//  Created by Jose Polo Ramos on 2/26/25.
//

import Foundation

/// Enum defining the different types of entry properties that can be modified
enum EntryType {
    case app        // Application name
    case command    // Shell command to execute
    case shortcut   // Keyboard shortcut
    case path       // File path to open
}

/// Main data structure that manages the collection of keyboard shortcut entries
/// Handles persistence via UserDefaults and provides CRUD operations
struct Entries: CustomStringConvertible {
    
    /// Custom string representation for debugging
    /// Concatenates all entry descriptions with newlines
    var description: String {
        var str: String = ""
        for entry in entries {
            str = str + "\n" + entry.description
        }
        return "Entries:: [\(str) ]"
    }
    
    // Array holding all keyboard shortcut entries
    var entries: [Entry] = []
    
    // Tracks the highest key index to generate unique keys
    var maxKey: Int = 0
    
    // Flag to track if data has been modified (for UI updates)
    var changed: Bool = false

    // Singleton instance - only one Entries object exists
    static var instance = Entries()
    
    /// Private initializer to enforce singleton pattern
    /// Loads existing entries from UserDefaults on creation
    private init() {
        fetchFromDefaults()
    }
    
    /// Static method to retrieve a specific entry by its keyboard shortcut
    /// - Parameter shortCut: The keyboard shortcut string to search for
    /// - Returns: The matching Entry if found, nil otherwise
    static func entry(fromShortCut shortCut: String) -> Entry? {
        // Retrieve the entire speedkeys dictionary from UserDefaults
        guard let saved = UserDefaults.standard.dictionary(forKey: "speedkeys") else {
            print("no dictionary found")
            return nil
        }
     
        // Look up the specific shortcut in the dictionary
        guard let e = saved[shortCut] as? [String: String] else {
            print("shortcut not found")
            return nil
        }
     
        // Reconstruct Entry object from the stored dictionary
        let entry = Entry(
            app: e["app"] ?? "",
            shortCut: e["shortCut"] ?? "",
            path: e["path"] ?? "",
            command: e["command"] ?? "",
            key: e["key"] ?? "",
            index: 0
        )
        return entry
    }
    
    /// Loads all entries from UserDefaults and populates the entries array
    /// Called during initialization to restore saved shortcuts
    mutating private func fetchFromDefaults() {
        guard let saved = UserDefaults.standard.dictionary(forKey: "speedkeys") else {
            print("no dictionary found")
            return
        }

        var index = 0

        // Iterate through all saved entries and reconstruct them
        for (_, value) in saved {
            let v = value as! [String: String]
            append(v)
            index = index + 1
        }
        
        print(entries)
    }
    
    /// Updates the maxKey tracker if the provided key is larger
    /// - Parameter newMaxKey: The key string to compare (format: "prefix::number")
    mutating func setMaxKey(_ newMaxKey: String) {
        // Extract the numeric part after "::"
        let _max: Int = Int(newMaxKey.split(separator: "::").last ?? "0") ?? 0
        if (_max > self.maxKey) {
            self.maxKey = _max
        }
    }

    /// Finds the array index of an entry by its key
    /// - Parameter key: The unique key to search for
    /// - Returns: The array index if found, nil otherwise
    func index(of key: String) -> Int? {
        for index in entries.indices {
            if entries[index].key == key {
                return index
            }
        }
        return nil
    }
    
    /// Adds a new entry to the collection
    /// - Parameter entry: Dictionary containing entry properties
    mutating func append(_ entry: [String: String]) {
        
        // Increment and update the max key tracker
        setMaxKey(String(maxKey + 1))
        
        // Create new Entry object from dictionary
        let entry: Entry = Entry(
            app: entry["app"] ?? "",
            shortCut: entry["shortCut"] ?? "",
            path: entry["path"] ?? "",
            command: entry["command"] ?? "",
            key: nil,
            index: maxKey + 1
        )
        entries.append(entry)
        
        // Toggle changed flag to trigger UI updates
        changed = !changed
    }
    
    /// Removes an entry from the collection by its key
    /// - Parameter key: The unique key of the entry to delete
    mutating func delete(entryWithKey key: String) {
        
        guard let index = index(of: key) else {
            return
        }
        
        if index < entries.count {
            entries.remove(at: index)
        }
    }
    
    /// Updates a specific property of an entry
    /// - Parameters:
    ///   - value: The new value to set
    ///   - type: The type of property to update (app, path, command, or shortcut)
    ///   - key: The unique key of the entry to modify
    mutating func put(value: String, asType type: EntryType, forKey key: String) {

        // Find and update the matching entry
        for i in entries.indices {
            print("searching: \(entries[i].key) == \(key)")
            if entries[i].key == key && i < entries.count {
                switch type {
                case .app:
                    entries[i].app = value
                    break
                case .path:
                    print("changing path")
                    entries[i].path = value
                    break
                case .command:
                    entries[i].command = value
                    break
                case .shortcut:
                    entries[i].shortCut = value
                    break
                }
            }
        }
    }
    
    /// Persists all entries to UserDefaults
    /// Clears existing data and writes all current entries
    /// Skips entries without a shortcut defined
    mutating func save() {
        // Clear existing speedkeys data
        UserDefaults.standard.removeObject(forKey: "speedkeys")
        var dc: [String: Any] = [:]
        
        print("pre-insert count: \(entries.count)")
        
        // Build dictionary with shortcuts as keys
        for entry in entries {
            // Only save entries that have a shortcut defined
            if entry.shortCut.isEmpty { continue }
            dc[entry.shortCut] = entry.asDictionary
        }
        
        // Save the entire dictionary to UserDefaults
        UserDefaults.standard.set(dc, forKey: "speedkeys")
        print("post-insert count: \(entries.count)")
    }
    
    /// Resets the entries array and reloads from UserDefaults
    /// Useful for discarding unsaved changes
    mutating func reset() {
        entries.removeAll()
        fetchFromDefaults()
    }
}
