import Foundation

/// A structured postal address for VCard compatibility and locale-aware formatting
/// Uses @preconcurrency Codable to allow nonisolated encoding/decoding in Swift 6
struct PostalAddress: Hashable, Sendable, @preconcurrency Codable {
    var street: String = ""         // Street address line 1 (can include apt/suite)
    var street2: String = ""        // Street address line 2 (optional)
    var city: String = ""
    var state: String = ""
    var postalCode: String = ""
    var country: String = ""
}

// MARK: - Computed Properties

extension PostalAddress {
    /// Whether all fields are empty
    nonisolated var isEmpty: Bool {
        street.isEmpty && street2.isEmpty && city.isEmpty &&
        state.isEmpty && postalCode.isEmpty && country.isEmpty
    }
    
    /// Whether at least one field has a value
    nonisolated var hasValue: Bool {
        !isEmpty
    }
    
    /// Formats for multi-line display
    /// Uses a standard format: Street, Street2, City State ZIP, Country
    nonisolated var formattedString: String {
        guard hasValue else { return "" }
        
        var lines: [String] = []
        
        if !street.isEmpty { lines.append(street) }
        if !street2.isEmpty { lines.append(street2) }
        
        // City, State ZIP on one line (US format)
        var cityStateLine: [String] = []
        if !city.isEmpty { cityStateLine.append(city) }
        if !state.isEmpty {
            cityStateLine.append(state)
        }
        if !postalCode.isEmpty {
            if cityStateLine.isEmpty {
                cityStateLine.append(postalCode)
            } else {
                // Add ZIP to the line
                let lastIndex = cityStateLine.count - 1
                cityStateLine[lastIndex] = "\(cityStateLine[lastIndex]) \(postalCode)"
            }
        }
        if !cityStateLine.isEmpty {
            lines.append(cityStateLine.joined(separator: ", "))
        }
        
        if !country.isEmpty { lines.append(country) }
        
        return lines.joined(separator: "\n")
    }
    
    /// Single-line formatted string for compact display
    nonisolated var singleLineString: String {
        var components: [String] = []
        
        if !street.isEmpty { components.append(street) }
        if !street2.isEmpty { components.append(street2) }
        if !city.isEmpty { components.append(city) }
        if !state.isEmpty { components.append(state) }
        if !postalCode.isEmpty { components.append(postalCode) }
        if !country.isEmpty { components.append(country) }
        
        return components.joined(separator: ", ")
    }
}

// MARK: - String Serialization

extension PostalAddress {
    /// Encodes to JSON string for storage in ContactField.value
    nonisolated func encode() -> String {
        guard let data = try? JSONEncoder().encode(self),
              let jsonString = String(data: data, encoding: .utf8) else {
            return ""
        }
        return jsonString
    }
    
    /// Decodes from JSON string (with backward compatibility for legacy pipe-delimited format)
    nonisolated static func decode(from string: String) -> PostalAddress? {
        guard !string.isEmpty else { return nil }
        
        // Try JSON decoding first (new format)
        if let data = string.data(using: .utf8),
           let address = try? JSONDecoder().decode(PostalAddress.self, from: data),
           address.hasValue {
            return address
        }
        
        // Fall back to legacy pipe-delimited format for migration
        return decodeLegacy(from: string)
    }
    
    /// Decodes from legacy pipe-delimited format (for backward compatibility)
    nonisolated private static func decodeLegacy(from string: String) -> PostalAddress? {
        let components = string.split(separator: "|", omittingEmptySubsequences: false).map(String.init)
        guard components.count == 6 else { return nil }
        
        let address = PostalAddress(
            street: components[0],
            street2: components[1],
            city: components[2],
            state: components[3],
            postalCode: components[4],
            country: components[5]
        )
        
        return address.hasValue ? address : nil
    }
}

// MARK: - VCard Support

extension PostalAddress {
    /// Generates a VCard ADR line
    /// Format: ADR;TYPE=type:;;street;city;state;zip;country
    nonisolated func vCardADRLine(type: String = "WORK") -> String {
        // VCard ADR format: PO Box; Extended Address; Street; City; State; Postal Code; Country
        // We use: ;;street;city;state;postalCode;country
        let streetCombined = street2.isEmpty ? street : "\(street), \(street2)"
        let escapedStreet = streetCombined.replacing(";", with: "\\;").replacing(",", with: "\\,")
        let escapedCity = city.replacing(";", with: "\\;").replacing(",", with: "\\,")
        let escapedState = state.replacing(";", with: "\\;").replacing(",", with: "\\,")
        let escapedPostal = postalCode.replacing(";", with: "\\;").replacing(",", with: "\\,")
        let escapedCountry = country.replacing(";", with: "\\;").replacing(",", with: "\\,")
        
        return "ADR;TYPE=\(type.uppercased()):;;\(escapedStreet);\(escapedCity);\(escapedState);\(escapedPostal);\(escapedCountry)"
    }
}
