//
//  ToastCenter.swift
//  suixinkan
//
//  Created by Codex on 2026/6/18.
//

import Observation
import SwiftUI

@MainActor
@Observable
/// 全局 Toast 状态中心，负责保存和清除当前提示文案。
final class ToastCenter {
    fileprivate var message: String?
    @ObservationIgnored private let autoDismissNanoseconds: UInt64
    @ObservationIgnored private var dismissTask: Task<Void, Never>?
    @ObservationIgnored private var displayToken: UInt64 = 0

    /// 创建 Toast 状态中心，默认 2.2 秒后自动隐藏。
    init(autoDismissNanoseconds: UInt64 = 2_200_000_000) {
        self.autoDismissNanoseconds = autoDismissNanoseconds
    }

    /// 显示一条全局 Toast 文案。
    func show(_ message: String) {
        displayToken &+= 1
        self.message = message
        scheduleAutoDismiss(token: displayToken)
    }

    /// 清除当前 Toast 文案。
    func dismiss() {
        displayToken &+= 1
        dismissTask?.cancel()
        dismissTask = nil
        message = nil
    }

    /// 为当前 Toast 安排自动隐藏任务，并避免旧任务误清除新 Toast。
    private func scheduleAutoDismiss(token: UInt64) {
        dismissTask?.cancel()
        let delay = autoDismissNanoseconds
        dismissTask = Task { [weak self] in
            do {
                try await Task.sleep(nanoseconds: delay)
            } catch {
                return
            }
            await MainActor.run {
                guard let self, self.displayToken == token else { return }
                self.message = nil
                self.dismissTask = nil
            }
        }
    }

    #if DEBUG
    /// 测试专用快照，业务代码不应读取 Toast 展示状态。
    var snapshotForTests: ToastSnapshot {
        ToastSnapshot(message: message)
    }
    #endif
}

/// 全局 Toast 叠加层修饰器，负责把 Toast 展示到页面顶部。
private struct GlobalToastOverlay: ViewModifier {
    @Environment(ToastCenter.self) private var toastCenter

    func body(content: Content) -> some View {
        content
            .overlay(alignment: .top) {
                GeometryReader { proxy in
                    if let message = toastCenter.message {
                        toastBanner(message, topInset: proxy.safeAreaInsets.top)
                            .transition(.move(edge: .top).combined(with: .opacity))
                            .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top)
                    }
                }
                .allowsHitTesting(false)
            }
            .animation(.easeInOut(duration: 0.18), value: toastCenter.message)
    }

    /// 构建顶部全宽 Toast 横幅。
    private func toastBanner(_ message: String, topInset: CGFloat) -> some View {
        HStack {
            Text(message)
                .font(.system(size: 14, weight: .medium))
                .lineLimit(2)
                .multilineTextAlignment(.center)
                .fixedSize(horizontal: false, vertical: true)
                .frame(maxWidth: .infinity, alignment: .center)
        }
        .foregroundStyle(.white)
        .padding(.horizontal, AppMetrics.Spacing.pageHorizontal)
        .padding(.top, topInset + AppMetrics.Spacing.small)
        .padding(.bottom, AppMetrics.Spacing.small)
        .frame(maxWidth: .infinity, alignment: .center)
        .background(AppDesign.primary.ignoresSafeArea(edges: .top))
    }
}

#if DEBUG
/// 全局 Toast 测试快照，用于验证文案和自动隐藏行为。
struct ToastSnapshot: Equatable {
    let message: String?
}
#endif

extension View {
    /// 给任意根视图追加全局 Toast 展示能力。
    func globalToastOverlay() -> some View {
        modifier(GlobalToastOverlay())
    }
}
