//
//  HeaderView.swift
//  lichunWebsocket
//
//  The "Collapsing Hero" header (design direction E, chosen 2026-05-29).
//
//  • Full seasonal hero scene when Home is scrolled to the top: a code-rendered
//    sky + sun + drifting seasonal motif + hills, the big clock, the character
//    name/mood, and a frosted control tray (play/pause + speed stepper + stats).
//  • Shrinks continuously to a compact bar as the Home feed scrolls (driven by
//    appViewModel.homeScrollOffset). On non-Home tabs it is always compact.
//  • Play/pause uses the real start/stop loop commands; pause state is tracked
//    in AppViewModel.isGamePaused (the server exposes no paused flag).
//
//  Seasonal theming switches the whole scene by player.season.
//

import SwiftUI

struct HeaderView: View {
    @EnvironmentObject var webSocketService: WebSocketService
    @EnvironmentObject var appViewModel: AppViewModel

    // Brief scale-pop on resource gain/loss so a change reads as meaningful.
    @State private var energyPulse: CGFloat = 1.0
    @State private var moneyPulse: CGFloat = 1.0

    // MARK: - Layout constants
    private let heroHeight: CGFloat = 196
    private let compactHeight: CGFloat = 82
    private let collapseDistance: CGFloat = 120

    // MARK: - Collapse math

    /// 0 = full hero, 1 = fully compact. Driven by Home scroll; forced to 1 on
    /// every non-Home tab so the hero only lives on the Home feed.
    private var fraction: CGFloat {
        guard appViewModel.selectedTab == 0 else { return 1 }
        let scrolled = max(0, -appViewModel.homeScrollOffset)
        return min(1, scrolled / collapseDistance)
    }
    private var headerHeight: CGFloat { compactHeight + (heroHeight - compactHeight) * (1 - fraction) }
    private var heroOpacity: Double { Double(max(0, min(1, 1 - fraction * 1.8))) }
    private var compactOpacity: Double { Double(max(0, min(1, (fraction - 0.45) * 2.4))) }
    private var detailOpacity: Double { Double(max(0, min(1, 1 - fraction))) }

    // MARK: - Derived display values

    private var style: SeasonVisual { SeasonVisual.of(webSocketService.player.season) }
    private var energy: Int { webSocketService.person.calcEnergy }
    private var money: Int { webSocketService.person.money }
    private var diamonds: Int { webSocketService.person.diamonds }
    private var firstName: String {
        let f = webSocketService.person.firstname
        return f.isEmpty ? "—" : f
    }
    private var fullName: String {
        let last = webSocketService.person.lastname
        return last.isEmpty ? firstName : "\(firstName) \(last)"
    }
    private var moodEmoji: String { webSocketService.person.moodEmoji }
    private var age: Int { webSocketService.person.ageYears }
    private var lifeStage: String { webSocketService.person.lifeStage }
    private var speedName: String { Constants.GameSpeed.label(for: webSocketService.player.gameSpeed) }
    private var isPaused: Bool { appViewModel.isGamePaused }

    private var hasUnreadMessages: Bool {
        webSocketService.player.activeConversations.contains { $0.unread }
    }
    private var hasClaimableRewards: Bool {
        if let s = webSocketService.dailyRewardState, s.canClaim && !s.todaysClaimed { return true }
        if let s = webSocketService.dailyQuestsState, s.quests.contains(where: { $0.canClaim }) { return true }
        return false
    }

