//
//  CardValidation.swift
//  DWP
//
//  Created by Ahmed Wahdan on 19/12/2024.
//

import Foundation

class CardValidation {
    static let shared = CardValidation()
    private init() {}
    
    public func isValidCardNumber(_ cardNumber: String) -> Bool {
        
        let sanitizedNumber = preprocessCardNumber(cardNumber)
        guard sanitizedNumber.allSatisfy({ $0.isNumber }) else { return false }
        let digits = sanitizedNumber.compactMap { $0.wholeNumberValue }
        print("cardNumber: \(cardNumber)")
        
        // Allow 14, 15, or 16 digit card numbers
        guard digits.count == 14 || digits.count == 15 || digits.count == 16 else { return false }
        
        let checksum = digits.reversed().enumerated().reduce(0) { sum, pair in
            let (index, digit) = pair
            if index % 2 == 1 {
                let doubled = digit * 2
                return sum + (doubled > 9 ? doubled - 9 : doubled)
            } else {
                return sum + digit
            }
        }
        
        return checksum % 10 == 0 // true
    }
    
    public func preprocessCardNumber(_ cardNumber: String) -> String {
        // Remove spaces and non-numeric characters
        return cardNumber.replacingOccurrences(of: "\\D", with: "", options: .regularExpression)
    }
    
    
    public func getCardType(for cardNumber: String) -> String? {
        let prefixes = [
            "Visa": ["4"],
            "MasterCard": ["51", "52", "53", "54", "55"],
            "American Express": ["34", "37"],
            "Discover": ["6011", "65"],
            "JCB": ["35"],
            "Diners Club": ["300", "301", "302", "303", "304", "305", "36", "38"]
        ]
        for (type, prefixArray) in prefixes {
            for prefix in prefixArray where cardNumber.starts(with: prefix) {
                return type
            }
        }
        return nil
    }
    
    public func isValidExpiryDate(_ expiryDate: String) -> Bool {
        let shortDateFormatter = DateFormatter()
        shortDateFormatter.dateFormat = "MM/yy"
        
        let longDateFormatter = DateFormatter()
        longDateFormatter.dateFormat = "MM/yyyy"
        
        // Try parsing the short format (MM/yy)
        if let shortDate = shortDateFormatter.date(from: expiryDate), shortDate > Date() {
            return true
        }
        
        // Try parsing the long format (MM/yyyy)
        if let longDate = longDateFormatter.date(from: expiryDate), longDate > Date() {
            return true
        }
        
        // If neither format works, return false
        return false
    }
    
    public func isValidName(_ name: String) -> Bool {
        let ignoreList = ["VALID", "THRU", "MONTH", "YEAR", "EXPIRES", "DATE", "MASTER", "DEBIT", "END", "CREDIT", "CARD", "EXPRESS", "AMEX", "GOOD THRU", "GOOD", "Gold", "GOLD", "Standard", "STANDARD", "Platinum", "PLATINUM", "WORLD ELITE", "WORLD", "ELITE", "World Elite", "World", "Elite", "Discover"]
        
        let uppercasedName = name.uppercased()
        
        for term in ignoreList {
            if uppercasedName.contains(term) { return false }
        }
        
        let namePattern = #"^[A-Z]+( [A-Z]+){1,2}$"#  // Matches two or three capitalized words
        let regex = try? NSRegularExpression(pattern: namePattern)
        let range = NSRange(location: 0, length: name.utf16.count)
        
        return regex?.firstMatch(in: name, options: [], range: range) != nil
    }
    
    public func normalizeName(_ name: String) -> String {
        return name.lowercased()
            .split(separator: " ")
            .map { $0.capitalized }
            .joined(separator: " ")
    }

    // Extract expiry date using regex
    public func extractExpiryDate(from text: String) -> String? {
        let patterns = [
            #"^(0[1-9]|1[0-2])/([0-9]{2})$"#,  // MM/yy format
            #"^(0[1-9]|1[0-2])/([0-9]{4})$"#  // MM/yyyy format
        ]
        
        for pattern in patterns {
            if let range = text.range(of: pattern, options: .regularExpression) {
                let extractedDate = String(text[range])
                if isValidExpiryDate(extractedDate) {
                    return extractedDate
                }
            }
        }
        return nil
    }

    // Filter irrelevant texts
    public func filterRelevantTexts(_ texts: [String]) -> [String] {
        return texts.filter { text in
            let regex = #"^[A-Za-z0-9\s/:]{3,}$"#
            return text.range(of: regex, options: .regularExpression) != nil
        }
    }
    
}

enum ValidationError: Error {
    case invalidCardNumber
    case invalidExpiryDate
    case invalidName
}
