import Foundation
import Observation
import OSLog

/// Manager for VibeTunnel configuration stored in ~/.vibetunnel/config.json
/// Provides centralized configuration management for all app settings
@MainActor
@Observable
final class ConfigManager {
    static let shared = ConfigManager()

    private let logger = Logger(subsystem: BundleIdentifiers.loggerSubsystem, category: "ConfigManager")
    private let configDir: URL
    private let configPath: URL
    private var fileMonitor: DispatchSourceFileSystemObject?

    // Core configuration
    private(set) var quickStartCommands: [QuickStartCommand] = []
    var repositoryBasePath: String = FilePathConstants.defaultRepositoryBasePath

    // Server settings
    var serverPort: Int = 4_020
    var dashboardAccessMode: DashboardAccessMode = .network
    var cleanupOnStartup: Bool = true
    var authenticationMode: AuthenticationMode = .osAuth

    // Development settings
    var debugMode: Bool = false
    var useDevServer: Bool = false
    var devServerPath: String = ""
    var logLevel: String = "info"

    // Application preferences
    var preferredGitApp: String?
    var preferredTerminal: String?
    var updateChannel: UpdateChannel = .stable
    var showInDock: Bool = false
    var preventSleepWhenRunning: Bool = true

    // Notification preferences
    var notificationsEnabled: Bool = true
    var notificationSessionStart: Bool = true
    var notificationSessionExit: Bool = true
    var notificationCommandCompletion: Bool = true
    var notificationCommandError: Bool = true
    var notificationBell: Bool = true
    var notificationClaudeTurn: Bool = false
    var notificationSoundEnabled: Bool = true
    var notificationVibrationEnabled: Bool = true
    var showInNotificationCenter: Bool = true

    // Remote access
    var ngrokEnabled: Bool = false
    var ngrokTokenPresent: Bool = false

    // Session defaults
    var sessionCommand: String = "zsh"
    var sessionWorkingDirectory: String = FilePathConstants.defaultRepositoryBasePath
    var sessionSpawnWindow: Bool = true
    var sessionTitleMode: TitleMode = .dynamic

    /// Comprehensive configuration structure
    private struct VibeTunnelConfig: Codable {
        let version: Int
        var quickStartCommands: [QuickStartCommand]
        var repositoryBasePath: String?

        // Extended configuration sections
        var server: ServerConfig?
        var development: DevelopmentConfig?
        var preferences: PreferencesConfig?
        var remoteAccess: RemoteAccessConfig?
        var sessionDefaults: SessionDefaultsConfig?
    }

    // MARK: - Configuration Sub-structures

    private struct ServerConfig: Codable {
        var port: Int
        var dashboardAccessMode: String
        var cleanupOnStartup: Bool
        var authenticationMode: String
    }

    private struct DevelopmentConfig: Codable {
        var debugMode: Bool
        var useDevServer: Bool
        var devServerPath: String
        var logLevel: String
    }

    private struct PreferencesConfig: Codable {
        var preferredGitApp: String?
        var preferredTerminal: String?
        var updateChannel: String
        var showInDock: Bool
        var preventSleepWhenRunning: Bool
        var notifications: NotificationConfig?
    }

    private struct NotificationConfig: Codable {
        var enabled: Bool
        var sessionStart: Bool
        var sessionExit: Bool
        var commandCompletion: Bool
        var commandError: Bool
        var bell: Bool
        var claudeTurn: Bool
        var soundEnabled: Bool
        var vibrationEnabled: Bool
        var showInNotificationCenter: Bool?
    }

    private struct RemoteAccessConfig: Codable {
        var ngrokEnabled: Bool
        var ngrokTokenPresent: Bool
    }

    private struct SessionDefaultsConfig: Codable {
        var command: String
        var workingDirectory: String
        var spawnWindow: Bool
        var titleMode: String
    }

    /// Default commands matching web/src/types/config.ts
    private let defaultCommands = [
        QuickStartCommand(name: "✨ claude", command: "claude --dangerously-skip-permissions"),
        QuickStartCommand(name: "✨ gemini", command: "gemini"),
        QuickStartCommand(name: nil, command: "opencode"),
        QuickStartCommand(name: nil, command: "crush"),
        QuickStartCommand(name: nil, command: "zsh"),
        QuickStartCommand(name: nil, command: "node")
    ]

    private init() {
        let homeDir = FileManager.default.homeDirectoryForCurrentUser
        self.configDir = homeDir.appendingPathComponent(".vibetunnel")
        self.configPath = configDir.appendingPathComponent("config.json")

        // Load initial configuration
        loadConfiguration()

        // Start monitoring for changes
        startFileMonitoring()
    }

    // MARK: - Configuration Loading