    private var timeString: String {
        let h = webSocketService.player.hourOfDay
        let m = webSocketService.player.minuteOfHour
        let h12 = h % 12 == 0 ? 12 : h % 12
        return "\(h12):" + String(format: "%02d", m) + (h < 12 ? "am" : "pm")
    }
    private var dateBits: (dow: String, monthDay: String) {
        let raw = webSocketService.player.date
        let parts = raw.split(separator: "-").map(String.init)
        let months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
        guard parts.count >= 2, let m = Int(parts[0]), m >= 1, m <= 12 else { return ("", "") }
        let monthDay = "\(months[m - 1]) \(parts[1])"
        var dow = ""
        let df = DateFormatter()
        df.dateFormat = "MM-dd"
        if let d = df.date(from: raw) { df.dateFormat = "EEE"; dow = df.string(from: d) }
        return (dow, monthDay)
    }
    private var seasonName: String {
        let s = webSocketService.player.season
        return s.isEmpty ? "" : s.prefix(1).uppercased() + s.dropFirst().lowercased()
    }
    private var seasonLine: String {
        let b = dateBits
        let season = seasonName.isEmpty ? "" : "\(seasonName) · "
        let dow = b.dow.isEmpty ? "" : "\(b.dow) "
        return "\(style.emoji) \(season)\(dow)\(b.monthDay)"
    }

    // MARK: - Body

    var body: some View {
        ZStack(alignment: .top) {
            // Background scene bleeds up behind the Dynamic Island / notch.
            SeasonScene(season: webSocketService.player.season, detailOpacity: detailOpacity)
                .ignoresSafeArea(edges: .top)

            // Crossfading content, clipped to the (shrinking) header height.
            ZStack(alignment: .top) {
                heroContent
                    .frame(height: heroHeight, alignment: .top)
                    .opacity(heroOpacity)
                    .allowsHitTesting(heroOpacity > 0.5)
                compactContent
                    .frame(height: compactHeight, alignment: .top)
                    .opacity(compactOpacity)
                    .allowsHitTesting(compactOpacity > 0.5)
            }
            .frame(height: headerHeight, alignment: .top)
            .clipped()
        }
        .frame(height: headerHeight, alignment: .top)
        .overlay(alignment: .bottom) {
            Divider().opacity(0.25 + 0.35 * (1 - detailOpacity))
        }
        .animation(.easeOut(duration: 0.28), value: appViewModel.selectedTab)
        .animation(.spring(response: 0.4, dampingFraction: 0.8), value: energy)
        .animation(.spring(response: 0.4, dampingFraction: 0.8), value: money)
        .onChange(of: energy) { _ in pulse($energyPulse) }
        .onChange(of: money) { _ in pulse($moneyPulse) }
    }

    // MARK: - Hero content

    private var heroContent: some View {
        VStack(spacing: 0) {
            HStack(alignment: .top) {
                VStack(alignment: .leading, spacing: 1) {
                    Text(timeString)
                        .font(.system(size: 30, weight: .heavy, design: .rounded))
                        .foregroundColor(Color(hex: 0x4A5A6A))
                    Text(seasonLine)
                        .font(.system(size: 12, weight: .bold, design: .rounded))
                        .foregroundColor(Color(hex: 0x5A6A78))
                }
                Spacer()
                navCluster()
            }
            Spacer(minLength: 4)
            HStack(alignment: .bottom) {
                VStack(alignment: .leading, spacing: 1) {
                    Text("\(fullName) \(moodEmoji)")
                        .font(.system(size: 20, weight: .heavy, design: .rounded))
                        .foregroundColor(Color(hex: 0x4A3A2A))
                        .lineLimit(1)
                        .minimumScaleFactor(0.7)
                    Text("Age \(age) · \(lifeStage)")
                        .font(.system(size: 12, weight: .bold, design: .rounded))
                        .foregroundColor(Color(hex: 0x6A5A48))
                }
                Spacer()
            }
            .padding(.bottom, 8)
            controlTray(compact: false)
        }
        .padding(.horizontal, 16)
        .padding(.top, 6)
        .padding(.bottom, 12)
    }

    // MARK: - Compact content

    private var compactContent: some View {
        VStack(spacing: 7) {
            HStack(spacing: 7) {
                moodAvatar(size: 26)
                Text("\(firstName) · \(age)")
                    .font(.system(size: 13, weight: .bold, design: .rounded))
                    .foregroundColor(AppColors.primaryText)
                    .lineLimit(1)
                Spacer(minLength: 4)
                Text("\(timeString)  \(style.emoji)")
                    .font(.system(size: 13, weight: .bold, design: .rounded))
                    .foregroundColor(AppColors.primaryText)
                navCluster(size: 30)
            }
            controlTray(compact: true)
        }
        .padding(.horizontal, 14)
        .padding(.top, 10)
        .padding(.bottom, 10)
    }

