//
//  FontUtils.swift
//  TheNoiseClock
//
//  Created by Matt Bruce on 9/7/25.
//

import SwiftUI

/// Font sizing and typography utilities
enum FontUtils {
    
    /// Calculate dynamic font size based on container dimensions
    /// - Parameters:
    ///   - containerWidth: Container width
    ///   - containerHeight: Container height
    /// - Returns: Calculated font size
    static func dynamicFontSize(containerWidth: CGFloat, containerHeight: CGFloat) -> CGFloat {
        let shortest = min(containerWidth, containerHeight)
        return min(shortest * 0.28, AppConstants.Defaults.maxFontSize)
    }
    
    /// Calculate optimal font size that maximizes space usage
    /// - Parameters:
    ///   - containerWidth: Available container width
    ///   - containerHeight: Available container height
    ///   - isPortrait: Whether the device is in portrait orientation
    ///   - showSeconds: Whether seconds are displayed
    ///   - showAmPm: Whether AM/PM is displayed
    /// - Returns: Optimal font size for maximum space utilization
    static func optimalFontSize(
        containerWidth: CGFloat,
        containerHeight: CGFloat,
        isPortrait: Bool,
        showSeconds: Bool = false,
        showAmPm: Bool = false
    ) -> CGFloat {
        // Account for safe areas and padding
        let safeInset = AppConstants.Defaults.safeInset
        let availableWidth = max(1, containerWidth - safeInset * 2)
        let availableHeight = max(1, containerHeight - safeInset * 2)
        
        // Estimate text content requirements (for future use)
        _ = showSeconds ? 6 : 4 // HH:MM or HH:MM:SS
        _ = showAmPm ? 2 : 0 // AM/PM
        
        // Calculate optimal size based on orientation and content
        let optimalSize: CGFloat
        if isPortrait {
            // In portrait, height is the limiting factor
            // Account for separators and spacing
            let separatorHeight = availableHeight * 0.08 // 8% for separators (reduced)
            let contentHeight = availableHeight - separatorHeight
            let estimatedLines = showSeconds ? 3 : 2 // HH, MM, SS or HH, MM
            let lineHeight = contentHeight / CGFloat(estimatedLines)
            optimalSize = lineHeight * 0.85 // 85% of line height for actual text (increased)
        } else {
            // In landscape, be more aggressive with space usage
            // Account for separators and spacing
            let separatorWidth = availableWidth * 0.08 // 8% for separators (reduced)
            let contentWidth = availableWidth - separatorWidth
            let estimatedColumns = showSeconds ? 3 : 2 // HH, MM, SS or HH, MM
            let columnWidth = contentWidth / CGFloat(estimatedColumns)
            optimalSize = columnWidth * 0.75 // 75% of column width for actual text (increased)
        }
        
        // Apply reasonable bounds
        let minSize: CGFloat = 20
        let maxSize: CGFloat = AppConstants.Defaults.maxFontSize
        return max(minSize, min(optimalSize, maxSize))
    }
    
    /// Calculate font size that fills available space with scaling
    /// - Parameters:
    ///   - containerWidth: Available container width
    ///   - containerHeight: Available container height
    ///   - textContent: The actual text content to measure
    ///   - isPortrait: Whether the device is in portrait orientation
    /// - Returns: Font size that maximizes space usage
    static func fillSpaceFontSize(
        containerWidth: CGFloat,
        containerHeight: CGFloat,
        textContent: String,
        isPortrait: Bool
    ) -> CGFloat {
        let safeInset = AppConstants.Defaults.safeInset
        let availableWidth = max(1, containerWidth - safeInset * 2)
        let availableHeight = max(1, containerHeight - safeInset * 2)
        
        // Start with a reasonable base size
        let baseSize = isPortrait ? availableHeight * 0.3 : availableWidth * 0.15
        
        // Binary search for optimal size
        var low: CGFloat = 10
        var high: CGFloat = AppConstants.Defaults.maxFontSize
        var bestSize: CGFloat = baseSize
        
        for _ in 0..<10 { // Limit iterations
            let testSize = (low + high) / 2
            let font = UIFont.systemFont(ofSize: testSize, weight: .bold)
            let textSize = measureTextSize(text: textContent, font: font)
            
            let fitsWidth = textSize.width <= availableWidth
            let fitsHeight = textSize.height <= availableHeight
            
            if fitsWidth && fitsHeight {
                bestSize = testSize
                low = testSize
            } else {
                high = testSize
            }
        }
        
        return bestSize
    }
    
    /// Calculate AM/PM font size based on base font size
    /// - Parameter baseFontSize: Base font size
    /// - Returns: AM/PM font size (20% of base)
    static func ampmFontSize(baseFontSize: CGFloat) -> CGFloat {
        return baseFontSize * 0.20
    }
    
    // MARK: - Font Customization
    
