import Foundation

/// Utility for parsing and cleaning error messages from API responses and network errors
enum ErrorMessageParser {

    // MARK: - API Error Code Mappings

    /// Maps backend error codes to user-friendly messages
    private static let errorCodeMappings: [String: String] = [
        // Authentication errors
        "error.invalid_credentials": "Invalid username or password. Please try again.",
        "error.invalid_token": "Your session has expired. Please log in again.",
        "error.not_authenticated": "Please log in to continue.",
        "error.account_inactive": "Your account is inactive. Please contact support.",
        "error.username_taken": "This username is already taken. Please choose another.",
        "error.email_taken": "This email is already registered. Try logging in instead.",
        "error.email_already_taken": "This email is already in use.",
        "error.registration_failed": "Registration failed. Please try again.",
        "error.failed_to_get_user": "Unable to load your profile. Please try again.",
        "error.failed_to_update_profile": "Unable to update your profile. Please try again.",

        // Email verification errors
        "error.invalid_verification_code": "Invalid verification code. Please check and try again.",
        "error.verification_code_expired": "Your verification code has expired. Please request a new one.",
        "error.email_already_verified": "Your email is already verified.",
        "error.verification_failed": "Verification failed. Please try again.",
        "error.failed_to_resend_verification": "Unable to send verification code. Please try again.",

        // Password reset errors
        "error.rate_limit_exceeded": "Too many attempts. Please wait a few minutes and try again.",
        "error.too_many_requests": "Too many requests. Please try again later.",
        "error.too_many_attempts": "Too many attempts. Please request a new code.",
        "error.invalid_reset_token": "This reset link has expired. Please request a new one.",
        "error.password_reset_failed": "Password reset failed. Please try again.",

        // Social sign-in errors
        "error.apple_signin_not_configured": "Apple Sign In is not available. Please use email login.",
        "error.apple_signin_failed": "Apple Sign In failed. Please try again.",
        "error.invalid_apple_token": "Apple Sign In failed. Please try again.",
        "error.google_signin_not_configured": "Google Sign In is not available. Please use email login.",
        "error.google_signin_failed": "Google Sign In failed. Please try again.",
        "error.invalid_google_token": "Google Sign In failed. Please try again.",

        // Resource not found errors
        "error.task_not_found": "Task not found. It may have been deleted.",
        "error.residence_not_found": "Property not found. It may have been deleted.",
        "error.contractor_not_found": "Contractor not found. It may have been deleted.",
        "error.document_not_found": "Document not found. It may have been deleted.",
        "error.completion_not_found": "Task completion not found.",
        "error.user_not_found": "User not found.",
        "error.notification_not_found": "Notification not found.",
        "error.template_not_found": "Task template not found.",
        "error.upgrade_trigger_not_found": "Feature not available.",

        // Access denied errors
        "error.task_access_denied": "You don't have permission to view this task.",
        "error.residence_access_denied": "You don't have permission to view this property.",
        "error.contractor_access_denied": "You don't have permission to view this contractor.",
        "error.document_access_denied": "You don't have permission to view this document.",
        "error.not_residence_owner": "Only the property owner can do this.",
        "error.cannot_remove_owner": "The property owner cannot be removed.",
        "error.access_denied": "You don't have permission for this action.",

        // Sharing errors
        "error.share_code_invalid": "Invalid share code. Please check and try again.",
        "error.share_code_expired": "This share code has expired. Please request a new one.",
        "error.user_already_member": "This user is already a member of this property.",

        // Subscription/limit errors
        "error.properties_limit_reached": "You've reached your property limit. Upgrade to add more.",
        "error.properties_limit_exceeded": "You've reached your property limit. Upgrade to add more.",
        "error.tasks_limit_exceeded": "You've reached your task limit. Upgrade to add more.",
        "error.contractors_limit_exceeded": "You've reached your contractor limit. Upgrade to add more.",
        "error.documents_limit_exceeded": "You've reached your document limit. Upgrade to add more.",

        // Task state errors
        "error.task_already_cancelled": "This task has already been cancelled.",
        "error.task_already_archived": "This task has already been archived.",

        // Form/upload errors
        "error.failed_to_parse_form": "Unable to process the form. Please try again.",
        "error.task_id_required": "Task ID is required.",
        "error.residence_id_required": "Property ID is required.",
        "error.title_required": "Title is required.",
        "error.failed_to_upload_image": "Unable to upload image. Please try again.",
        "error.failed_to_upload_file": "Unable to upload file. Please try again.",
        "error.no_file_provided": "Please select a file to upload.",

        // Invalid ID errors
        "error.invalid_task_id": "Invalid task.",
        "error.invalid_task_id_value": "Invalid task.",
        "error.invalid_residence_id": "Invalid property.",
        "error.invalid_residence_id_value": "Invalid property.",
        "error.invalid_contractor_id": "Invalid contractor.",
        "error.invalid_document_id": "Invalid document.",
        "error.invalid_completion_id": "Invalid task completion.",
        "error.invalid_user_id": "Invalid user.",
        "error.invalid_notification_id": "Invalid notification.",
        "error.invalid_device_id": "Invalid device.",
        "error.invalid_platform": "Invalid platform.",
        "error.invalid_id": "Invalid ID.",

        // Data fetch errors
        "error.failed_to_fetch_residence_types": "Unable to load property types. Please try again.",
        "error.failed_to_fetch_task_categories": "Unable to load task categories. Please try again.",
        "error.failed_to_fetch_task_priorities": "Unable to load task priorities. Please try again.",
        "error.failed_to_fetch_task_frequencies": "Unable to load task frequencies. Please try again.",
        "error.failed_to_fetch_task_statuses": "Unable to load task statuses. Please try again.",
        "error.failed_to_fetch_contractor_specialties": "Unable to load contractor specialties. Please try again.",
        "error.failed_to_fetch_templates": "Unable to load task templates. Please try again.",
        "error.failed_to_search_templates": "Unable to search templates. Please try again.",

        // Subscription purchase errors
        "error.receipt_data_required": "Purchase verification failed. Please try again.",
        "error.purchase_token_required": "Purchase verification failed. Please try again.",

        // Media errors
        "error.file_not_found": "File not found.",
        "error.image_not_found": "Image not found.",

        // Generic errors
        "error.invalid_request": "Invalid request. Please try again.",
        "error.invalid_request_body": "Invalid request. Please check your input.",
        "error.internal": "Something went wrong. Please try again.",
        "error.query_required": "Search query is required.",
        "error.query_too_short": "Search query is too short."
    ]

