import Foundation

enum ContactFieldInputRules {
    private static let phoneFieldIDs: Set<String> = ["phone", "whatsapp", "signal"]
    private static let strictWebLinkFieldIDs: Set<String> = ["website", "customLink", "calendly"]
    private static let usernameOrLinkFieldIDs: Set<String> = [
        "linkedIn", "twitter", "instagram", "facebook", "tiktok", "threads",
        "youtube", "snapchat", "pinterest", "twitch", "bluesky", "mastodon", "reddit",
        "github", "gitlab", "stackoverflow",
        "telegram", "discord", "slack", "matrix",
        "patreon", "kofi"
    ]

    static func shouldFormatAsPhone(_ fieldTypeID: String) -> Bool {
        phoneFieldIDs.contains(fieldTypeID)
    }
    
    nonisolated static func linkedInProfileURL(for value: String) -> URL? {
        guard let normalized = normalizedLinkedInProfileURLString(value) else { return nil }
        return URL(string: normalized)
    }

    static func isValid(_ value: String, for fieldTypeID: String) -> Bool {
        let trimmed = value.trimmingCharacters(in: .whitespacesAndNewlines)
        guard !trimmed.isEmpty else { return false }

        if phoneFieldIDs.contains(fieldTypeID) {
            return PhoneNumberText.isValid(trimmed)
        }

        switch fieldTypeID {
        case "linkedIn":
            return linkedInProfileURL(for: trimmed) != nil
        case "email":
            return EmailText.isValid(trimmed)
        case "zelle":
            return PhoneNumberText.isValid(trimmed) || EmailText.isValid(trimmed)
        case "venmo":
            return isValidVenmo(trimmed)
        case "cashApp":
            return isValidCashApp(trimmed)
        case "paypal":
            return isValidPayPal(trimmed)
        default:
            break
        }

        if strictWebLinkFieldIDs.contains(fieldTypeID) {
            return WebLinkText.isValid(trimmed)
        }

        if usernameOrLinkFieldIDs.contains(fieldTypeID) {
            return isValidUsernameOrLink(trimmed)
        }

        return !trimmed.isEmpty
    }

    static func normalizedForStorage(_ value: String, for fieldTypeID: String) -> String {
        let trimmed = value.trimmingCharacters(in: .whitespacesAndNewlines)
        guard !trimmed.isEmpty else { return "" }

        if phoneFieldIDs.contains(fieldTypeID) {
            return PhoneNumberText.normalizedForStorage(trimmed)
        }

        switch fieldTypeID {
        case "linkedIn":
            return linkedInProfileURL(for: trimmed)?.absoluteString ?? trimmed
        case "email":
            return EmailText.normalizedForStorage(trimmed)
        case "zelle":
            if EmailText.isValid(trimmed) {
                return EmailText.normalizedForStorage(trimmed)
            }
            if PhoneNumberText.isValid(trimmed) {
                return PhoneNumberText.normalizedForStorage(trimmed)
            }
            return trimmed
        case "paypal":
            if EmailText.isValid(trimmed) {
                return EmailText.normalizedForStorage(trimmed)
            }
            if WebLinkText.isValid(trimmed) {
                return WebLinkText.normalizedForStorage(trimmed)
            }
            return trimmed
        default:
            break
        }

        if strictWebLinkFieldIDs.contains(fieldTypeID) || usernameOrLinkFieldIDs.contains(fieldTypeID) {
            if WebLinkText.isValid(trimmed) {
                return WebLinkText.normalizedForStorage(trimmed)
            }
        }

        return trimmed
    }

    static func validationMessage(for fieldTypeID: String) -> String {
        if phoneFieldIDs.contains(fieldTypeID) {
            return String(localized: "Enter a valid phone number")
        }

        switch fieldTypeID {
        case "linkedIn":
            return String(localized: "Enter a valid LinkedIn profile username or link")
        case "email":
            return String(localized: "Enter a valid email address")
        case "zelle":
            return String(localized: "Enter a valid phone number or email")
        case "venmo":
            return String(localized: "Enter a valid Venmo username")
        case "cashApp":
            return String(localized: "Enter a valid Cash App cashtag")
        case "paypal":
            return String(localized: "Enter a valid PayPal username, email, or link")
        default:
            break
        }

        if strictWebLinkFieldIDs.contains(fieldTypeID) {
            return String(localized: "Enter a valid web link")
        }

        if usernameOrLinkFieldIDs.contains(fieldTypeID) {
            return String(localized: "Enter a valid username or link")
        }

        return String(localized: "Enter a value")
    }
}