    // MARK: - Control tray (stats + pause + speed)

    private func controlTray(compact: Bool) -> some View {
        HStack(spacing: 6) {
            Button { webSocketService.showEnergyRefillModal = true } label: {
                statPill("⚡️", "\(energy)", compact: compact, scale: energyPulse)
            }
            .buttonStyle(.plain)
            .accessibilityLabel("Energy \(energy)")
            .accessibilityHint("Opens energy refills")

            statPill("💰", moneyShort(money), compact: compact, scale: moneyPulse)
            statPill("💎", "\(diamonds)", compact: compact)

            Spacer(minLength: 2)

            pauseButton(size: compact ? 28 : 32)
            speedStepper(compact: compact)
        }
        .padding(compact ? 0 : 8)
        .background {
            if !compact {
                RoundedRectangle(cornerRadius: 15).fill(.ultraThinMaterial)
            }
        }
    }

    // MARK: - Pieces

    private func statPill(_ emoji: String, _ value: String, compact: Bool, scale: CGFloat = 1) -> some View {
        HStack(spacing: 3) {
            Text(emoji).font(.system(size: compact ? 11 : 13))
            Text(value)
                .font(.system(size: compact ? 11 : 13, weight: .bold, design: .rounded))
                .foregroundColor(AppColors.primaryText)
                .lineLimit(1)
                .contentTransition(.numericText())
                .scaleEffect(scale)
        }
        .fixedSize(horizontal: true, vertical: false)
        .padding(.horizontal, compact ? 6 : 9)
        .padding(.vertical, compact ? 3 : 5)
        .background(Color.white.opacity(0.7))
        .clipShape(Capsule())
    }

    private func pauseButton(size: CGFloat) -> some View {
        Button {
            togglePause()
        } label: {
            ZStack {
                Circle().fill(AppColors.primary)
                    .shadow(color: AppColors.primary.opacity(0.5), radius: size * 0.18, y: 2)
                Image(systemName: isPaused ? "play.fill" : "pause.fill")
                    .font(.system(size: size * 0.42, weight: .bold))
                    .foregroundColor(.white)
            }
            .frame(width: size, height: size)
        }
        .buttonStyle(SquishButtonStyle(pressHaptic: .medium))
        .accessibilityLabel(isPaused ? "Resume time" : "Pause time")
    }

    private func speedStepper(compact: Bool) -> some View {
        HStack(spacing: compact ? 5 : 6) {
            stepButton("minus", compact: compact) { changeSpeed("-") }
            Text(speedName)
                .font(.system(size: compact ? 11 : 12, weight: .bold, design: .rounded))
                .foregroundColor(AppColors.primaryText)
                .frame(minWidth: compact ? 42 : 46)
                .lineLimit(1)
                .contentTransition(.opacity)
            stepButton("plus", compact: compact) { changeSpeed("+") }
        }
        .animation(.easeInOut(duration: 0.2), value: speedName)
    }

    private func stepButton(_ icon: String, compact: Bool, _ action: @escaping () -> Void) -> some View {
        Button(action: action) {
            ZStack {
                RoundedRectangle(cornerRadius: compact ? 7 : 9).fill(Color.white.opacity(0.85))
                Image(systemName: icon)
                    .font(.system(size: compact ? 12 : 13, weight: .bold))
                    .foregroundColor(AppColors.primaryDark)
            }
            .frame(width: compact ? 24 : 30, height: compact ? 24 : 30)
        }
        .buttonStyle(SquishButtonStyle(pressHaptic: .light))
    }

    private func moodAvatar(size: CGFloat) -> some View {
        Circle().fill(Color.white)
            .frame(width: size, height: size)
            .overlay(Text(moodEmoji).font(.system(size: size * 0.55)))
            .shadow(color: .black.opacity(0.08), radius: 2, y: 1)
    }