    // MARK: - Network Error Patterns

    /// Network/connection error patterns to detect
    private static let networkErrorPatterns: [(pattern: String, message: String)] = [
        ("Could not connect to the server", "Unable to connect. Please check your internet connection."),
        ("NSURLErrorDomain", "Unable to connect. Please check your internet connection."),
        ("The Internet connection appears to be offline", "No internet connection. Please check your network."),
        ("A server with the specified hostname could not be found", "Unable to connect. Please check your internet connection."),
        ("The request timed out", "Request timed out. Please try again."),
        ("The network connection was lost", "Connection lost. Please try again."),
        ("An SSL error has occurred", "Secure connection failed. Please try again."),
        ("CFNetwork", "Unable to connect. Please check your internet connection."),
        ("kCFStreamError", "Unable to connect. Please check your internet connection."),
        ("Code=-1004", "Unable to connect. Please check your internet connection."),
        ("Code=-1009", "No internet connection. Please check your network."),
        ("Code=-1001", "Request timed out. Please try again."),
        ("Code=-1003", "Unable to connect. Please check your internet connection."),
        ("Code=-1005", "Connection lost. Please try again."),
        ("Code=-1200", "Secure connection failed. Please try again."),
        ("UnresolvedAddressException", "Unable to connect. Please check your internet connection."),
        ("ConnectException", "Unable to connect. Please check your internet connection."),
        ("SocketTimeoutException", "Request timed out. Please try again."),
        ("Connection refused", "Unable to connect. The server may be temporarily unavailable."),
        ("Connection reset", "Connection lost. Please try again."),
        ("Too many requests", "Too many requests. Please try again later.")
    ]

