import Foundation
import Combine

@MainActor
final class AppSettingsController: ObservableObject, TerminalSessionConfigurationProviding {
    static let shared = AppSettingsController(
        store: UserDefaultsAppSettingsStore(),
        observeExternalChanges: true
    )

    @Published private(set) var settings: AppSettings

    private let store: any AppSettingsStoreType
    private let notificationCenter: NotificationCenter
    private var defaultsObserver: NSObjectProtocol?

    init(
        store: any AppSettingsStoreType,
        observeExternalChanges: Bool = false,
        notificationCenter: NotificationCenter = .default
    ) {
        self.store = store
        self.notificationCenter = notificationCenter
        self.settings = store.load()

        if observeExternalChanges {
            defaultsObserver = notificationCenter.addObserver(
                forName: UserDefaults.didChangeNotification,
                object: nil,
                queue: .main
            ) { [weak self] _ in
                Task { @MainActor in
                    self?.refresh()
                }
            }
        }
    }

    deinit {
        if let defaultsObserver {
            notificationCenter.removeObserver(defaultsObserver)
        }
    }

    var terminalSessionConfiguration: TerminalSessionConfiguration {
        TerminalSessionConfiguration(
            fontSize: CGFloat(settings.terminal.fontSize),
            theme: settings.terminal.theme,
            shellPath: settings.terminal.shellPath
        )
    }

    var hotkeySettings: AppSettings.HotkeySettings {
        settings.hotkeys
    }

    var terminalSizePresets: [TerminalSizePreset] {
        settings.terminal.sizePresets
    }

    func refresh() {
        let loaded = store.load()
        guard loaded != settings else { return }
        settings = loaded
    }

    func update(_ mutate: (inout AppSettings) -> Void) {
        var updated = settings
        mutate(&updated)
        guard updated != settings else { return }
        settings = updated
        store.save(updated)
    }
}
