import Foundation

/// Shared normalization and merge policy for user search flows.
///
/// Canonical semantics:
/// - server query: `username contains` + `publicKey exact`
/// - local augmentation: only Saved Messages aliases + exact known key fallback
enum SearchParityPolicy {

    private static let savedAliases: Set<String> = [
        "saved",
        "saved messages",
        "savedmessages",
        "notes",
        "self",
        "me",
    ]

    static func sanitizeInput(_ query: String) -> String {
        query.replacingOccurrences(of: "@", with: "")
            .trimmingCharacters(in: .whitespacesAndNewlines)
    }

    static func normalizedQuery(_ query: String) -> String {
        sanitizeInput(query).lowercased()
    }

    static func normalizedPublicKey(_ key: String) -> String {
        let trimmed = key.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
        if trimmed.hasPrefix("0x") {
            return String(trimmed.dropFirst(2))
        }
        return trimmed
    }

    static func isExactPublicKeyQuery(_ query: String) -> Bool {
        let normalized = normalizedPublicKey(query)
        guard normalized.count == 66 else { return false }
        return normalized.allSatisfy(\.isHexDigit)
    }

    static func isSavedMessagesAlias(_ query: String) -> Bool {
        savedAliases.contains(normalizedQuery(query))
    }

    static func localAugmentedUsers(
        query: String,
        currentPublicKey: String,
        dialogs: [Dialog]
    ) -> [SearchUser] {
        let normalized = normalizedQuery(query)
        guard !normalized.isEmpty else { return [] }

        var results: [SearchUser] = []

        if isSavedMessagesAlias(normalized) || matchesExactPublicKey(query: normalized, publicKey: currentPublicKey) {
            results.append(SearchUser(
                username: "",
                title: "Saved Messages",
                publicKey: currentPublicKey,
                verified: 0,
                online: 0
            ))
        }

        guard isExactPublicKeyQuery(normalized) else {
            return dedupByPublicKey(results)
        }

        for dialog in dialogs where matchesExactPublicKey(query: normalized, publicKey: dialog.opponentKey) {
            guard dialog.opponentKey != currentPublicKey else { continue }
            results.append(SearchUser(
                username: dialog.opponentUsername,
                title: dialog.opponentTitle,
                publicKey: dialog.opponentKey,
                verified: dialog.verified,
                online: dialog.isOnline ? 0 : 1
            ))
        }

        return dedupByPublicKey(results)
    }

    static func mergeServerAndLocal(server: [SearchUser], local: [SearchUser]) -> [SearchUser] {
        var merged: [SearchUser] = []
        var seen = Set<String>()

        for user in server {
            let key = normalizedPublicKey(user.publicKey)
            guard !seen.contains(key) else { continue }
            seen.insert(key)
            merged.append(user)
        }

        for user in local {
            let key = normalizedPublicKey(user.publicKey)
            guard !seen.contains(key) else { continue }
            seen.insert(key)
            merged.append(user)
        }

        return merged
    }

    private static func matchesExactPublicKey(query: String, publicKey: String) -> Bool {
        normalizedPublicKey(query) == normalizedPublicKey(publicKey)
    }

    private static func dedupByPublicKey(_ users: [SearchUser]) -> [SearchUser] {
        var seen = Set<String>()
        var deduped: [SearchUser] = []
        for user in users {
            let key = normalizedPublicKey(user.publicKey)
            guard !seen.contains(key) else { continue }
            seen.insert(key)
            deduped.append(user)
        }
        return deduped
    }
}
