import UIKit
import SwiftUI


enum TextStyle: String, CaseIterable {

    case largeTitle
    case title1
    case title2
    case title3
    case headline
    case subheadline
    case body
    case bodyBold
    case callout
    case footnote
    case segmentedControl
    case count
    case caption1
    case caption2
    
    case topic
    case tabIcon
    case tabLabel
    case smallIcon
    
    var shouldScale: Bool {
        if [.tabLabel, .tabIcon, .largeTitle, .topic, .count].contains(self) {
            return false
        }
        
        return true
    }

    var size: CGFloat {
        let originalSize: CGFloat
        switch self {
        case .largeTitle:
            originalSize = 34

        case .title1:
            originalSize = 24
            
        case .title2:
            originalSize = 22
            
        case .title3:
            originalSize = 20
        
        case .tabIcon:
            originalSize = 18

        case .headline, .body, .bodyBold:
            originalSize = 16
        
        case .callout, .smallIcon:
            originalSize = 15
            
        case .subheadline:
            originalSize = 14
            
        case .footnote, .segmentedControl:
            originalSize = 13
            
        case .caption1, .tabLabel:
            originalSize = 12
            
        case .caption2, .count:
            originalSize = 11
            
        case .topic:
            originalSize = 9
        }
        
        return self.shouldScale ? UIFontMetrics.default.scaledValue(for: originalSize) : originalSize
    }

    var uiColor: UIColor {
        switch self {
        case .caption1, .caption2:
            return ColorName.text.uiColorForVariation(.tertiary)
        
        case .subheadline, .smallIcon:
            return ColorName.text.uiColorForVariation(.secondary)

        case .count:
            return ColorName.textOnTint.uiColor
            
        default:
            return ColorName.text.uiColorForVariation(.primary)
        }
    }

    var color: Color {
        Color(self.uiColor)
    }

    var uiFontWeight: UIFont.Weight {
        let isBoldEnabled = UIAccessibility.isBoldTextEnabled

        switch self {
        case .headline, .title2, .title3, .bodyBold, .segmentedControl:
            return isBoldEnabled ? .semibold : .medium
            
        case .topic, .count, .title1:
            return isBoldEnabled ? .bold : .semibold

        default:
            return isBoldEnabled ? .semibold : .regular
        }
    }

    var isSerif: Bool {
        return false
    }

    var isRounded: Bool {
        return false
    }

    var fontName: String {
        switch self.uiFontWeight {
        case .bold, .heavy, .black:
            return "Poppins-Bold"
        case .semibold:
            return "Poppins-SemiBold"
        case .medium:
            return "Poppins-Medium"
        default:
            return "Poppins-Regular"
        }
    }
    
    var uiFont: UIFont {
        if let font = UIFont(name: fontName, size: self.size) {
            return font
        }
        
        let descriptor = UIFont.systemFont(ofSize: self.size, weight: uiFontWeight).fontDescriptor

        if isSerif, let serif = descriptor.withDesign(.serif) {
            return UIFont(descriptor: serif, size: 0.0)
        }
        
        if isRounded, let rounded = descriptor.withDesign(.rounded) {
            return UIFont(descriptor: rounded, size: 0.0)
        }

        return UIFont(descriptor: descriptor, size: 0.0)
    }

    var font: Font {
        Font(uiFont)
    }

    var lineSpacing: CGFloat {
        switch self {
        case .caption2:
            return 1.3
        case .body, .bodyBold:
            return 1.25
        case .headline:
            return 1.0
        case .title3:
            return 1
        default:
            return 1
        }
    }
}

struct ApplyTextStyle: ViewModifier {
    @Environment(\.sizeCategory) var sizeCategory
    var style: TextStyle
    var color: Color?

    var lineSpacing: CGFloat {
        return (style.lineSpacing * style.size) - style.size
    }

    func body(content: Content) -> some View {
        content
            .font(style.font)
            .foregroundColor(color ?? style.color)
            .lineSpacing(lineSpacing)
    }
}

extension View {
    func textStyle(_ style: TextStyle, color: Color? = nil) -> ModifiedContent<Self, ApplyTextStyle> {
        return modifier(ApplyTextStyle(style: style, color: color))
    }
}

extension String {
    func applyTextStyle(_ style: TextStyle) -> NSAttributedString {

        let attributedText = NSMutableAttributedString(string: self)
        let paraStyle = NSMutableParagraphStyle()
        paraStyle.paragraphSpacing = style.uiFont.pointSize
        paraStyle.lineHeightMultiple = 1 - style.lineSpacing

        attributedText.addAttributes([
            .paragraphStyle: paraStyle,
            .font: style.uiFont,
            .foregroundColor: style.uiColor
        ], range: NSRange(location: 0, length: attributedText.length))

        return attributedText
    }
}
