import Foundation
import Security

actor KeychainService {
    static let shared = KeychainService()

    private let service = "com.bookstax.credentials"
    private let tokenIdKey = "tokenId"
    private let tokenSecretKey = "tokenSecret"

    // MARK: - Public API

    func saveCredentials(tokenId: String, tokenSecret: String) throws {
        try save(value: tokenId, key: tokenIdKey)
        try save(value: tokenSecret, key: tokenSecretKey)
    }

    func loadCredentials() throws -> (tokenId: String, tokenSecret: String)? {
        guard let id = try load(key: tokenIdKey),
              let secret = try load(key: tokenSecretKey) else { return nil }
        return (id, secret)
    }

    func deleteCredentials() throws {
        try delete(key: tokenIdKey)
        try delete(key: tokenSecretKey)
    }

    // MARK: - Per-profile credential methods

    func saveCredentials(tokenId: String, tokenSecret: String, profileId: UUID) throws {
        try save(value: tokenId, key: "tokenId-\(profileId.uuidString)")
        try save(value: tokenSecret, key: "tokenSecret-\(profileId.uuidString)")
    }

    func loadCredentials(profileId: UUID) throws -> (tokenId: String, tokenSecret: String)? {
        guard let id = try load(key: "tokenId-\(profileId.uuidString)"),
              let secret = try load(key: "tokenSecret-\(profileId.uuidString)") else { return nil }
        return (id, secret)
    }

    func deleteCredentials(profileId: UUID) throws {
        try delete(key: "tokenId-\(profileId.uuidString)")
        try delete(key: "tokenSecret-\(profileId.uuidString)")
    }

    // MARK: - Synchronous static helpers (for use at app init / non-async contexts)

    static func loadCredentialsSync(profileId: UUID) -> (tokenId: String, tokenSecret: String)? {
        guard let tokenId = loadSync(key: "tokenId-\(profileId.uuidString)"),
              let tokenSecret = loadSync(key: "tokenSecret-\(profileId.uuidString)") else { return nil }
        return (tokenId, tokenSecret)
    }

    @discardableResult
    static func saveSync(value: String, key: String) -> Bool {
        let service = "com.bookstax.credentials"
        guard let data = value.data(using: .utf8) else { return false }
        let deleteQuery: [CFString: Any] = [
            kSecClass: kSecClassGenericPassword,
            kSecAttrService: service,
            kSecAttrAccount: key
        ]
        SecItemDelete(deleteQuery as CFDictionary)
        let addQuery: [CFString: Any] = [
            kSecClass: kSecClassGenericPassword,
            kSecAttrService: service,
            kSecAttrAccount: key,
            kSecAttrAccessible: kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
            kSecValueData: data
        ]
        return SecItemAdd(addQuery as CFDictionary, nil) == errSecSuccess
    }

    static func saveCredentialsSync(tokenId: String, tokenSecret: String, profileId: UUID) {
        saveSync(value: tokenId, key: "tokenId-\(profileId.uuidString)")
        saveSync(value: tokenSecret, key: "tokenSecret-\(profileId.uuidString)")
    }

    static func deleteCredentialsSync(profileId: UUID) {
        let service = "com.bookstax.credentials"
        for suffix in ["tokenId-\(profileId.uuidString)", "tokenSecret-\(profileId.uuidString)"] {
            let query: [CFString: Any] = [
                kSecClass: kSecClassGenericPassword,
                kSecAttrService: service,
                kSecAttrAccount: suffix
            ]
            SecItemDelete(query as CFDictionary)
        }
    }

    static func loadSync(key: String) -> String? {
        let service = "com.bookstax.credentials"
        let query: [CFString: Any] = [
            kSecClass: kSecClassGenericPassword,
            kSecAttrService: service,
            kSecAttrAccount: key,
            kSecReturnData: true,
            kSecMatchLimit: kSecMatchLimitOne
        ]
        var result: CFTypeRef?
        let status = SecItemCopyMatching(query as CFDictionary, &result)
        guard status == errSecSuccess,
              let data = result as? Data,
              let string = String(data: data, encoding: .utf8) else { return nil }
        return string
    }

    // MARK: - Private helpers

    private func save(value: String, key: String) throws {
        guard let data = value.data(using: .utf8) else { return }
        // Delete existing item first to allow update
        let deleteQuery: [CFString: Any] = [
            kSecClass: kSecClassGenericPassword,
            kSecAttrService: service,
            kSecAttrAccount: key
        ]
        SecItemDelete(deleteQuery as CFDictionary)

        let addQuery: [CFString: Any] = [
            kSecClass: kSecClassGenericPassword,
            kSecAttrService: service,
            kSecAttrAccount: key,
            kSecAttrAccessible: kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
            kSecValueData: data
        ]
        let status = SecItemAdd(addQuery as CFDictionary, nil)
        guard status == errSecSuccess else {
            throw BookStackError.keychainError(status)
        }
    }

    private func load(key: String) throws -> String? {
        let query: [CFString: Any] = [
            kSecClass: kSecClassGenericPassword,
            kSecAttrService: service,
            kSecAttrAccount: key,
            kSecReturnData: true,
            kSecMatchLimit: kSecMatchLimitOne
        ]
        var result: CFTypeRef?
        let status = SecItemCopyMatching(query as CFDictionary, &result)
        if status == errSecItemNotFound { return nil }
        guard status == errSecSuccess,
              let data = result as? Data,
              let string = String(data: data, encoding: .utf8) else {
            throw BookStackError.keychainError(status)
        }
        return string
    }

    private func delete(key: String) throws {
        let query: [CFString: Any] = [
            kSecClass: kSecClassGenericPassword,
            kSecAttrService: service,
            kSecAttrAccount: key
        ]
        let status = SecItemDelete(query as CFDictionary)
        guard status == errSecSuccess || status == errSecItemNotFound else {
            throw BookStackError.keychainError(status)
        }
    }
}
