import SwiftUI
import Combine

// MARK: - Toast type

enum ZXToastType {
    case success, error, warning, info

    var icon: String {
        switch self {
        case .success: return "checkmark.circle.fill"
        case .error:   return "xmark.circle.fill"
        case .warning: return "exclamationmark.triangle.fill"
        case .info:    return "info.circle.fill"
        }
    }

    var color: Color {
        switch self {
        case .success: return Color.zxGreen
        case .error:   return Color.zxRed
        case .warning: return Color.zxOrange
        case .info:    return Color.zxPurple
        }
    }
}

// MARK: - Global toast manager

@MainActor
final class ZXToastManager: ObservableObject {
    static let shared = ZXToastManager()

    @Published var current: ZXToastItem?

    private var queue: [ZXToastItem] = []
    private var hideTask: Task<Void, Never>?

    private init() {}

    func show(_ message: String, type: ZXToastType = .info, duration: TimeInterval = 2.5) {
        let item = ZXToastItem(message: message, type: type)
        if current != nil {
            queue.append(item)
            current = nil
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.35) { [weak self] in
                self?.showNext()
            }
        } else {
            withAnimation(.spring(response: 0.4, dampingFraction: 0.7)) {
                current = item
            }
            scheduleHide(duration)
        }
    }

    func success(_ message: String) { show(message, type: .success) }
    func error(_ message: String)   { show(message, type: .error) }
    func warning(_ message: String) { show(message, type: .warning) }
    func info(_ message: String)    { show(message, type: .info) }

    private func showNext() {
        guard !queue.isEmpty else { return }
        let next = queue.removeFirst()
        withAnimation(.spring(response: 0.4, dampingFraction: 0.7)) {
            current = next
        }
        scheduleHide(next.duration)
    }

    private func scheduleHide(_ duration: TimeInterval) {
        hideTask?.cancel()
        hideTask = Task {
            try? await Task.sleep(nanoseconds: UInt64(duration * 1_000_000_000))
            guard !Task.isCancelled else { return }
            await MainActor.run {
                withAnimation(.easeOut(duration: 0.25)) {
                    current = nil
                }
            }
            try? await Task.sleep(nanoseconds: 350_000_000)
            showNext()
        }
    }
}

struct ZXToastItem: Equatable {
    let id = UUID()
    let message: String
    let type: ZXToastType
    let duration: TimeInterval

    init(message: String, type: ZXToastType, duration: TimeInterval = 2.5) {
        self.message = message
        self.type = type
        self.duration = duration
    }

    static func == (lhs: ZXToastItem, rhs: ZXToastItem) -> Bool { lhs.id == rhs.id }
}

// MARK: - Toast overlay modifier

struct ZXToastOverlay: ViewModifier {
    @ObservedObject private var manager = ZXToastManager.shared

    func body(content: Content) -> some View {
        content.overlay(alignment: .top) {
            if let item = manager.current {
                ZXToastBar(item: item)
                    .padding(.horizontal, 20)
                    .padding(.top, ZXSpacing.statusBarH + 8)
                    .transition(.move(edge: .top).combined(with: .opacity))
            }
        }
    }
}

extension View {
    func zxToast() -> some View {
        modifier(ZXToastOverlay())
    }
}

// MARK: - Toast bar view

struct ZXToastBar: View {
    let item: ZXToastItem

    var body: some View {
        HStack(spacing: 10) {
            Image(systemName: item.type.icon)
                .font(.system(size: 16))
                .foregroundColor(item.type.color)
            Text(item.message)
                .font(.system(size: 14, weight: .semibold))
                .foregroundColor(Color.zxF0)
                .lineLimit(2)
            Spacer(minLength: 0)
        }
        .padding(.horizontal, 16)
        .padding(.vertical, 14)
        .background(
            RoundedRectangle(cornerRadius: 16)
                .fill(.ultraThinMaterial)
                .overlay(
                    RoundedRectangle(cornerRadius: 16)
                        .stroke(item.type.color.opacity(0.2), lineWidth: 1)
                )
        )
        .shadow(color: Color.black.opacity(0.25), radius: 12, y: 4)
    }
}
