import ActivityKit
import SwiftUI
import WidgetKit

/// Lock-screen / Dynamic Island rendering of the aggregate session Activity (UX_IOS §5.3).
/// The glance answers "what is Nucleic doing right now?": headline counts (running / waiting /
/// approvals), the total churn in flight, and the most urgent sessions with their state and diff.
/// Tapping opens the app (straight to the approval, via the notification-style route).
///
/// Self-contained colors/glyphs — the extension doesn't link the app's `Theme`, so the small
/// palette + glyph maps it needs live here (the same teal/amber/blue/green as `Palette`).
struct SessionLiveActivity: Widget {
    var body: some WidgetConfiguration {
        ActivityConfiguration(for: NucleicSessionAttributes.self) { context in
            LockScreenView(state: context.state, hostName: context.attributes.hostName)
                .widgetURL(context.state.tapURL)
                // When something needs the user, wash the whole card in the attention tint;
                // otherwise the calm dark background (see the border/header cues in LockScreenView).
                .activityBackgroundTint(context.state.needsAttention
                    ? ActivityPalette.attentionTint(context.state).opacity(0.28)
                    : Color.black.opacity(0.55))
                .activitySystemActionForegroundColor(.white)
        } dynamicIsland: { context in
            let state = context.state
            return DynamicIsland {
                DynamicIslandExpandedRegion(.leading) {
                    CountBadge(
                        systemImage: "circle.lefthalf.filled",
                        count: state.runningCount, tint: ActivityPalette.active)
                }
                DynamicIslandExpandedRegion(.trailing) {
                    if state.approvalCount > 0 {
                        CountBadge(
                            systemImage: "exclamationmark.shield.fill",
                            count: state.approvalCount, tint: ActivityPalette.attention)
                    } else if state.needsYouCount > 0 {
                        CountBadge(
                            systemImage: "bubble.left.fill",
                            count: state.needsYouCount, tint: ActivityPalette.accent)
                    } else {
                        Label("0", systemImage: "checkmark.circle")
                            .font(.headline).foregroundStyle(.secondary)
                    }
                }
                DynamicIslandExpandedRegion(.bottom) {
                    VStack(alignment: .leading, spacing: 5) {
                        ForEach(state.lines.prefix(2)) { line in
                            SessionRow(line: line)
                        }
                    }
                }
            } compactLeading: {
                // Calm state shows the Nucleic Control brand mark (the purple atom); when the
                // user is needed it switches to the amber approval glyph.
                Image(systemName: state.needsAttention
                    ? ActivityPalette.glyph(.approval) : "atom")
                    .foregroundStyle(state.needsAttention
                        ? ActivityPalette.attention : ActivityPalette.brand)
            } compactTrailing: {
                Text("\(state.needsAttention ? max(state.approvalCount, state.needsYouCount) : state.runningCount)")
                    .font(.caption2.bold())
                    .foregroundStyle(state.needsAttention ? ActivityPalette.attention : ActivityPalette.active)
            } minimal: {
                Image(systemName: state.needsAttention
                    ? ActivityPalette.glyph(.approval) : ActivityPalette.glyph(.running))
                    .foregroundStyle(state.needsAttention
                        ? ActivityPalette.attention : ActivityPalette.active)
            }
            // The compact/minimal presentations tap through to the same target as the lock
            // screen: a waiting session opens itself; otherwise the sessions list.
            .widgetURL(state.tapURL)
        }
    }
}

// MARK: - Lock screen

private struct LockScreenView: View {
    let state: NucleicSessionAttributes.ContentState
    let hostName: String