// MARK: - Private Validation Helpers

private extension ContactFieldInputRules {
    nonisolated static func normalizedLinkedInProfileURLString(_ value: String) -> String? {
        let trimmed = value.trimmingCharacters(in: .whitespacesAndNewlines)
        guard !trimmed.isEmpty, !trimmed.contains(where: \.isWhitespace) else { return nil }
        
        if let url = URL(string: trimmed),
           let host = url.host?.lowercased(),
           host.contains("linkedin.com") {
            let pathComponents = url.path
                .split(separator: "/")
                .map(String.init)
            guard pathComponents.count >= 2,
                  pathComponents[0].lowercased() == "in",
                  let handle = normalizedLinkedInHandle(pathComponents[1]) else {
                return nil
            }
            return "https://www.linkedin.com/in/\(handle)/"
        }
        
        var candidate = trimmed
            .replacingOccurrences(of: "https://", with: "")
            .replacingOccurrences(of: "http://", with: "")
            .replacingOccurrences(of: "www.", with: "")
        
        if candidate.lowercased().hasPrefix("linkedin.com/") {
            candidate = String(candidate.dropFirst("linkedin.com/".count))
            guard candidate.lowercased().hasPrefix("in/") else { return nil }
            candidate = String(candidate.dropFirst("in/".count))
        } else if candidate.lowercased().hasPrefix("in/") {
            candidate = String(candidate.dropFirst("in/".count))
        }
        
        let handleCandidate = candidate
            .split(separator: "/")
            .first
            .map(String.init) ?? candidate
        
        guard let handle = normalizedLinkedInHandle(handleCandidate) else { return nil }
        return "https://www.linkedin.com/in/\(handle)/"
    }
    
    nonisolated static func normalizedLinkedInHandle(_ value: String) -> String? {
        let handle = value.hasPrefix("@") ? String(value.dropFirst()) : value
        guard !handle.isEmpty else { return nil }
        
        let allowed = CharacterSet.alphanumerics.union(CharacterSet(charactersIn: "-_"))
        guard handle.unicodeScalars.allSatisfy({ allowed.contains($0) }) else { return nil }
        return handle
    }
    
    static func isValidUsernameOrLink(_ value: String) -> Bool {
        guard !value.contains(where: \.isWhitespace) else { return false }
        if WebLinkText.isValid(value) { return true }

        let allowed = CharacterSet.alphanumerics.union(CharacterSet(charactersIn: "@._-/:+$#"))
        let hasValidScalars = value.unicodeScalars.allSatisfy { allowed.contains($0) }
        let hasContent = value.contains(where: { $0.isLetter || $0.isNumber })
        return hasValidScalars && hasContent && value.count >= 2
    }

    static func isValidVenmo(_ value: String) -> Bool {
        guard !value.contains(where: \.isWhitespace) else { return false }
        let username = value.hasPrefix("@") ? String(value.dropFirst()) : value
        return isSimpleHandle(username, min: 2, max: 30, allowDash: true)
    }

    static func isValidCashApp(_ value: String) -> Bool {
        guard !value.contains(where: \.isWhitespace) else { return false }
        let cashtag = value.hasPrefix("$") ? String(value.dropFirst()) : value
        return isSimpleHandle(cashtag, min: 1, max: 20, allowDash: false)
    }

    static func isValidPayPal(_ value: String) -> Bool {
        guard !value.contains(where: \.isWhitespace) else { return false }
        if EmailText.isValid(value) || WebLinkText.isValid(value) {
            return true
        }
        return isSimpleHandle(value, min: 2, max: 64, allowDash: true)
    }

    static func isSimpleHandle(_ value: String, min: Int, max: Int, allowDash: Bool) -> Bool {
        guard !value.isEmpty, (min...max).contains(value.count) else { return false }

        let extraCharacters = allowDash ? "._-" : "_"
        let allowed = CharacterSet.alphanumerics.union(CharacterSet(charactersIn: extraCharacters))
        return value.unicodeScalars.allSatisfy { allowed.contains($0) }
    }
}