    private func loadConfiguration() {
        // Ensure directory exists
        try? FileManager.default.createDirectory(at: configDir, withIntermediateDirectories: true)

        if FileManager.default.fileExists(atPath: configPath.path) {
            do {
                let data = try Data(contentsOf: configPath)
                let config = try JSONDecoder().decode(VibeTunnelConfig.self, from: data)

                // Load all configuration values
                self.quickStartCommands = config.quickStartCommands
                self.repositoryBasePath = config.repositoryBasePath ?? FilePathConstants.defaultRepositoryBasePath

                // Server settings
                if let server = config.server {
                    self.serverPort = server.port
                    self.dashboardAccessMode = DashboardAccessMode(rawValue: server.dashboardAccessMode) ?? .network
                    self.cleanupOnStartup = server.cleanupOnStartup
                    self.authenticationMode = AuthenticationMode(rawValue: server.authenticationMode) ?? .osAuth
                }

                // Development settings
                if let dev = config.development {
                    self.debugMode = dev.debugMode
                    self.useDevServer = dev.useDevServer
                    self.devServerPath = dev.devServerPath
                    self.logLevel = dev.logLevel
                }

                // Preferences
                if let prefs = config.preferences {
                    self.preferredGitApp = prefs.preferredGitApp
                    self.preferredTerminal = prefs.preferredTerminal
                    self.updateChannel = UpdateChannel(rawValue: prefs.updateChannel) ?? .stable
                    self.showInDock = prefs.showInDock
                    self.preventSleepWhenRunning = prefs.preventSleepWhenRunning

                    // Notification preferences
                    if let notif = prefs.notifications {
                        self.notificationsEnabled = notif.enabled
                        self.notificationSessionStart = notif.sessionStart
                        self.notificationSessionExit = notif.sessionExit
                        self.notificationCommandCompletion = notif.commandCompletion
                        self.notificationCommandError = notif.commandError
                        self.notificationBell = notif.bell
                        self.notificationClaudeTurn = notif.claudeTurn
                        self.notificationSoundEnabled = notif.soundEnabled
                        self.notificationVibrationEnabled = notif.vibrationEnabled
                        if let showInCenter = notif.showInNotificationCenter {
                            self.showInNotificationCenter = showInCenter
                        }
                    }
                }

                // Remote access
                if let remote = config.remoteAccess {
                    self.ngrokEnabled = remote.ngrokEnabled
                    self.ngrokTokenPresent = remote.ngrokTokenPresent
                }

                // Session defaults
                if let session = config.sessionDefaults {
                    self.sessionCommand = session.command
                    self.sessionWorkingDirectory = session.workingDirectory
                    self.sessionSpawnWindow = session.spawnWindow
                    self.sessionTitleMode = TitleMode(rawValue: session.titleMode) ?? .dynamic
                }

                logger.info("Loaded configuration from disk")
            } catch {
                logger.error("Failed to load config: \(error.localizedDescription)")
                useDefaults()
            }
        } else {
            logger.info("No config file found, creating with defaults")
            useDefaults()
        }
    }

    private func useDefaults() {
        self.quickStartCommands = defaultCommands
        self.repositoryBasePath = FilePathConstants.defaultRepositoryBasePath

        // Set notification defaults to match TypeScript defaults
        // Master switch is OFF by default, but individual preferences are set to true
        self.notificationsEnabled = false // Changed from true to match web defaults
        self.notificationSessionStart = true
        self.notificationSessionExit = true
        self.notificationCommandCompletion = true
        self.notificationCommandError = true
        self.notificationBell = true
        self.notificationClaudeTurn = false
        self.notificationSoundEnabled = true
        self.notificationVibrationEnabled = true
        self.showInNotificationCenter = true

        saveConfiguration()
    }

    // MARK: - Configuration Saving

    private func saveConfiguration() {
        var config = VibeTunnelConfig(
            version: 2,
            quickStartCommands: quickStartCommands,
            repositoryBasePath: repositoryBasePath
        )

        // Server configuration
        config.server = ServerConfig(
            port: serverPort,
            dashboardAccessMode: dashboardAccessMode.rawValue,
            cleanupOnStartup: cleanupOnStartup,
            authenticationMode: authenticationMode.rawValue
        )

        // Development configuration
        config.development = DevelopmentConfig(
            debugMode: debugMode,
            useDevServer: useDevServer,
            devServerPath: devServerPath,
            logLevel: logLevel
        )

        // Preferences
        config.preferences = PreferencesConfig(
            preferredGitApp: preferredGitApp,
            preferredTerminal: preferredTerminal,
            updateChannel: updateChannel.rawValue,
            showInDock: showInDock,
            preventSleepWhenRunning: preventSleepWhenRunning,
            notifications: NotificationConfig(
                enabled: notificationsEnabled,
                sessionStart: notificationSessionStart,
                sessionExit: notificationSessionExit,
                commandCompletion: notificationCommandCompletion,
                commandError: notificationCommandError,
                bell: notificationBell,
                claudeTurn: notificationClaudeTurn,
                soundEnabled: notificationSoundEnabled,
                vibrationEnabled: notificationVibrationEnabled,
                showInNotificationCenter: showInNotificationCenter
            )
        )

        // Remote access
        config.remoteAccess = RemoteAccessConfig(
            ngrokEnabled: ngrokEnabled,
            ngrokTokenPresent: ngrokTokenPresent
        )

        // Session defaults
        config.sessionDefaults = SessionDefaultsConfig(
            command: sessionCommand,
            workingDirectory: sessionWorkingDirectory,
            spawnWindow: sessionSpawnWindow,
            titleMode: sessionTitleMode.rawValue
        )

        do {
            let encoder = JSONEncoder()
            encoder.outputFormatting = [.prettyPrinted, .sortedKeys]
            let data = try encoder.encode(config)

            // Ensure directory exists
            try FileManager.default.createDirectory(at: configDir, withIntermediateDirectories: true)

            // Write atomically to prevent corruption
            try data.write(to: configPath, options: .atomic)
            logger.info("Saved configuration to disk")
        } catch {
            logger.error("Failed to save config: \(error.localizedDescription)")
        }
    }

