//
//  GlobalLoadingCenter.swift
//  suixinkan
//
//  Created by Codex on 2026/6/23.
//

import Combine
import Foundation
import Lottie
import SwiftUI

@MainActor
/// 全局 Loading 命令中心，只负责展示状态控制，不承载业务数据。
final class GlobalLoadingCenter {
    fileprivate let state = GlobalLoadingState()

    /// 显示全局 Loading；多次调用会通过引用计数叠加。
    func show(message: String = "") {
        if !message.isEmpty {
            state.message = message
        }
        state.activeCount += 1
        state.isVisible = true
    }

    /// 隐藏一次全局 Loading；所有调用方都结束后才真正关闭。
    func hide() {
        state.activeCount = max(0, state.activeCount - 1)
        guard state.activeCount == 0 else { return }
        state.isVisible = false
        state.message = ""
    }

    /// 更新当前 Loading 文案，仅在 Loading 可见且文案非空时生效。
    func updateMessage(_ message: String) {
        guard state.isVisible, !message.isEmpty else { return }
        state.message = message
    }

    /// 包裹一个异步操作，操作结束或抛错时自动关闭 Loading。
    func withLoading<T>(
        message: String = "",
        operation: () async throws -> T
    ) async rethrows -> T {
        show(message: message)
        defer { hide() }
        return try await operation()
    }

    /// 根据开关决定是否展示 Loading，适合下拉刷新等可选 loading 场景。
    func withOptionalLoading<T>(
        _ enabled: Bool,
        message: String = "",
        operation: () async throws -> T
    ) async rethrows -> T {
        if enabled {
            return try await withLoading(message: message, operation: operation)
        }
        return try await operation()
    }

    #if DEBUG
    /// 测试专用快照，业务代码不应读取 Loading 展示状态。
    var snapshotForTests: GlobalLoadingSnapshot {
        GlobalLoadingSnapshot(
            isVisible: state.isVisible,
            message: state.message,
            activeCount: state.activeCount
        )
    }
    #endif
}

@MainActor
/// 全局 Loading 的私有可观察状态，只允许根部 Overlay 订阅。
fileprivate final class GlobalLoadingState: ObservableObject {
    @Published fileprivate var isVisible = false
    @Published fileprivate var message = ""
    fileprivate var activeCount = 0
}

/// Lottie Loading 动画容器，负责播放主包中的 loading.json。
struct LottieLoadingAnimationView: UIViewRepresentable {
    /// 判断当前包内是否存在可用 Loading 动画资源。
    static var hasAnimationResource: Bool {
        loadAnimation() != nil
    }

    /// 创建承载 Lottie 动画的 UIKit 视图。
    func makeUIView(context: Context) -> UIView {
        let container = UIView()
        container.backgroundColor = .clear

        let animationView = LottieAnimationView()
        animationView.translatesAutoresizingMaskIntoConstraints = false
        animationView.contentMode = .scaleAspectFit
        animationView.loopMode = .loop
        animationView.backgroundBehavior = .pauseAndRestore
        animationView.animation = Self.loadAnimation()
        animationView.play()

        container.addSubview(animationView)
        NSLayoutConstraint.activate([
            animationView.leadingAnchor.constraint(equalTo: container.leadingAnchor),
            animationView.trailingAnchor.constraint(equalTo: container.trailingAnchor),
            animationView.topAnchor.constraint(equalTo: container.topAnchor),
            animationView.bottomAnchor.constraint(equalTo: container.bottomAnchor)
        ])
        return container
    }

    /// Loading 动画无需响应 SwiftUI 状态更新。
    func updateUIView(_ uiView: UIView, context: Context) {}

    /// 从主包中加载 loading 动画资源，兼容 Resources 子目录和根目录。
    private static func loadAnimation() -> LottieAnimation? {
        if let animation = LottieAnimation.named("loading") {
            return animation
        }
        if let url = Bundle.main.url(forResource: "loading", withExtension: "json", subdirectory: "Resources") {
            return LottieAnimation.filepath(url.path)
        }
        if let url = Bundle.main.url(forResource: "loading", withExtension: "json") {
            return LottieAnimation.filepath(url.path)
        }
        return nil
    }
}

/// 全局 Loading Overlay 宿主，只在根部订阅 Loading 状态，避免业务页面重绘。
private struct GlobalLoadingOverlayHost: View {
    @ObservedObject private var state: GlobalLoadingState

    /// 使用全局 Loading 命令中心初始化 Overlay 宿主。
    init(loadingCenter: GlobalLoadingCenter) {
        _state = ObservedObject(wrappedValue: loadingCenter.state)
    }

    var body: some View {
        ZStack {
            if state.isVisible {
                ZStack {
                    Color.black.opacity(0.28)
                        .ignoresSafeArea()

                    VStack(spacing: 14) {
                        loadingAnimation
                    }
                    .padding(.horizontal, 10)
                    .padding(.vertical, 10)
                    .background(.white, in: RoundedRectangle(cornerRadius: 18, style: .continuous))
                    .shadow(color: Color.black.opacity(0.12), radius: 24, x: 0, y: 10)
                }
                .transition(.opacity)
                .zIndex(10_000)
                .accessibilityElement(children: .combine)
                .accessibilityLabel("加载中")
            }
        }
        .animation(.easeInOut(duration: 0.2), value: state.isVisible)
    }

    @ViewBuilder
    private var loadingAnimation: some View {
        if LottieLoadingAnimationView.hasAnimationResource {
            LottieLoadingAnimationView()
                .frame(width: 132, height: 132)
        } else {
            ProgressView()
                .controlSize(.large)
                .tint(AppDesign.primary)
                .frame(width: 132, height: 132)
        }
    }
}

private struct GlobalLoadingOverlayModifier: ViewModifier {
    let loadingCenter: GlobalLoadingCenter

    /// 在页面根部覆盖全局 Loading 展示层。
    func body(content: Content) -> some View {
        ZStack {
            content
            GlobalLoadingOverlayHost(loadingCenter: loadingCenter)
        }
    }
}

extension View {
    /// 挂载全局 Loading 展示层，通常只应在 RootView 调用一次。
    func globalLoadingOverlay(loadingCenter: GlobalLoadingCenter) -> some View {
        modifier(GlobalLoadingOverlayModifier(loadingCenter: loadingCenter))
    }
}

#if DEBUG
/// 全局 Loading 测试快照，用于验证引用计数和展示文案。
struct GlobalLoadingSnapshot: Equatable {
    let isVisible: Bool
    let message: String
    let activeCount: Int
}
#endif

private struct GlobalLoadingCenterKey: EnvironmentKey {
    static let defaultValue = GlobalLoadingCenter()
}

extension EnvironmentValues {
    /// 全局 Loading 命令中心，业务页面只能通过它发出 show/hide 指令。
    var globalLoading: GlobalLoadingCenter {
        get { self[GlobalLoadingCenterKey.self] }
        set { self[GlobalLoadingCenterKey.self] = newValue }
    }
}
