import Foundation

public struct SaplingConfiguration: Hashable, Codable, Sendable {
    public var recentWorkspaceURLs: [URL]
    public var defaultBranchName: String
    public var autosavesDrafts: Bool
    public var showsHiddenFiles: Bool
    public var preferredEditorFontSize: Double

    public init(
        recentWorkspaceURLs: [URL] = [],
        defaultBranchName: String = "main",
        autosavesDrafts: Bool = true,
        showsHiddenFiles: Bool = false,
        preferredEditorFontSize: Double = 15
    ) {
        self.recentWorkspaceURLs = recentWorkspaceURLs
        self.defaultBranchName = defaultBranchName
        self.autosavesDrafts = autosavesDrafts
        self.showsHiddenFiles = showsHiddenFiles
        self.preferredEditorFontSize = preferredEditorFontSize
    }
}

public protocol ConfigurationStore: Sendable {
    func loadConfiguration() throws -> SaplingConfiguration
    func saveConfiguration(_ configuration: SaplingConfiguration) throws
}

public final class InMemoryConfigurationStore: ConfigurationStore, @unchecked Sendable {
    private var configuration: SaplingConfiguration

    public init(configuration: SaplingConfiguration = SaplingConfiguration()) {
        self.configuration = configuration
    }

    public func loadConfiguration() throws -> SaplingConfiguration {
        configuration
    }

    public func saveConfiguration(_ configuration: SaplingConfiguration) throws {
        self.configuration = configuration
    }
}

public final class JSONConfigurationStore: ConfigurationStore, @unchecked Sendable {
    private let fileURL: URL
    private let encoder: JSONEncoder
    private let decoder: JSONDecoder

    public init(fileURL: URL) {
        self.fileURL = fileURL
        self.encoder = JSONEncoder()
        self.decoder = JSONDecoder()
        encoder.outputFormatting = [.prettyPrinted, .sortedKeys]
    }

    public func loadConfiguration() throws -> SaplingConfiguration {
        guard FileManager.default.fileExists(atPath: fileURL.path) else {
            return SaplingConfiguration()
        }

        let data = try Data(contentsOf: fileURL)
        return try decoder.decode(SaplingConfiguration.self, from: data)
    }

    public func saveConfiguration(_ configuration: SaplingConfiguration) throws {
        try FileManager.default.createDirectory(
            at: fileURL.deletingLastPathComponent(),
            withIntermediateDirectories: true
        )
        let data = try encoder.encode(configuration)
        try data.write(to: fileURL, options: [.atomic])
    }
}
