import Foundation
import CoreImage
import UIKit

@objc public class QRCodeStudio: NSObject {
    private let historyKey = "QRCodeStudio.History"
    private let defaults = UserDefaults.standard
    
    @objc public func generate(
        type: String,
        data: [String: Any],
        options: [String: Any]
    ) throws -> [String: Any] {
        // Format data based on type
        let qrContent = formatQRData(type: type, data: data)
        
        // Generate QR code image
        guard let qrImage = generateQRImage(
            content: qrContent,
            size: options["size"] as? CGFloat ?? 300,
            foreground: options["foreground"] as? String ?? "#000000",
            background: options["background"] as? String ?? "#FFFFFF",
            errorCorrection: options["errorCorrection"] as? String ?? "M"
        ) else {
            throw QRGeneratorError.generationFailed
        }
        
        // Apply logo if provided
        var finalImage = qrImage
        if let logoData = options["logo"] as? String,
           let logo = decodeBase64Image(logoData) {
            finalImage = addLogoToQR(qrImage: qrImage, logo: logo)
        }
        
        // Convert to requested format
        let format = options["format"] as? String ?? "png"
        let quality = options["quality"] as? CGFloat ?? 0.9
        
        guard let imageData = encodeImage(finalImage, format: format, quality: quality) else {
            throw QRGeneratorError.encodingFailed
        }
        
        return [
            "content": qrContent,
            "type": type,
            "format": format,
            "data": imageData.base64EncodedString(),
            "size": [
                "width": Int(finalImage.size.width),
                "height": Int(finalImage.size.height)
            ]
        ]
    }
    
    @objc public func parseScanResult(_ content: String) -> [String: Any] {
        let type = detectQRType(content)
        let parsedData = parseQRContent(content, type: type)
        
        return [
            "content": content,
            "type": type,
            "data": parsedData,
            "timestamp": ISO8601DateFormatter().string(from: Date())
        ]
    }
    
    @objc public func saveToHistory(_ item: [String: Any]) {
        var history = getHistory()
        
        // Add new item to beginning
        var newItem = item
        newItem["id"] = UUID().uuidString
        newItem["timestamp"] = ISO8601DateFormatter().string(from: Date())
        
        history.insert(newItem, at: 0)
        
        // Keep only last 100 items
        if history.count > 100 {
            history = Array(history.prefix(100))
        }
        
        // Save to UserDefaults
        if let data = try? JSONSerialization.data(withJSONObject: history) {
            defaults.set(data, forKey: historyKey)
        }
    }
    
    @objc public func getHistory() -> [[String: Any]] {
        guard let data = defaults.data(forKey: historyKey),
              let history = try? JSONSerialization.jsonObject(with: data) as? [[String: Any]] else {
            return []
        }
        return history
    }
    
    @objc public func clearHistory() {
        defaults.removeObject(forKey: historyKey)
    }
    
    @objc public func generateBarcode(
        format: String,
        data: String,
        width: Int,
        height: Int,
        displayText: Bool,
        outputFormat: String
    ) throws -> [String: Any] {
        // For iOS, we'll use Core Image filters for basic barcodes
        // Note: iOS Core Image only supports limited barcode formats
        guard let barcodeImage = generateBarcodeImage(
            format: format,
            data: data,
            width: CGFloat(width),
            height: CGFloat(height)
        ) else {
            throw QRGeneratorError.generationFailed
        }
        
        // Convert to requested format
        guard let imageData = encodeImage(barcodeImage, format: outputFormat, quality: 1.0) else {
            throw QRGeneratorError.encodingFailed
        }
        
        return [
            "format": format,
            "data": data,
            "dataUrl": "data:image/\(outputFormat);base64,\(imageData.base64EncodedString())",
            "width": Int(barcodeImage.size.width),
            "height": Int(barcodeImage.size.height)
        ]
    }
    
    // MARK: - Private Methods
    