    // MARK: - File Monitoring

    private func startFileMonitoring() {
        // Stop any existing monitor
        stopFileMonitoring()

        // Create file descriptor
        let fileDescriptor = open(configPath.path, O_EVTONLY)
        guard fileDescriptor != -1 else {
            logger.warning("Could not open config file for monitoring")
            return
        }

        // Create dispatch source on main queue since ConfigManager is @MainActor
        let source = DispatchSource.makeFileSystemObjectSource(
            fileDescriptor: fileDescriptor,
            eventMask: [.write, .delete, .rename],
            queue: .main
        )

        source.setEventHandler { [weak self] in
            guard let self else { return }

            // Debounce rapid changes
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [weak self] in
                guard let self else { return }

                self.logger.info("Configuration file changed, reloading...")
                let oldCommands = self.quickStartCommands
                self.loadConfiguration()

                // Only log if commands actually changed
                if oldCommands != self.quickStartCommands {
                    self.logger.info("Quick start commands updated")
                }
            }
        }

        source.setCancelHandler {
            close(fileDescriptor)
        }

        source.resume()
        self.fileMonitor = source

        logger.info("Started monitoring configuration file")
    }

    private func stopFileMonitoring() {
        fileMonitor?.cancel()
        fileMonitor = nil
    }

    // MARK: - Public API

    /// Update quick start commands
    func updateQuickStartCommands(_ commands: [QuickStartCommand]) {
        guard commands != quickStartCommands else { return }

        self.quickStartCommands = commands
        saveConfiguration()
        logger.info("Updated quick start commands: \(commands.count) items")
    }

    /// Reset to default commands
    func resetToDefaults() {
        updateQuickStartCommands(defaultCommands)
        logger.info("Reset quick start commands to defaults")
    }

    /// Add a new command
    func addCommand(name: String?, command: String) {
        var commands = quickStartCommands
        commands.append(QuickStartCommand(name: name, command: command))
        updateQuickStartCommands(commands)
    }

    /// Update an existing command
    func updateCommand(id: String, name: String?, command: String) {
        var commands = quickStartCommands
        if let index = commands.firstIndex(where: { $0.id == id }) {
            commands[index].name = name
            commands[index].command = command
            updateQuickStartCommands(commands)
        }
    }

    /// Delete a command
    func deleteCommand(id: String) {
        var commands = quickStartCommands
        commands.removeAll { $0.id == id }
        updateQuickStartCommands(commands)
    }

    /// Delete all commands (clear the list)
    func deleteAllCommands() {
        updateQuickStartCommands([])
        logger.info("Deleted all quick start commands")
    }

    /// Move commands for drag and drop reordering
    func moveCommands(from source: IndexSet, to destination: Int) {
        var commands = quickStartCommands
        commands.move(fromOffsets: source, toOffset: destination)
        updateQuickStartCommands(commands)
        logger.info("Reordered quick start commands")
    }

    /// Update repository base path
    func updateRepositoryBasePath(_ path: String) {
        guard path != repositoryBasePath else { return }

        self.repositoryBasePath = path
        saveConfiguration()
        logger.info("Updated repository base path to: \(path)")
    }

    /// Update notification preferences
    func updateNotificationPreferences(
        enabled: Bool? = nil,
        sessionStart: Bool? = nil,
        sessionExit: Bool? = nil,
        commandCompletion: Bool? = nil,
        commandError: Bool? = nil,
        bell: Bool? = nil,
        claudeTurn: Bool? = nil,
        soundEnabled: Bool? = nil,
        vibrationEnabled: Bool? = nil
    ) {
        // Update only the provided values
        if let enabled { self.notificationsEnabled = enabled }
        if let sessionStart { self.notificationSessionStart = sessionStart }
        if let sessionExit { self.notificationSessionExit = sessionExit }
        if let commandCompletion { self.notificationCommandCompletion = commandCompletion }
        if let commandError { self.notificationCommandError = commandError }
        if let bell { self.notificationBell = bell }
        if let claudeTurn { self.notificationClaudeTurn = claudeTurn }
        if let soundEnabled { self.notificationSoundEnabled = soundEnabled }
        if let vibrationEnabled { self.notificationVibrationEnabled = vibrationEnabled }

        saveConfiguration()
        logger.info("Updated notification preferences")
    }

    /// Get the configuration file path for debugging
    var configurationPath: String {
        configPath.path
    }

    deinit {
        // File monitoring will be cleaned up automatically
    }
}