    // MARK: - Technical Error Indicators

    /// Indicators that a message is technical/developer-facing
    private static let technicalIndicators = [
        "Exception",
        "Error Domain=",
        "UserInfo=",
        "at com.",
        "at org.",
        "at java.",
        "at kotlin.",
        "at io.",
        "Caused by:",
        "Stack trace:",
        ".kt:",
        ".java:",
        ".swift:",
        "0x",
        "Code=",
        "interface:",
        "LocalDataTask",
        "NSUnderlyingError",
        "_kCF"
    ]

    // MARK: - Public Methods

    /// Parses error messages to extract user-friendly text
    /// Handles API error codes, network errors, JSON responses, and raw error messages
    /// - Parameter rawMessage: The raw error message from the API or exception
    /// - Returns: A user-friendly error message
    static func parse(_ rawMessage: String) -> String {
        let trimmed = rawMessage.trimmingCharacters(in: .whitespacesAndNewlines)

        // Check for known API error codes first (e.g., "error.invalid_token")
        if trimmed.hasPrefix("error.") {
            if let friendlyMessage = errorCodeMappings[trimmed] {
                return friendlyMessage
            }
            // Unknown error code - generate a generic message from the code
            return generateMessageFromCode(trimmed)
        }

        // Check for network/connection errors
        for (pattern, friendlyMessage) in networkErrorPatterns {
            if trimmed.localizedCaseInsensitiveContains(pattern) {
                return friendlyMessage
            }
        }

        // Check if it looks like a technical exception message
        if isTechnicalError(trimmed) {
            return "Something went wrong. Please try again."
        }

        // Check if the message looks like JSON (starts with { or [)
        guard trimmed.hasPrefix("{") || trimmed.hasPrefix("[") else {
            // Not JSON - if it's a short, readable message, return it
            // Otherwise return a generic message
            return isUserFriendly(trimmed) ? trimmed : "Something went wrong. Please try again."
        }

        // If it's JSON, try to parse and extract meaningful error info
        guard let data = trimmed.data(using: .utf8) else {
            return "An error occurred. Please try again."
        }

        do {
            if let json = try JSONSerialization.jsonObject(with: data) as? [String: Any] {
                // Try to find common error fields
                if let errorMsg = json["error"] as? String {
                    // Recursively parse the error message (it might be an error code)
                    return parse(errorMsg)
                }
                if let message = json["message"] as? String {
                    return parse(message)
                }
                if let detail = json["detail"] as? String {
                    return parse(detail)
                }

                // Check if this looks like a data object (has id, title, etc)
                if json["id"] != nil && (json["title"] != nil || json["name"] != nil) {
                    return "Request failed. Please check your input and try again."
                }
            }
        } catch {
            // JSON parsing failed
        }

        // If we couldn't parse or extract a message, return a generic error
        return "An error occurred. Please try again."
    }

    // MARK: - Private Methods

    /// Generates a user-friendly message from an unknown error code
    private static func generateMessageFromCode(_ code: String) -> String {
        // Remove "error." prefix and convert underscores to spaces
        var message = code.replacingOccurrences(of: "error.", with: "")
        message = message.replacingOccurrences(of: "_", with: " ")

        // Capitalize first letter
        if let firstChar = message.first {
            message = firstChar.uppercased() + message.dropFirst()
        }

        return message + ". Please try again."
    }

    /// Checks if the message looks like a technical/developer error (stack trace, exception, etc)
    private static func isTechnicalError(_ message: String) -> Bool {
        return technicalIndicators.contains { message.localizedCaseInsensitiveContains($0) }
    }

    /// Checks if a message is user-friendly (short, no technical jargon)
    private static func isUserFriendly(_ message: String) -> Bool {
        // If it's short and doesn't contain technical indicators, it's probably user-friendly
        return message.count < 200 && !isTechnicalError(message)
    }
}