    private func formatQRData(type: String, data: [String: Any]) -> String {
        switch type.uppercased() {
        case "TEXT":
            return data["text"] as? String ?? ""
            
        case "WEBSITE":
            return data["url"] as? String ?? ""
            
        case "WIFI":
            let ssid = data["ssid"] as? String ?? ""
            let password = data["password"] as? String ?? ""
            let security = data["security"] as? String ?? "WPA"
            let hidden = data["hidden"] as? Bool ?? false
            return "WIFI:T:\(security);S:\(ssid);P:\(password);H:\(hidden);;"
            
        case "EMAIL":
            let to = data["to"] as? String ?? ""
            let subject = data["subject"] as? String ?? ""
            let body = data["body"] as? String ?? ""
            var mailto = "mailto:\(to)"
            var params: [String] = []
            if !subject.isEmpty { params.append("subject=\(subject.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? "")") }
            if !body.isEmpty { params.append("body=\(body.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? "")") }
            if !params.isEmpty { mailto += "?" + params.joined(separator: "&") }
            return mailto
            
        case "PHONE":
            let number = data["phoneNumber"] as? String ?? ""
            return "tel:\(number)"
            
        case "SMS":
            let number = data["phoneNumber"] as? String ?? ""
            let message = data["message"] as? String ?? ""
            return "sms:\(number)?body=\(message.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? "")"
            
        case "VCARD":
            var vcard = "BEGIN:VCARD\nVERSION:3.0\n"
            if let firstName = data["firstName"] as? String,
               let lastName = data["lastName"] as? String {
                vcard += "FN:\(firstName) \(lastName)\n"
                vcard += "N:\(lastName);\(firstName);;;\n"
            }
            if let org = data["organization"] as? String {
                vcard += "ORG:\(org)\n"
            }
            if let phone = data["phone"] as? String {
                vcard += "TEL:\(phone)\n"
            }
            if let email = data["email"] as? String {
                vcard += "EMAIL:\(email)\n"
            }
            if let website = data["website"] as? String {
                vcard += "URL:\(website)\n"
            }
            vcard += "END:VCARD"
            return vcard
            
        case "LOCATION":
            let latitude = data["latitude"] as? Double ?? 0
            let longitude = data["longitude"] as? Double ?? 0
            return "geo:\(latitude),\(longitude)"
            
        case "EVENT":
            let title = data["title"] as? String ?? ""
            let start = data["start"] as? String ?? ""
            let end = data["end"] as? String ?? ""
            let location = data["location"] as? String ?? ""
            let description = data["description"] as? String ?? ""
            
            var event = "BEGIN:VEVENT\n"
            event += "SUMMARY:\(title)\n"
            event += "DTSTART:\(start)\n"
            event += "DTEND:\(end)\n"
            if !location.isEmpty { event += "LOCATION:\(location)\n" }
            if !description.isEmpty { event += "DESCRIPTION:\(description)\n" }
            event += "END:VEVENT"
            return event
            
        case "CRYPTO":
            let address = data["address"] as? String ?? ""
            let amount = data["amount"] as? Double
            let currency = data["currency"] as? String ?? "bitcoin"
            
            var uri = "\(currency):\(address)"
            if let amount = amount {
                uri += "?amount=\(amount)"
            }
            return uri
            
        default:
            // For custom types, return JSON string
            if let jsonData = try? JSONSerialization.data(withJSONObject: data),
               let jsonString = String(data: jsonData, encoding: .utf8) {
                return jsonString
            }
            return ""
        }
    }
    
    private func detectQRType(_ content: String) -> String {
        if content.hasPrefix("http://") || content.hasPrefix("https://") {
            return "website"
        } else if content.hasPrefix("mailto:") {
            return "email"
        } else if content.hasPrefix("tel:") {
            return "phone"
        } else if content.hasPrefix("sms:") || content.hasPrefix("smsto:") {
            return "sms"
        } else if content.hasPrefix("WIFI:") {
            return "wifi"
        } else if content.hasPrefix("BEGIN:VCARD") {
            return "vcard"
        } else if content.hasPrefix("geo:") {
            return "location"
        } else if content.hasPrefix("BEGIN:VEVENT") {
            return "event"
        } else if content.hasPrefix("bitcoin:") || content.hasPrefix("ethereum:") {
            return "crypto"
        } else {
            return "text"
        }
    }
    