    private func navCluster(size: CGFloat = 36) -> some View {
        HStack(spacing: 8) {
            navIcon("message.fill", badge: hasUnreadMessages, size: size) {
                appViewModel.showMessagesFromHeader = true
            }
            navIcon("person.fill", badge: hasClaimableRewards, size: size) {
                appViewModel.showProfileMenu = true
            }
        }
    }

    private func navIcon(_ symbol: String, badge: Bool, size: CGFloat, _ action: @escaping () -> Void) -> some View {
        Button(action: action) {
            ZStack(alignment: .topTrailing) {
                Circle().fill(Color.white.opacity(0.72))
                    .frame(width: size, height: size)
                    .overlay(
                        Image(systemName: symbol)
                            .font(.system(size: size * 0.42, weight: .semibold))
                            .foregroundColor(AppColors.primaryText)
                    )
                    .shadow(color: .black.opacity(0.1), radius: 4, y: 2)
                if badge {
                    Circle().fill(Color.red)
                        .frame(width: 10, height: 10)
                        .overlay(Circle().stroke(Color.white, lineWidth: 1.5))
                        .offset(x: 2, y: -2)
                }
            }
            .frame(width: size, height: size)
        }
        .buttonStyle(SquishButtonStyle(pressHaptic: .soft))
    }

    // MARK: - Actions

    private func togglePause() {
        if appViewModel.isGamePaused {
            webSocketService.sendMessage(message: WebSocketCommands.command("start"))
            appViewModel.isGamePaused = false
        } else {
            webSocketService.sendMessage(message: WebSocketCommands.command("stop"))
            appViewModel.isGamePaused = true
        }
    }

    private func changeSpeed(_ dir: String) {
        webSocketService.sendMessage(message: WebSocketCommands.speed(dir))
        // Changing speed implies the player wants time moving again.
        if appViewModel.isGamePaused {
            webSocketService.sendMessage(message: WebSocketCommands.command("start"))
            appViewModel.isGamePaused = false
        }
        hapticFeedback(style: .light)
    }

    private func moneyShort(_ amount: Int) -> String {
        if amount >= 10_000 { return String(format: "%.0fk", Double(amount) / 1000.0) }
        if amount >= 1_000 { return String(format: "%.1fk", Double(amount) / 1000.0) }
        return "\(amount)"
    }

    /// Quick pop-then-settle used by the resource numbers.
    private func pulse(_ binding: Binding<CGFloat>) {
        withAnimation(.spring(response: 0.25, dampingFraction: 0.45)) {
            binding.wrappedValue = 1.18
        }
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.22) {
            withAnimation(.spring(response: 0.4, dampingFraction: 0.7)) {
                binding.wrappedValue = 1.0
            }
        }
    }
}

// MARK: - Seasonal visual style

struct SeasonVisual {
    let sky: [Color]
    let sun: Color
    let hill1: Color
    let hill2: Color
    let emoji: String

    static func of(_ season: String) -> SeasonVisual {
        let sky: [Color]
        let sun: Color, hill1: Color, hill2: Color, emoji: String
        switch season.lowercased() {
        case "summer":
            sky = [Color(hex: 0xBFE8FF), Color(hex: 0xEAF7FF), Color(hex: 0xFFF6DD)]
            sun = Color(hex: 0xFFD54F); hill1 = Color(hex: 0x9DDFAA); hill2 = Color(hex: 0xB5C9F4); emoji = "☀️"
        case "autumn", "fall":
            sky = [Color(hex: 0xFFE3C2), Color(hex: 0xFFEAD6), Color(hex: 0xFFE0E6)]
            sun = Color(hex: 0xFFAB91); hill1 = Color(hex: 0xFFCC80); hill2 = Color(hex: 0xE8B98C); emoji = "🍁"
        case "winter":
            sky = [Color(hex: 0xDCEBFF), Color(hex: 0xF0F4FA), Color(hex: 0xEDE8F6)]
            sun = Color(hex: 0xCFE6FF); hill1 = Color(hex: 0xE3F0FF); hill2 = Color(hex: 0xD7CCEC); emoji = "❄️"
        default: // spring
            sky = [Color(hex: 0xBFE3FF), Color(hex: 0xE9F6FF), Color(hex: 0xFFE9F2)]
            sun = Color(hex: 0xFFE08A); hill1 = Color(hex: 0x9DDFAA); hill2 = Color(hex: 0xB5C9F4); emoji = "🌸"
        }
        return SeasonVisual(sky: sky, sun: sun, hill1: hill1, hill2: hill2, emoji: emoji)
    }
}