    var body: some View {
        let attention = state.needsAttention
        let tint = ActivityPalette.attentionTint(state)
        return VStack(alignment: .leading, spacing: 8) {
            // Header: brand + host, and the headline counts. When the user is needed the brand
            // mark becomes the attention glyph in its tint, so the header itself reads "act now".
            HStack(spacing: 8) {
                Image(systemName: attention
                    ? ActivityPalette.glyph(state.approvalCount > 0 ? .approval : .needsInput)
                    : "hexagon.fill")
                    .font(.footnote)
                    .foregroundStyle(attention ? tint : ActivityPalette.active)
                Text(hostName)
                    .font(.caption.weight(.semibold))
                    .lineLimit(1)
                Spacer(minLength: 8)
                CountChips(state: state)
            }

            // The most urgent sessions — what's actually happening.
            ForEach(state.lines.prefix(3)) { line in
                SessionRow(line: line)
            }
        }
        .padding(14)
        .foregroundStyle(.white)
        // Attention theme: a soft tinted wash + a colored border around the whole glance, so a
        // session waiting on the user is unmistakable at a glance even before reading the rows.
        .background {
            if attention {
                RoundedRectangle(cornerRadius: 22, style: .continuous)
                    .fill(tint.opacity(0.12))
            }
        }
        .overlay {
            if attention {
                RoundedRectangle(cornerRadius: 22, style: .continuous)
                    .strokeBorder(tint.opacity(0.9), lineWidth: 2)
            }
        }
    }
}

// MARK: - Shared pieces

/// One session: state glyph + title + "project · Backend" + a right-aligned status/diff.
///
/// Each row is its own tap target: wrapped in a `Link` to `nucleic://session/<id>`, so tapping a
/// specific session on the lock screen or expanded Dynamic Island opens *that* session — overriding
/// the activity-wide `widgetURL` (which still governs the header, churn footer, and empty space, and
/// remains the only tap target the compact/minimal presentations can offer).
private struct SessionRow: View {
    let line: NucleicSessionAttributes.SessionLine

    /// An approval this row can resolve inline: it's blocked on an approval, carries that approval's
    /// id, and isn't high-risk (§3.3 — high-risk keeps the plain tap-to-open row and is answered on
    /// the app's guarded card). `nil` until the producer populates `approvalID` — an older host, or
    /// today's summary that doesn't yet carry it, so the row stays a deep-link exactly as before.
    private var inlineApprovalID: String? {
        guard line.kind == .approval, (line.approvalIsHighRisk ?? false) == false else { return nil }
        return line.approvalID
    }

    var body: some View {
        if let approvalID = inlineApprovalID {
            actionableRow(approvalID: approvalID)
        } else if let url = NucleicDeepLink.session(line.id) {
            Link(destination: url) { rowContent }
        } else {
            rowContent
        }
    }

    /// An approval row with inline Allow/Deny. The identity still deep-links to the session; the
    /// buttons fire `ApproveFromActivityIntent`, which the system performs in the app's background
    /// process — allow/deny without unlocking (§4.1).
    private func actionableRow(approvalID: String) -> some View {
        HStack(spacing: 8) {
            if let url = NucleicDeepLink.session(line.id) {
                Link(destination: url) { rowIdentity }
            } else {
                rowIdentity
            }
            Spacer(minLength: 6)
            ApprovalButtons(sessionID: line.id, approvalID: approvalID)
        }
    }

    /// The glyph + title + "project · Backend" — the row's identity, minus the trailing status.
    private var rowIdentity: some View {
        HStack(spacing: 8) {
            Image(systemName: ActivityPalette.glyph(line.kind))
                .font(.footnote)
                .foregroundStyle(ActivityPalette.color(line.kind))
                .frame(width: 16)
            VStack(alignment: .leading, spacing: 1) {
                Text(line.title)
                    .font(.subheadline.weight(.medium))
                    .lineLimit(1)
                HStack(spacing: 4) {
                    Text(line.project)
                    Text("·").foregroundStyle(.secondary)
                    Text(line.backend.label)
                        .foregroundStyle(ActivityPalette.backendColor(line.backend))
                }
                .font(.caption2)
                .foregroundStyle(.secondary)
                .lineLimit(1)
            }
        }
    }

    private var rowContent: some View {
        HStack(spacing: 8) {
            rowIdentity
            Spacer(minLength: 6)
            Text(line.detail)
                .font(.caption2.weight(.medium))
                .monospacedDigit()
                .foregroundStyle(ActivityPalette.color(line.kind))
                .lineLimit(1)
        }
    }
}

/// The compact inline Allow/Deny pair for an actionable approval row. Each button fires
/// `ApproveFromActivityIntent` (a low/medium-risk decision — high-risk never reaches here).
private struct ApprovalButtons: View {
    let sessionID: String
    let approvalID: String