    private func parseQRContent(_ content: String, type: String) -> [String: Any] {
        switch type {
        case "website":
            return ["url": content]
            
        case "wifi":
            var data: [String: Any] = [:]
            let components = content.replacingOccurrences(of: "WIFI:", with: "").split(separator: ";")
            for component in components {
                let parts = component.split(separator: ":", maxSplits: 1)
                if parts.count == 2 {
                    let key = String(parts[0])
                    let value = String(parts[1])
                    switch key {
                    case "T": data["security"] = value
                    case "S": data["ssid"] = value
                    case "P": data["password"] = value
                    case "H": data["hidden"] = value == "true"
                    default: break
                    }
                }
            }
            return data
            
        case "email":
            let url = URL(string: content)
            var data: [String: Any] = [:]
            data["to"] = url?.absoluteString.replacingOccurrences(of: "mailto:", with: "").components(separatedBy: "?").first ?? ""
            if let components = URLComponents(url: url!, resolvingAgainstBaseURL: false) {
                for item in components.queryItems ?? [] {
                    if item.name == "subject" { data["subject"] = item.value ?? "" }
                    if item.name == "body" { data["body"] = item.value ?? "" }
                }
            }
            return data
            
        case "phone":
            return ["phoneNumber": content.replacingOccurrences(of: "tel:", with: "")]
            
        case "sms":
            let parts = content.replacingOccurrences(of: "sms:", with: "").split(separator: "?", maxSplits: 1)
            var data: [String: Any] = ["phoneNumber": String(parts.first ?? "")]
            if parts.count > 1, let body = parts.last?.replacingOccurrences(of: "body=", with: "").removingPercentEncoding {
                data["message"] = body
            }
            return data
            
        case "vcard":
            var data: [String: Any] = [:]
            let lines = content.components(separatedBy: "\n")
            for line in lines {
                if line.hasPrefix("FN:") {
                    let name = line.replacingOccurrences(of: "FN:", with: "")
                    let nameParts = name.split(separator: " ")
                    if nameParts.count >= 2 {
                        data["firstName"] = String(nameParts.first ?? "")
                        data["lastName"] = String(nameParts.dropFirst().joined(separator: " "))
                    }
                } else if line.hasPrefix("ORG:") {
                    data["organization"] = line.replacingOccurrences(of: "ORG:", with: "")
                } else if line.hasPrefix("TEL:") {
                    data["phone"] = line.replacingOccurrences(of: "TEL:", with: "")
                } else if line.hasPrefix("EMAIL:") {
                    data["email"] = line.replacingOccurrences(of: "EMAIL:", with: "")
                } else if line.hasPrefix("URL:") {
                    data["website"] = line.replacingOccurrences(of: "URL:", with: "")
                }
            }
            return data
            
        case "location":
            let coords = content.replacingOccurrences(of: "geo:", with: "").split(separator: ",")
            return [
                "latitude": Double(coords.first ?? "0") ?? 0,
                "longitude": Double(coords.last ?? "0") ?? 0
            ]
            
        default:
            return ["text": content]
        }
    }
    
    private func generateQRImage(
        content: String,
        size: CGFloat,
        foreground: String,
        background: String,
        errorCorrection: String
    ) -> UIImage? {
        let data = content.data(using: .utf8)
        
        guard let filter = CIFilter(name: "CIQRCodeGenerator") else { return nil }
        filter.setValue(data, forKey: "inputMessage")
        
        // Set error correction level
        let correctionLevel: String
        switch errorCorrection {
        case "L": correctionLevel = "L"
        case "M": correctionLevel = "M"
        case "Q": correctionLevel = "Q"
        case "H": correctionLevel = "H"
        default: correctionLevel = "M"
        }
        filter.setValue(correctionLevel, forKey: "inputCorrectionLevel")
        
        guard let outputImage = filter.outputImage else { return nil }
        
        // Scale the image
        let scale = size / outputImage.extent.width
        let scaledImage = outputImage.transformed(by: CGAffineTransform(scaleX: scale, y: scale))
        
        // Apply colors
        guard let colorFilter = CIFilter(name: "CIFalseColor") else { return nil }
        colorFilter.setValue(scaledImage, forKey: "inputImage")
        colorFilter.setValue(CIColor(string: foreground), forKey: "inputColor0")
        colorFilter.setValue(CIColor(string: background), forKey: "inputColor1")
        
        guard let coloredImage = colorFilter.outputImage else { return nil }
        
        // Convert to UIImage
        let context = CIContext()
        guard let cgImage = context.createCGImage(coloredImage, from: coloredImage.extent) else { return nil }
        
        return UIImage(cgImage: cgImage)
    }
    
    private func addLogoToQR(qrImage: UIImage, logo: UIImage) -> UIImage {
        let size = qrImage.size
        let logoSize = CGSize(
            width: size.width * 0.25,
            height: size.height * 0.25
        )
        
        UIGraphicsBeginImageContextWithOptions(size, false, 0)
        
        // Draw QR code
        qrImage.draw(in: CGRect(origin: .zero, size: size))
        
        // Draw white background for logo
        let logoBackgroundRect = CGRect(
            x: (size.width - logoSize.width * 1.2) / 2,
            y: (size.height - logoSize.height * 1.2) / 2,
            width: logoSize.width * 1.2,
            height: logoSize.height * 1.2
        )
        UIColor.white.setFill()
        UIBezierPath(roundedRect: logoBackgroundRect, cornerRadius: 10).fill()
        
        // Draw logo
        let logoRect = CGRect(
            x: (size.width - logoSize.width) / 2,
            y: (size.height - logoSize.height) / 2,
            width: logoSize.width,
            height: logoSize.height
        )
        logo.draw(in: logoRect)
        
        let resultImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        
        return resultImage ?? qrImage
    }
    
