import Foundation

actor ChatPersistenceStore {
    static let shared = ChatPersistenceStore()

    private let encoder = JSONEncoder()
    private let decoder = JSONDecoder()
    private let rootDirectory: URL

    private init() {
        let fileManager = FileManager.default
        let baseURL = fileManager.urls(for: .applicationSupportDirectory, in: .userDomainMask).first
            ?? fileManager.urls(for: .documentDirectory, in: .userDomainMask).first
            ?? URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true)

        let directory = baseURL
            .appendingPathComponent("Rosetta", isDirectory: true)
            .appendingPathComponent("ChatState", isDirectory: true)

        try? fileManager.createDirectory(at: directory, withIntermediateDirectories: true)
        rootDirectory = directory
    }

    func load<T: Decodable>(_ type: T.Type, fileName: String, password: String? = nil) -> T? {
        let fileURL = rootDirectory.appendingPathComponent(fileName, isDirectory: false)
        guard FileManager.default.fileExists(atPath: fileURL.path) else { return nil }
        guard let data = try? Data(contentsOf: fileURL) else { return nil }
        if let password {
            guard let encryptedSnapshot = String(data: data, encoding: .utf8),
                  let decrypted = try? CryptoManager.shared.decryptWithPassword(encryptedSnapshot, password: password),
                  let decoded = try? decoder.decode(type, from: decrypted)
            else { return nil }
            return decoded
        }
        return try? decoder.decode(type, from: data)
    }

    func save<T: Encodable>(_ value: T, fileName: String, password: String? = nil) {
        let fileURL = rootDirectory.appendingPathComponent(fileName, isDirectory: false)
        guard let data = try? encoder.encode(value) else { return }
        let payload: Data
        if let password,
           let encryptedSnapshot = try? CryptoManager.shared.encryptWithPassword(data, password: password),
           let encryptedData = encryptedSnapshot.data(using: .utf8) {
            payload = encryptedData
        } else {
            payload = data
        }
        try? payload.write(
            to: fileURL,
            options: [.atomic, .completeFileProtectionUntilFirstUserAuthentication]
        )
    }

    func remove(fileName: String) {
        let fileURL = rootDirectory.appendingPathComponent(fileName, isDirectory: false)
        try? FileManager.default.removeItem(at: fileURL)
    }

    nonisolated static func accountScopedFileName(prefix: String, accountPublicKey: String) -> String {
        "\(prefix)_\(normalizedAccountKey(accountPublicKey)).json"
    }

    private nonisolated static func normalizedAccountKey(_ accountPublicKey: String) -> String {
        let trimmed = accountPublicKey.trimmingCharacters(in: .whitespacesAndNewlines)
        if trimmed.isEmpty {
            return "anonymous"
        }
        let scalars = trimmed.unicodeScalars.map { scalar -> Character in
            if CharacterSet.alphanumerics.contains(scalar) {
                return Character(scalar)
            }
            return "_"
        }
        return String(scalars)
    }
}