    var body: some View {
        HStack(spacing: 6) {
            Button(intent: intent(allow: false)) {
                Image(systemName: "xmark").font(.caption2.bold())
            }
            .tint(ActivityPalette.danger)
            Button(intent: intent(allow: true)) {
                Image(systemName: "checkmark").font(.caption2.bold())
            }
            .tint(ActivityPalette.success)
        }
        .buttonStyle(.borderedProminent)
        .controlSize(.small)
    }

    private func intent(allow: Bool) -> ApproveFromActivityIntent {
        ApproveFromActivityIntent(
            approvalID: approvalID, sessionID: sessionID, isHighRisk: false, allow: allow)
    }
}

/// Headline count chips: running (blue), waiting (teal), approvals (amber). Only nonzero show.
private struct CountChips: View {
    let state: NucleicSessionAttributes.ContentState

    var body: some View {
        HStack(spacing: 10) {
            if state.runningCount > 0 {
                chip("circle.lefthalf.filled", state.runningCount, ActivityPalette.active)
            }
            if state.needsYouCount > 0 {
                chip("bubble.left.fill", state.needsYouCount, ActivityPalette.accent)
            }
            if state.approvalCount > 0 {
                chip("exclamationmark.shield.fill", state.approvalCount, ActivityPalette.attention)
            }
        }
    }

    private func chip(_ systemImage: String, _ count: Int, _ tint: Color) -> some View {
        HStack(spacing: 3) {
            Image(systemName: systemImage).font(.caption2)
            Text("\(count)").font(.caption.weight(.semibold)).monospacedDigit()
        }
        .foregroundStyle(tint)
    }
}

/// Dynamic Island expanded leading/trailing count badge.
private struct CountBadge: View {
    let systemImage: String
    let count: Int
    let tint: Color

    var body: some View {
        Label("\(count)", systemImage: systemImage)
            .font(.headline)
            .monospacedDigit()
            .foregroundStyle(tint)
    }
}

/// The categorical colors + glyphs the Activity needs — the same semantics as the app's
/// `Palette.status` / `StatusStyle.symbol`, re-declared because the widget extension can't link
/// the app's `Theme`.
private enum ActivityPalette {
    static let active = Color(red: 0.20, green: 0.55, blue: 0.95)     // running blue
    static let attention = Color(red: 1.00, green: 0.65, blue: 0.15)  // approval amber
    static let accent = Color(red: 0.16, green: 0.72, blue: 0.68)     // needs-input teal
    static let success = Color(red: 0.30, green: 0.78, blue: 0.45)    // done green
    static let danger = Color(red: 0.92, green: 0.34, blue: 0.34)     // error red
    static let neutral = Color.secondary
    static let brand = Color(red: 0.62, green: 0.51, blue: 0.93)      // Nucleic Control purple
    static let added = Color(red: 0.30, green: 0.78, blue: 0.45)
    static let removed = Color(red: 0.92, green: 0.34, blue: 0.34)

    /// The color the "attention needed" theme paints with: approval amber is loudest, else the
    /// needs-input teal. Only meaningful when `state.needsAttention` — callers gate on that.
    static func attentionTint(_ state: NucleicSessionAttributes.ContentState) -> Color {
        state.approvalCount > 0 ? attention : accent
    }

    static func glyph(_ kind: NucleicSessionAttributes.Kind) -> String {
        switch kind {
        case .running: "circle.lefthalf.filled"
        case .provisioning: "circle.dotted"
        case .approval: "exclamationmark.triangle.fill"
        case .needsInput: "bubble.left.fill"
        case .done: "checkmark.circle.fill"
        case .error: "xmark.octagon.fill"
        case .idle: "circle"
        }
    }

    static func color(_ kind: NucleicSessionAttributes.Kind) -> Color {
        switch kind {
        case .running: active
        case .provisioning: neutral
        case .approval: attention
        case .needsInput: accent
        case .done: success
        case .error: danger
        case .idle: neutral
        }
    }

    static func backendColor(_ backend: NucleicSessionAttributes.Backend) -> Color {
        switch backend {
        case .claude: Color(red: 0.85, green: 0.52, blue: 0.30)   // Anthropic clay
        case .codex: Color(red: 0.45, green: 0.72, blue: 0.55)    // OpenAI green
        case .grok: Color(red: 0.55, green: 0.58, blue: 0.66)     // xAI graphite
        case .other: Color.secondary
        }
    }
}