    /// Convert font family string to Font
    /// - Parameter family: Font family name
    /// - Returns: SwiftUI Font
    static func fontFamily(_ family: String) -> Font {
        switch family {
        case "San Francisco":
            return .system(.body, design: .default)
        case "System":
            return .system(.body, design: .default)
        case "Monaco":
            return .system(.body, design: .monospaced)
        case "Courier":
            return .system(.body, design: .monospaced)
        default:
            return .system(.body, design: .default)
        }
    }
    
    /// Convert font weight string to Font.Weight
    /// - Parameter weight: Font weight name
    /// - Returns: Font.Weight
    static func fontWeight(_ weight: String) -> Font.Weight {
        switch weight {
        case "Ultra Light":
            return .ultraLight
        case "Thin":
            return .thin
        case "Light":
            return .light
        case "Regular":
            return .regular
        case "Medium":
            return .medium
        case "Semibold":
            return .semibold
        case "Bold":
            return .bold
        case "Heavy":
            return .heavy
        case "Black":
            return .black
        default:
            return .bold
        }
    }
    
    /// Convert font design string to Font.Design
    /// - Parameter design: Font design name
    /// - Returns: Font.Design
    static func fontDesign(_ design: String) -> Font.Design {
        switch design {
        case "Default":
            return .default
        case "Serif":
            return .serif
        case "Rounded":
            return .rounded
        case "Monospaced":
            return .monospaced
        default:
            return .rounded
        }
    }
    
    /// Create a custom font with specified parameters
    /// - Parameters:
    ///   - size: Font size
    ///   - family: Font family name
    ///   - weight: Font weight name
    ///   - design: Font design name
    /// - Returns: SwiftUI Font
    static func customFont(
        size: CGFloat,
        family: String,
        weight: String,
        design: String
    ) -> Font {
        return .system(
            size: size,
            weight: fontWeight(weight),
            design: fontDesign(design)
        )
    }
    
    /// Calculate the maximum width for any two-digit combination
    /// - Parameters:
    ///   - font: The font to measure with
    ///   - fontSize: The font size
    /// - Returns: Maximum width for any two-digit combination
    static func maxTwoDigitWidth(font: UIFont, fontSize: CGFloat) -> CGFloat {
        let digits = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]
        var maxWidth: CGFloat = 0
        
        // Test all possible two-digit combinations to find the widest
        for firstDigit in digits {
            for secondDigit in digits {
                let combination = firstDigit + secondDigit
                let width = measureTextSize(text: combination, font: font).width
                maxWidth = max(maxWidth, width)
            }
        }
        
