import SwiftUI

// MARK: - Async Content View

/// A reusable view for handling async content states (loading, success, error)
struct AsyncContentView<T, Content: View, Loading: View, Error: View>: View {
    let state: ViewState<T>
    let content: (T) -> Content
    let loading: () -> Loading
    let error: (String, @escaping () -> Void) -> Error
    let onRetry: () -> Void

    init(
        state: ViewState<T>,
        @ViewBuilder content: @escaping (T) -> Content,
        @ViewBuilder loading: @escaping () -> Loading,
        @ViewBuilder error: @escaping (String, @escaping () -> Void) -> Error,
        onRetry: @escaping () -> Void
    ) {
        self.state = state
        self.content = content
        self.loading = loading
        self.error = error
        self.onRetry = onRetry
    }

    var body: some View {
        switch state {
        case .idle:
            EmptyView()
        case .loading:
            loading()
        case .loaded(let data):
            content(data)
        case .error(let message):
            error(message, onRetry)
        }
    }
}

// MARK: - Convenience Initializers

extension AsyncContentView where Loading == DefaultLoadingView {
    /// Initialize with default loading view
    init(
        state: ViewState<T>,
        @ViewBuilder content: @escaping (T) -> Content,
        @ViewBuilder error: @escaping (String, @escaping () -> Void) -> Error,
        onRetry: @escaping () -> Void
    ) {
        self.state = state
        self.content = content
        self.loading = { DefaultLoadingView() }
        self.error = error
        self.onRetry = onRetry
    }
}

extension AsyncContentView where Error == DefaultErrorView {
    /// Initialize with default error view
    init(
        state: ViewState<T>,
        @ViewBuilder content: @escaping (T) -> Content,
        @ViewBuilder loading: @escaping () -> Loading,
        onRetry: @escaping () -> Void
    ) {
        self.state = state
        self.content = content
        self.loading = loading
        self.error = { message, retry in DefaultErrorView(message: message, onRetry: retry) }
        self.onRetry = onRetry
    }
}

extension AsyncContentView where Loading == DefaultLoadingView, Error == DefaultErrorView {
    /// Initialize with default loading and error views
    init(
        state: ViewState<T>,
        @ViewBuilder content: @escaping (T) -> Content,
        onRetry: @escaping () -> Void
    ) {
        self.state = state
        self.content = content
        self.loading = { DefaultLoadingView() }
        self.error = { message, retry in DefaultErrorView(message: message, onRetry: retry) }
        self.onRetry = onRetry
    }
}

// MARK: - Default Loading View

struct DefaultLoadingView: View {
    var body: some View {
        VStack(spacing: 12) {
            ProgressView()
                .progressViewStyle(CircularProgressViewStyle())
                .scaleEffect(1.2)

            Text("Loading...")
                .font(.subheadline)
                .foregroundColor(Color.appTextSecondary)
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
    }
}

// MARK: - Default Error View

struct DefaultErrorView: View {
    let message: String
    let onRetry: () -> Void

    var body: some View {
        VStack(spacing: 16) {
            Image(systemName: "exclamationmark.triangle")
                .font(.system(size: 48))
                .foregroundColor(Color.appError)

            Text("Something went wrong")
                .font(.headline)
                .foregroundColor(Color.appTextPrimary)

            Text(message)
                .font(.subheadline)
                .foregroundColor(Color.appTextSecondary)
                .multilineTextAlignment(.center)
                .padding(.horizontal)

            Button(action: onRetry) {
                Label("Try Again", systemImage: "arrow.clockwise")
            }
            .buttonStyle(.bordered)
            .tint(Color.appPrimary)
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
        .padding()
    }
}

// MARK: - Async Empty State View

struct AsyncEmptyStateView: View {
    let icon: String
    let title: String
    let subtitle: String?
    let actionLabel: String?
    let action: (() -> Void)?

    init(
        icon: String,
        title: String,
        subtitle: String? = nil,
        actionLabel: String? = nil,
        action: (() -> Void)? = nil
    ) {
        self.icon = icon
        self.title = title
        self.subtitle = subtitle
        self.actionLabel = actionLabel
        self.action = action
    }

    var body: some View {
        VStack(spacing: 16) {
            Image(systemName: icon)
                .font(.system(size: 60))
                .foregroundColor(Color.appTextSecondary.opacity(0.5))

            Text(title)
                .font(.headline)
                .foregroundColor(Color.appTextPrimary)

            if let subtitle = subtitle {
                Text(subtitle)
                    .font(.subheadline)
                    .foregroundColor(Color.appTextSecondary)
                    .multilineTextAlignment(.center)
            }

            if let actionLabel = actionLabel, let action = action {
                Button(action: action) {
                    Text(actionLabel)
                }
                .buttonStyle(.borderedProminent)
                .tint(Color.appPrimary)
            }
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
        .padding()
    }
}

// MARK: - List Async Content View

/// Specialized async content view for lists with pull-to-refresh support
struct ListAsyncContentView<T, Content: View, EmptyContent: View>: View {
    let items: [T]
    let isLoading: Bool
    let errorMessage: String?
    let content: ([T]) -> Content
    let emptyContent: () -> EmptyContent
    let onRefresh: () async -> Void
    let onRetry: () -> Void

    init(
        items: [T],
        isLoading: Bool,
        errorMessage: String?,
        @ViewBuilder content: @escaping ([T]) -> Content,
        @ViewBuilder emptyContent: @escaping () -> EmptyContent,
        onRefresh: @escaping () async -> Void,
        onRetry: @escaping () -> Void
    ) {
        self.items = items
        self.isLoading = isLoading
        self.errorMessage = errorMessage
        self.content = content
        self.emptyContent = emptyContent
        self.onRefresh = onRefresh
        self.onRetry = onRetry
    }

    var body: some View {
        Group {
            if let errorMessage = errorMessage, items.isEmpty {
                // Wrap in ScrollView for pull-to-refresh support
                GeometryReader { geometry in
                    ScrollView {
                        DefaultErrorView(message: errorMessage, onRetry: onRetry)
                            .frame(maxWidth: .infinity, minHeight: geometry.size.height * 0.6)
                    }
                }
            } else if items.isEmpty && !isLoading {
                // Wrap in ScrollView for pull-to-refresh support
                GeometryReader { geometry in
                    ScrollView {
                        emptyContent()
                            .frame(maxWidth: .infinity, minHeight: geometry.size.height * 0.6)
                    }
                }
            } else {
                content(items)
            }
        }
        .overlay {
            if isLoading && items.isEmpty {
                DefaultLoadingView()
            }
        }
        .refreshable {
            await onRefresh()
        }
    }
}

// MARK: - Preview

#if DEBUG
struct AsyncContentView_Previews: PreviewProvider {
    static var previews: some View {
        Group {
            AsyncContentView(
                state: ViewState<String>.loading,
                content: { data in Text(data) },
                onRetry: {}
            )
            .previewDisplayName("Loading")

            AsyncContentView(
                state: ViewState<String>.loaded("Hello, World!"),
                content: { data in Text(data) },
                onRetry: {}
            )
            .previewDisplayName("Loaded")

            AsyncContentView(
                state: ViewState<String>.error("Network connection failed"),
                content: { data in Text(data) },
                onRetry: {}
            )
            .previewDisplayName("Error")

            AsyncEmptyStateView(
                icon: "tray",
                title: "No Items",
                subtitle: "Add your first item to get started",
                actionLabel: "Add Item",
                action: {}
            )
            .previewDisplayName("Empty State")
        }
    }
}
#endif
