//
//  FloatingDeltaView.swift
//  lichunWebsocket
//
//  Transient floating "+5 energy" / "-3 money" indicators that rise and fade
//  near the header when a lightweight "u" update changes a stat or resource.
//  Makes gains and losses feel earned. (T014: feel polish layer)
//

import SwiftUI

// MARK: - Stat Delta Model

/// A single transient stat/resource change emitted by GameStateStore. Each one
/// is rendered once by FloatingDeltaOverlay, then auto-pruned from the store.
struct StatDelta: Identifiable, Equatable {
    enum Kind {
        case energy
        case money
        case diamonds
        case health
        case happiness

        var emoji: String {
            switch self {
            case .energy: return "\u{26A1}\u{FE0F}"   // bolt
            case .money: return "\u{1F4B0}"           // money bag
            case .diamonds: return "\u{1F48E}"        // gem
            case .health: return "\u{2764}\u{FE0F}"   // heart
            case .happiness: return "\u{1F60A}"       // smiling face
            }
        }

        var tint: Color {
            switch self {
            case .energy: return AppColors.energy
            case .money: return AppColors.money
            case .diamonds: return AppColors.diamond
            case .health: return AppColors.health
            case .happiness: return AppColors.happiness
            }
        }
    }

    let id = UUID()
    let kind: Kind
    let amount: Int

    /// "+5", "-3", etc. Always sign-prefixed so direction reads at a glance.
    var label: String {
        amount > 0 ? "+\(amount)" : "\(amount)"
    }

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

// MARK: - Single Floating Delta Chip

/// One rising chip. Animates upward and fades on appear; the parent removes it
/// from the data source after its lifetime (~1.6s).
struct FloatingDeltaView: View {
    let delta: StatDelta

    @State private var rise: CGFloat = 0
    @State private var opacity: Double = 0
    @State private var scale: CGFloat = 0.7

    var body: some View {
        HStack(spacing: 4) {
            Text(delta.kind.emoji)
                .font(.system(size: 13))
            Text(delta.label)
                .font(.system(size: 14, weight: .bold, design: .rounded))
                .foregroundColor(delta.amount > 0 ? delta.kind.tint : AppColors.error)
        }
        .padding(.horizontal, AppSpacing.sm)
        .padding(.vertical, AppSpacing.xxs)
        .background(
            Capsule()
                .fill(AppColors.cardBackground.opacity(0.92))
                .overlay(
                    Capsule()
                        .stroke(delta.kind.tint.opacity(0.35), lineWidth: 1)
                )
        )
        .shadow(color: delta.kind.tint.opacity(0.25), radius: 6, x: 0, y: 2)
        .scaleEffect(scale)
        .opacity(opacity)
        .offset(y: rise)
        .onAppear {
            // Pop in.
            withAnimation(.spring(response: 0.3, dampingFraction: 0.55)) {
                scale = 1.0
                opacity = 1.0
            }
            // Rise and fade out.
            withAnimation(.easeOut(duration: 1.4)) {
                rise = -36
            }
            withAnimation(.easeIn(duration: 0.5).delay(0.9)) {
                opacity = 0
            }
        }
        .allowsHitTesting(false)
        .accessibilityHidden(true)
    }
}

// MARK: - Floating Delta Overlay

/// Renders all currently-active deltas as a fanned stack just under the header.
/// Observes the GameStateStore via the WebSocketService facade.
struct FloatingDeltaOverlay: View {
    @ObservedObject var gameState: GameStateStore

    var body: some View {
        VStack {
            HStack(spacing: AppSpacing.xs) {
                Spacer()
                ForEach(gameState.statDeltas) { delta in
                    FloatingDeltaView(delta: delta)
                        .transition(.scale.combined(with: .opacity))
                }
            }
            .padding(.trailing, AppSpacing.md)
            .padding(.top, AppSpacing.xs)
            .animation(.spring(response: 0.35, dampingFraction: 0.7), value: gameState.statDeltas.count)

            Spacer()
        }
        .allowsHitTesting(false)
    }
}
