import SwiftUI
import NucleicProtocol

/// The mobile home dashboard — the phone's mirror of the Mac home (UX_MACOS §2): a greeting +
/// streak, a GitHub-style activity grid, rollup stat cards, a quick to-do peek, and a
/// start-chat composer (control scope).
struct HomeView: View {
    @EnvironmentObject var store: RemoteStore

    private var counts: DashboardCounts { store.dashboard.counts }

    /// Sessions actively working a turn — drives both the "In progress" stat card and the
    /// live list pinned above the to-dos.
    private var running: [WireSessionSummary] {
        store.liveSessions.filter { $0.status == .running }.sorted { $0.updatedAt > $1.updatedAt }
    }
    private var activityByDay: [Date: Int] {
        Dictionary(store.dashboard.activity.map { (Calendar.current.startOfDay(for: $0.day), $0.count) },
                   uniquingKeysWith: +)
    }
    /// Tokens used per day — the activity grid's primary intensity signal (falls back to message
    /// count for days without recorded usage). Mirrors the Mac home.
    private var tokensByDay: [Date: Int] {
        Dictionary(store.dashboard.activity.map { (Calendar.current.startOfDay(for: $0.day), $0.tokens) },
                   uniquingKeysWith: +)
    }

    /// Current activity streak with streak freezes folded in — the run of consecutive active
    /// days, where a banked freeze (one earned per five days) bridges a missed day. Shared with
    /// the Mac via `StreakState`, so both devices report the same streak and frozen days.
    private var streakState: StreakState {
        StreakState.compute(activityByDay: activityByDay)
    }

    private var greeting: String {
        switch Calendar.current.component(.hour, from: Date()) {
        case 5..<12: "Good morning"
        case 12..<17: "Good afternoon"
        case 17..<22: "Good evening"
        default: "Hello"
        }
    }

    var body: some View {
        // Computed once and threaded down so the badge and grid agree on the streak and which
        // days a freeze bridged.
        let streak = streakState
        return NavigationStack {
            ScrollView {
                VStack(alignment: .leading, spacing: 22) {
                    HStack(alignment: .top) {
                        VStack(alignment: .leading, spacing: 2) {
                            Text(greeting).font(.largeTitle.bold())
                            Text(subtitle).font(.subheadline).foregroundStyle(.secondary)
                        }
                        Spacer()
                        if streak.streak > 0 { StreakBadge(days: streak.streak, freezes: streak.availableFreezes) }
                    }

                    statCards

                    VStack(alignment: .leading, spacing: 10) {
                        Text("Activity").font(.headline)
                        ActivityGrid(
                            activityByDay: activityByDay, tokensByDay: tokensByDay,
                            frozenDays: streak.frozenDays)
                    }
                    .frame(maxWidth: .infinity, alignment: .leading)
                    .card()

                    if !running.isEmpty { InProgressSessions(sessions: running) }

                    QuickTodos()

                    StartChatComposer()
                }
                .padding()
            }
            .navigationTitle("Home")
            .navigationBarTitleDisplayMode(.inline)
            .scrollDismissesKeyboard(.interactively)
            .refreshable { store.refreshSessions() }
            .safeAreaInset(edge: .bottom) { ConnectionChip().padding(.bottom, 8) }
        }
    }

    private var subtitle: String {
        if counts.chats == 0 { return "Start a chat below to get going." }
        let p = counts.projects == 1 ? "project" : "projects"
        return "\(counts.activeChats) active of \(counts.chats) chats · \(counts.projects) \(p)"
    }

    private var statCards: some View {
        HStack(spacing: 12) {
            StatCard(value: counts.projects, label: "Projects", icon: "folder")
            StatCard(value: running.count, label: "In progress", icon: "circle.lefthalf.filled")
            StatCard(value: counts.activeChats, label: "Active", icon: "bolt")
            StatCard(value: counts.messages, label: "Messages", icon: "paperplane")
            StatCard(values: TokenCount.adaptiveCandidates(counts.tokens), label: "Tokens", icon: "text.word.spacing")
        }
    }
}

/// Live peek at sessions actively running a turn, pinned above the to-do list on Home. Tapping
/// a row drops into the session transcript.
private struct InProgressSessions: View {
    let sessions: [WireSessionSummary]

    var body: some View {
        VStack(alignment: .leading, spacing: 10) {
            HStack {
                Text("In progress").font(.headline)
                Spacer()
                Text("\(sessions.count)").foregroundStyle(.secondary)
            }
            ForEach(sessions.prefix(4), id: \.sessionID) { summary in
                NavigationLink {
                    SessionDetailView(sessionID: summary.sessionID)
                } label: {
                    SessionRow(summary: summary)
                }
                .buttonStyle(.plain)
            }
        }
        .frame(maxWidth: .infinity, alignment: .leading)
        .card()
    }
}

private struct StatCard: View {
    /// Candidate renderings of the value, widest first: the card shows the fullest one its width
    /// allows (via `ViewThatFits`) and falls back to the narrowest. A single entry for the plain
    /// integer cards; several for the token card, so a wide card shows more digits than "1.6B".
    let values: [String]
    let label: String
    let icon: String