        return maxWidth
    }
    
    /// Get weight multiplier for visual consistency with font weight
    /// - Parameter weight: Font weight name
    /// - Returns: Multiplier for dot size (0.7 to 1.3)
    static func weightMultiplier(for weight: String) -> CGFloat {
        switch weight {
        case "Ultra Light":
            return 0.7
        case "Thin":
            return 0.75
        case "Light":
            return 0.8
        case "Regular":
            return 0.85
        case "Medium":
            return 0.9
        case "Semibold":
            return 1.0
        case "Bold":
            return 1.1
        case "Heavy":
            return 1.2
        case "Black":
            return 1.3
        default:
            return 1.0
        }
    }
    
    /// Create a UIFont with specified parameters for measurements
    /// - Parameters:
    ///   - size: Font size
    ///   - family: Font family name
    ///   - weight: Font weight name
    ///   - design: Font design name
    /// - Returns: UIFont
    static func customUIFont(
        size: CGFloat,
        family: String,
        weight: String,
        design: String
    ) -> UIFont {
        let uiWeight: UIFont.Weight
        switch weight {
        case "Ultra Light":
            uiWeight = .ultraLight
        case "Thin":
            uiWeight = .thin
        case "Light":
            uiWeight = .light
        case "Regular":
            uiWeight = .regular
        case "Medium":
            uiWeight = .medium
        case "Semibold":
            uiWeight = .semibold
        case "Bold":
            uiWeight = .bold
        case "Heavy":
            uiWeight = .heavy
        case "Black":
            uiWeight = .black
        default:
            uiWeight = .bold
        }
        
        let uiDesign: UIFontDescriptor.SystemDesign
        switch design {
        case "Serif":
            uiDesign = .serif
        case "Rounded":
            uiDesign = .rounded
        case "Monospaced":
            uiDesign = .monospaced
        default:
            uiDesign = .default
        }
        
        let descriptor = UIFontDescriptor.preferredFontDescriptor(withTextStyle: .body)
            .withDesign(uiDesign) ?? UIFontDescriptor.preferredFontDescriptor(withTextStyle: .body)
        
        return UIFont(descriptor: descriptor.addingAttributes([.traits: [UIFontDescriptor.TraitKey.weight: uiWeight]]), size: size)
    }
    
    /// Measure text size with given font
    /// - Parameters:
    ///   - text: Text to measure
    ///   - font: Font to use for measurement
    /// - Returns: Size of the text
    static func measureTextSize(text: String, font: UIFont) -> CGSize {
        let attributes = [NSAttributedString.Key.font: font]
        return (text as NSString).size(withAttributes: attributes)
    }
    
    /// Get the maximum character width for digits to ensure consistent spacing
    /// - Parameter font: The font to measure with
    /// - Returns: The width of the widest digit character
    static func maxDigitWidth(font: UIFont) -> CGFloat {
        let digits = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]
        var maxWidth: CGFloat = 0
        
        for digit in digits {
            let size = measureTextSize(text: digit, font: font)
            maxWidth = max(maxWidth, size.width)
        }
        
        return maxWidth
    }
    
    /// Calculate fixed-width layout size for time display to prevent jumping
    /// - Parameters:
    ///   - font: The font to use for measurements
    ///   - showSeconds: Whether seconds are displayed
    ///   - showAmPm: Whether AM/PM is displayed
    ///   - isPortrait: Whether the device is in portrait orientation
    /// - Returns: Total width and height needed for the layout
    static func fixedWidthLayoutSize(
        font: UIFont,
        showSeconds: Bool,
        showAmPm: Bool,
        isPortrait: Bool
    ) -> (width: CGFloat, height: CGFloat) {
        let digitWidth = maxDigitWidth(font: font)
        let digitHeight = measureTextSize(text: "8", font: font).height // Use 8 as reference height
        
        // Calculate separator sizes
        let dotDiameter = font.pointSize * 0.20
        let hSpacing = font.pointSize * 0.18
        let vSpacing = font.pointSize * 0.22
        
        if isPortrait {
            // Portrait: vertical layout
            let totalWidth = digitWidth * 2 // Two digits per row
            let separatorHeight = dotDiameter * 2 + vSpacing
            let contentHeight = digitHeight * (showSeconds ? 3 : 2) + separatorHeight
            return (totalWidth, contentHeight)
        } else {
            // Landscape: horizontal layout
            let digitCount = showSeconds ? 6 : 4 // HH:MM or HH:MM:SS
            let separatorCount = showSeconds ? 2 : 1
            let separatorWidth = dotDiameter * 2 + hSpacing
            let totalWidth = digitWidth * CGFloat(digitCount) + separatorWidth * CGFloat(separatorCount)
            let totalHeight = digitHeight
            return (totalWidth, totalHeight)
        }
    }
    
    /// Calculate responsive font size for time picker components
    /// - Parameters:
    ///   - containerWidth: Available container width
    ///   - containerHeight: Available container height
    ///   - isPortrait: Whether the device is in portrait orientation
    /// - Returns: Font size optimized for time picker
    static func timePickerFontSize(
        containerWidth: CGFloat,
        containerHeight: CGFloat,
        isPortrait: Bool
    ) -> CGFloat {
        let safeInset = AppConstants.Defaults.safeInset
        let availableWidth = max(1, containerWidth - safeInset * 2)
        let availableHeight = max(1, containerHeight - safeInset * 2)
        
        // For time picker, we want larger, more readable fonts
        let baseSize = isPortrait ? availableHeight * 0.15 : availableWidth * 0.08
        
        // Apply bounds with higher minimum for readability
        let minSize: CGFloat = 24
        let maxSize: CGFloat = 72
        return max(minSize, min(baseSize, maxSize))
    }
    
    /// Calculate maximum font size for stretched mode that uses visible space without overflow
    /// - Parameters:
    ///   - containerWidth: Available container width
    ///   - containerHeight: Available container height
    ///   - isPortrait: Whether the device is in portrait orientation
    ///   - showSeconds: Whether seconds are displayed
    ///   - showAmPm: Whether AM/PM is displayed
    /// - Returns: Maximum font size that fits within visible space
    static func maximumStretchedFontSize(
        containerWidth: CGFloat,
        containerHeight: CGFloat,
        isPortrait: Bool,
        showSeconds: Bool = false,
        showAmPm: Bool = false
    ) -> CGFloat {
        // Use reasonable safe areas
        let safeInset = AppConstants.Defaults.safeInset
        let availableWidth = max(1, containerWidth - safeInset * 2)
        let availableHeight = max(1, containerHeight - safeInset * 2)
        
        // Calculate optimal size with reasonable space usage
        let optimalSize: CGFloat
        if isPortrait {
            // In portrait, use most of the available height
            let contentHeight = availableHeight * 0.85 // Use 85% of available height
            let estimatedLines = showSeconds ? 3 : 2
            let lineHeight = contentHeight / CGFloat(estimatedLines)
            optimalSize = lineHeight * 0.8 // Use 80% of line height
        } else {
            // In landscape, use most of the available width
            let contentWidth = availableWidth * 0.85 // Use 85% of available width
            let estimatedColumns = showSeconds ? 3 : 2
            let columnWidth = contentWidth / CGFloat(estimatedColumns)
            optimalSize = columnWidth * 0.7 // Use 70% of column width
        }
        
        // Apply reasonable bounds
        let minSize: CGFloat = 20
        let maxSize: CGFloat = AppConstants.Defaults.maxFontSize
        return max(minSize, min(optimalSize, maxSize))
    }
}
