import SwiftUI

/// The label style for the transient in-app notification banners.
///
/// The style renders the label's icon next to its title, over a padded, capsule Liquid Glass background. The icon is tinted with a color
/// matching the kind of notification given at initialization — green for an informational one, yellow for a warning, and red for an
/// error — and the title wears a medium-weight callout font.
public struct NotificationLabelStyle: LabelStyle {

    // MARK: Properties

    /// The kind of notification whose matching color tints the label's icon.
    private let kind: AppNotification.Kind

    // MARK: Initializers

    /// Creates a notification label style for a given kind of notification.
    ///
    /// - Parameter kind: The kind of notification whose matching color tints the label's icon.
    public init(
        kind: AppNotification.Kind
    ) {
        self.kind = kind
    }

    // MARK: Methods

    /// Creates the styled body of the label.
    ///
    /// - Parameter configuration: The properties of the label being styled.
    public func makeBody(
        configuration: Configuration
    ) -> some View {
        HStack(
            spacing: Constant.Spacing.stack
        ) {
            configuration.icon
                .font(.title3)
                .foregroundStyle(kind.imageStyle)

            configuration.title
                .font(.callout)
                .fontWeight(.medium)
                .foregroundStyle(.primary)
        }
        .padding(.vertical, Constant.Padding.vertical)
        .padding(.horizontal, Constant.Padding.horizontal)
        .glassEffect(
            .regular,
            in: .capsule
        )
    }

}

// MARK: - Styles

public extension LabelStyle where Self == NotificationLabelStyle {

    /// The label style for the transient in-app notification banners.
    ///
    /// - Parameter kind: The kind of notification whose matching color tints the label's icon.
    /// - Returns: A notification label style for the given kind of notification.
    static func notification(
        kind: AppNotification.Kind
    ) -> NotificationLabelStyle {
        .init(kind: kind)
    }

}

// MARK: - AppNotification.Kind+Properties

private extension AppNotification.Kind {

    /// The color that tints the icon of a notification label: green for an informational notification, yellow for a warning, and red
    /// for an error.
    var imageStyle: Color {
        switch self {
        case .error: .red
        case .info: .green
        case .warning: .yellow
        }
    }

}

// MARK: - Constants

/// The constant values used across the label style.
private enum Constant {
    /// The padding constants.
    enum Padding {
        /// The horizontal padding between a label's content and its background.
        static let horizontal: CGFloat = 16
        /// The vertical padding between a label's content and its background.
        static let vertical: CGFloat = 8
    }

    /// The spacing constants.
    enum Spacing {
        /// The spacing between the elements of a stack.
        static let stack: CGFloat = 16
    }
}

// MARK: - Previews

#Preview(
    "Notification label style"
) {
    VStack(
        alignment: .leading,
        spacing: 16
    ) {
        Label {
            Text(verbatim: "Downloading the speech model for Dutch (Netherlands)…")
        } icon: {
            Image(systemName: "arrow.down.circle")
        }
        .labelStyle(.notification(
            kind: .info
        ))

        Label {
            Text(verbatim: "The speech model download for Dutch (Netherlands) was cancelled.")
        } icon: {
            Image(systemName: "xmark.circle")
        }
        .labelStyle(.notification(
            kind: .warning
        ))

        Label {
            Text(verbatim: "The speech model for German (Germany) could not be downloaded.")
        } icon: {
            Image(systemName: "exclamationmark.triangle")
        }
        .labelStyle(.notification(
            kind: .error
        ))
    }
}