    private func decodeBase64Image(_ base64String: String) -> UIImage? {
        let cleanedString = base64String
            .replacingOccurrences(of: "data:image/png;base64,", with: "")
            .replacingOccurrences(of: "data:image/jpeg;base64,", with: "")
            .replacingOccurrences(of: "data:image/jpg;base64,", with: "")
        
        guard let data = Data(base64Encoded: cleanedString) else { return nil }
        return UIImage(data: data)
    }
    
    private func encodeImage(_ image: UIImage, format: String, quality: CGFloat) -> Data? {
        switch format.lowercased() {
        case "png":
            return image.pngData()
        case "jpeg", "jpg":
            return image.jpegData(compressionQuality: quality)
        default:
            return image.pngData()
        }
    }
    
    private func generateBarcodeImage(
        format: String,
        data: String,
        width: CGFloat,
        height: CGFloat
    ) -> UIImage? {
        let filterName: String
        
        // Map format to Core Image filter
        switch format {
        case "QR_CODE":
            filterName = "CIQRCodeGenerator"
        case "CODE_128":
            filterName = "CICode128BarcodeGenerator"
        case "PDF_417":
            filterName = "CIPDF417BarcodeGenerator"
        case "AZTEC":
            filterName = "CIAztecCodeGenerator"
        default:
            // For unsupported formats, generate a QR code as fallback
            filterName = "CIQRCodeGenerator"
        }
        
        guard let filter = CIFilter(name: filterName) else { return nil }
        
        let inputData = data.data(using: .utf8)
        filter.setValue(inputData, forKey: "inputMessage")
        
        // Set quiet zone for better readability
        if filter.inputKeys.contains("inputQuietSpace") {
            filter.setValue(7.0, forKey: "inputQuietSpace")
        }
        
        // Set error correction for supported formats
        if filter.inputKeys.contains("inputCorrectionLevel") {
            filter.setValue("M", forKey: "inputCorrectionLevel")
        }
        
        guard let outputImage = filter.outputImage else { return nil }
        
        // Calculate scale to fit requested size
        let scaleX = width / outputImage.extent.width
        let scaleY = height / outputImage.extent.height
        let scale = min(scaleX, scaleY)
        
        let scaledImage = outputImage.transformed(by: CGAffineTransform(scaleX: scale, y: scale))
        
        // Convert to UIImage
        let context = CIContext()
        guard let cgImage = context.createCGImage(scaledImage, from: scaledImage.extent) else { return nil }
        
        return UIImage(cgImage: cgImage)
    }
}

// MARK: - Error Types

enum QRGeneratorError: LocalizedError {
    case generationFailed
    case encodingFailed
    case invalidData
    
    var errorDescription: String? {
        switch self {
        case .generationFailed:
            return "Failed to generate QR code"
        case .encodingFailed:
            return "Failed to encode image"
        case .invalidData:
            return "Invalid data provided"
        }
    }
}

// MARK: - CIColor Extension

extension CIColor {
    convenience init?(string: String) {
        let hex = string.trimmingCharacters(in: CharacterSet.alphanumerics.inverted)
        var int: UInt64 = 0
        Scanner(string: hex).scanHexInt64(&int)
        let a, r, g, b: UInt64
        switch hex.count {
        case 3: // RGB (12-bit)
            (a, r, g, b) = (255, (int >> 8) * 17, (int >> 4 & 0xF) * 17, (int & 0xF) * 17)
        case 6: // RGB (24-bit)
            (a, r, g, b) = (255, int >> 16, int >> 8 & 0xFF, int & 0xFF)
        case 8: // ARGB (32-bit)
            (a, r, g, b) = (int >> 24, int >> 16 & 0xFF, int >> 8 & 0xFF, int & 0xFF)
        default:
            return nil
        }
        
        self.init(
            red: CGFloat(r) / 255,
            green: CGFloat(g) / 255,
            blue: CGFloat(b) / 255,
            alpha: CGFloat(a) / 255
        )
    }
}