import Foundation
import Security

final class SecureStore: ObservableObject {
    static let shared = SecureStore()

    @Published private(set) var apiToken: String?
    @Published private(set) var profile: ProfileResponse?
    @Published private(set) var companyName: String?
    @Published private(set) var companyNumber: String?
    @Published private(set) var brandAppName: String?
    @Published private(set) var brandLogoUrl: String?

    private let service = "com.notifyhub.viewer"

    var displayAppName: String {
        if let brandAppName, !brandAppName.isEmpty { return brandAppName }
        return Bundle.main.object(forInfoDictionaryKey: "CFBundleDisplayName") as? String
            ?? Bundle.main.object(forInfoDictionaryKey: "CFBundleName") as? String
            ?? "Notiz Viewer"
    }

    var isLoggedIn: Bool {
        guard let apiToken else { return false }
        return !apiToken.isEmpty
    }

    func hasPage(_ page: String) -> Bool {
        profile?.permissions.pages.contains(page) ?? false
    }

    func hasAction(_ action: String) -> Bool {
        profile?.permissions.actions.contains(action) ?? false
    }

    private init() {
        apiToken = read(key: "api_token")
        brandAppName = read(key: "brand_app_name")
        brandLogoUrl = read(key: "brand_logo_url")
        companyName = read(key: "company_name")
        companyNumber = read(key: "company_number")
        loadProfile()
    }

    func storeSession(token: String, profile: ProfileResponse) {
        self.apiToken = token
        self.profile = profile
        write(key: "api_token", value: token)
        applyProfile(profile)
    }

    func applyProfile(_ profile: ProfileResponse) {
        self.profile = profile
        if let data = try? JSONEncoder().encode(profile),
           let json = String(data: data, encoding: .utf8) {
            write(key: "profile", value: json)
        }
        applyBranding(profile.branding)
        applyCompany(profile.company)
    }

    func applyBranding(_ branding: BrandingPayload?) {
        brandAppName = branding?.app_name
        brandLogoUrl = branding?.logo_url
        write(key: "brand_app_name", value: branding?.app_name)
        write(key: "brand_logo_url", value: branding?.logo_url)
    }

    func applyCompany(_ company: CompanySummary?) {
        companyName = company?.name
        companyNumber = company?.company_number
        write(key: "company_name", value: company?.name)
        write(key: "company_number", value: company?.company_number)
    }

    func clearSession() {
        apiToken = nil
        profile = nil
        brandAppName = nil
        brandLogoUrl = nil
        companyName = nil
        companyNumber = nil
        delete(key: "api_token")
        delete(key: "profile")
        delete(key: "brand_app_name")
        delete(key: "brand_logo_url")
        delete(key: "company_name")
        delete(key: "company_number")
    }

    private func loadProfile() {
        guard let json = read(key: "profile") else { return }
        if let data = json.data(using: .utf8),
           let profile = try? JSONDecoder().decode(ProfileResponse.self, from: data) {
            self.profile = profile
        }
    }

    // MARK: - Keychain

    private func write(key: String, value: String?) {
        delete(key: key)
        guard let value, let data = value.data(using: .utf8) else { return }
        let query: [String: Any] = [
            kSecClass as String: kSecClassGenericPassword,
            kSecAttrService as String: service,
            kSecAttrAccount as String: key,
            kSecValueData as String: data,
        ]
        SecItemAdd(query as CFDictionary, nil)
    }

    private func read(key: String) -> String? {
        let query: [String: Any] = [
            kSecClass as String: kSecClassGenericPassword,
            kSecAttrService as String: service,
            kSecAttrAccount as String: key,
            kSecReturnData as String: true,
            kSecMatchLimit as String: kSecMatchLimitOne,
        ]
        var item: CFTypeRef?
        guard SecItemCopyMatching(query as CFDictionary, &item) == errSecSuccess,
              let data = item as? Data,
              let value = String(data: data, encoding: .utf8)
        else { return nil }
        return value
    }

    private func delete(key: String) {
        let query: [String: Any] = [
            kSecClass as String: kSecClassGenericPassword,
            kSecAttrService as String: service,
            kSecAttrAccount as String: key,
        ]
        SecItemDelete(query as CFDictionary)
    }
}