/// Code-rendered seasonal backdrop. `detailOpacity` fades the sun / motif / hills
/// out as the header collapses, leaving just the soft sky gradient for the bar.
private struct SeasonScene: View {
    let season: String
    var detailOpacity: Double = 1

    var body: some View {
        let s = SeasonVisual.of(season)
        GeometryReader { geo in
            ZStack {
                LinearGradient(colors: s.sky, startPoint: .top, endPoint: .bottom)

                Group {
                    Circle()
                        .fill(RadialGradient(colors: [.white, s.sun], center: .center, startRadius: 2, endRadius: 30))
                        .frame(width: 48, height: 48)
                        .shadow(color: s.sun.opacity(0.6), radius: 16)
                        .position(x: geo.size.width - 36, y: 30)

                    ForEach(Array(spots(geo.size).enumerated()), id: \.offset) { _, p in
                        Text(s.emoji)
                            .font(.system(size: p.size))
                            .opacity(p.opacity)
                            .position(x: p.x, y: p.y)
                    }

                    Ellipse().fill(s.hill1.opacity(0.55))
                        .frame(width: geo.size.width * 0.85, height: 130)
                        .position(x: geo.size.width * 0.18, y: geo.size.height + 14)
                    Ellipse().fill(s.hill2.opacity(0.45))
                        .frame(width: geo.size.width * 0.8, height: 120)
                        .position(x: geo.size.width * 0.85, y: geo.size.height + 22)
                }
                .opacity(detailOpacity)
            }
            .clipped()
        }
    }

    private func spots(_ size: CGSize) -> [(x: CGFloat, y: CGFloat, size: CGFloat, opacity: Double)] {
        [(size.width * 0.46, 70, 13, 0.7),
         (size.width * 0.62, 112, 11, 0.6),
         (size.width * 0.38, 134, 12, 0.6),
         (size.width * 0.80, 150, 10, 0.55)]
    }
}

// MARK: - Previews

#if DEBUG
private func headerPreviewService() -> WebSocketService {
    let ws = WebSocketService(urlSession: URLSession.shared, delegateQueue: OperationQueue())
    ws.person.firstname = "Emma"
    ws.person.lastname = "Chen"
    ws.person.ageYears = 24
    ws.person.mood = "content"
    ws.person.calcEnergy = 65
    ws.person.money = 1250
    ws.person.diamonds = 42
    ws.person.health = 85
    ws.player.date = "05-15"
    ws.player.hourOfDay = 15
    ws.player.minuteOfHour = 45
    ws.player.season = "Spring"
    ws.player.gameSpeed = Constants.GameSpeed.normal
    return ws
}

#Preview("Hero (Home top)") {
    let avm = AppViewModel()
    avm.selectedTab = 0
    avm.homeScrollOffset = 0
    return VStack(spacing: 0) {
        HeaderView()
            .environmentObject(headerPreviewService())
            .environmentObject(avm)
        Spacer()
    }
    .frame(width: 393)
    .background(AppColors.background)
}

#Preview("Collapsed (scrolled)") {
    let avm = AppViewModel()
    avm.selectedTab = 0
    avm.homeScrollOffset = -200
    return VStack(spacing: 0) {
        HeaderView()
            .environmentObject(headerPreviewService())
            .environmentObject(avm)
        Spacer()
    }
    .frame(width: 393)
    .background(AppColors.background)
}
#endif