    init(value: Int, label: String, icon: String) {
        self.init(values: ["\(value)"], label: label, icon: icon)
    }

    init(value: String, label: String, icon: String) {
        self.init(values: [value], label: label, icon: icon)
    }

    init(values: [String], label: String, icon: String) {
        self.values = values.isEmpty ? [""] : values
        self.label = label
        self.icon = icon
    }

    var body: some View {
        VStack(alignment: .leading, spacing: 4) {
            Image(systemName: icon).foregroundStyle(Palette.accent)
            // Show the fullest value the card's width fits, shrinking toward the compact form when
            // narrow; ViewThatFits renders the last candidate if none fit, so it never overflows.
            ViewThatFits(in: .horizontal) {
                ForEach(Array(values.enumerated()), id: \.offset) { _, value in
                    Text(value).font(.title2.weight(.semibold).monospacedDigit())
                        .lineLimit(1)
                }
            }
            Text(label).font(.caption).foregroundStyle(.secondary)
        }
        .frame(maxWidth: .infinity, alignment: .leading)
        .card(padding: 12)
    }
}

/// A lightning bolt whose charge grows with the streak (mirrors the Mac StreakBadge).
struct StreakBadge: View {
    let days: Int
    /// Streak freezes in reserve (one per five days), shown as a small snowflake tally.
    var freezes: Int = 0
    private var intensity: Double { min(1, Double(max(0, days - 1)) / 20) }

    var body: some View {
        HStack(spacing: 6) {
            Image(systemName: "bolt.fill")
                .font(.system(size: 18 + 8 * intensity))
                .foregroundStyle(
                    LinearGradient(colors: [
                        Color(hue: 0.55 - 0.05 * intensity, saturation: 0.5 + 0.2 * intensity, brightness: 1),
                        Color(hue: 0.60, saturation: 0.85 + 0.15 * intensity, brightness: 1),
                    ], startPoint: .top, endPoint: .bottom))
                .shadow(color: Color(hue: 0.56, saturation: 0.85, brightness: 1).opacity(0.3 + 0.5 * intensity),
                        radius: 1 + 6 * intensity)
            VStack(alignment: .leading, spacing: -2) {
                Text("\(days)").font(.headline.monospacedDigit())
                Text("day streak").font(.caption2).foregroundStyle(.secondary)
            }
            if freezes > 0 {
                HStack(spacing: 2) {
                    Image(systemName: "snowflake")
                    Text("\(freezes)").monospacedDigit()
                }
                .font(.caption.weight(.semibold))
                .foregroundStyle(Palette.frozen)
            }
        }
        .padding(.horizontal, 12).padding(.vertical, 8)
        .background(Color(.secondarySystemBackground), in: Capsule())
    }
}

/// GitHub-style activity grid: columns = weeks (old→new), rows = Sun…Sat.
struct ActivityGrid: View {
    let activityByDay: [Date: Int]
    /// Tokens used per day — the primary intensity signal; days absent here fall back to their
    /// message count (mirrors the Mac grid via `ActivityShading`).
    var tokensByDay: [Date: Int] = [:]
    /// Missed days a streak freeze kept alive — drawn in the frozen blue rather than the
    /// empty-day gray (mirrors the Mac grid).
    var frozenDays: Set<Date> = []
    private let cell: CGFloat = 13
    private let gap: CGFloat = 3

    var body: some View {
        GeometryReader { geo in
            let weeks = max(8, Int((geo.size.width + gap) / (cell + gap)))
            grid(weeks: weeks)
        }
        .frame(height: cell * 7 + gap * 6)
    }

    private func grid(weeks: Int) -> some View {
        let cal = Calendar.current
        let today = cal.startOfDay(for: Date())
        let weekday = cal.component(.weekday, from: today)
        let startOfWeek = cal.date(byAdding: .day, value: -(weekday - 1), to: today)!
        let gridStart = cal.date(byAdding: .day, value: -7 * (weeks - 1), to: startOfWeek)!
        // Brightness relative to *this user's* activity, outliers excluded so a few marathon
        // days don't crush the everyday range. Shades by tokens-per-day, falling back to message
        // count for days without recorded usage. Computed once for the whole grid (mirrors Mac).
        let shading = ActivityShading(tokensByDay: tokensByDay, messagesByDay: activityByDay)
        return HStack(alignment: .top, spacing: gap) {
            ForEach(0..<weeks, id: \.self) { col in
                VStack(spacing: gap) {
                    ForEach(0..<7, id: \.self) { row in
                        let date = cal.date(byAdding: .day, value: col * 7 + row, to: gridStart)!
                        let isFuture = date > today
                        let isFrozen = !isFuture && frozenDays.contains(date)
                        RoundedRectangle(cornerRadius: 2)
                            .fill(isFuture ? Color.clear
                                : isFrozen ? Palette.frozen
                                : Palette.activity(intensity: shading.intensity(
                                    tokens: tokensByDay[date] ?? 0, messages: activityByDay[date] ?? 0)))
                            .frame(width: cell, height: cell)
                    }
                }
            }
        }
        .frame(maxWidth: .infinity, alignment: .leading)
    }
}
