import SwiftUI
import Foundation
import NucleicCore

/// The project overview page, shown in the detail pane when a project name is
/// clicked. Built for a user returning after a break: at-a-glance cards for what
/// needs their attention and recent work — so they don't have to reconstruct what
/// they were doing.
struct ProjectView: View {
    let project: Project
    @Environment(AppStore.self) private var store
    @Environment(\.appPalette) private var palette

    /// Full session records (with cached summary blurbs), most-recent first.
    @State private var sessions: [Session] = []
    @State private var showingSettings = false

    private var active: [Session] { sessions.filter { !$0.archived } }
    /// Chats blocked on the user — what to deal with first. A chat whose agent simply
    /// finished its task (disposition `.completed`) isn't waiting on anyone, so it's
    /// excluded; only genuinely-blocked chats (awaiting approval, or awaiting input the
    /// agent actually asked for) surface here.
    private var needsAttention: [Session] {
        active.filter {
            $0.status == .awaitingApproval
                || ($0.status == .awaitingInput && $0.lastTurnDisposition != .completed)
        }
    }
    /// Recent chats, most-recent first.
    private var recent: [Session] { Array(active.prefix(8)) }

    private var activeCount: Int { active.filter { !$0.status.isTerminal }.count }
    private var changedFiles: Int {
        active.compactMap { $0.diffStat?.filesChanged }.reduce(0, +)
    }

    var body: some View {
        ScrollView {
            VStack(alignment: .leading, spacing: 28) {
                header
                statRow
                columns
            }
            .padding(32)
            .frame(maxWidth: .infinity, alignment: .leading)
        }
        .scrollContentBackground(.hidden)
        .task(id: project.id) { sessions = await store.sessions(for: project.id) }
        .task { await store.loadTodos() }
        .sheet(isPresented: $showingSettings) { ProjectSettingsSheet(project: project) }
    }

    /// The three sections, stacked in a single column: Needs-your-attention, the
    /// to-do list, then Recent work.
    @ViewBuilder
    private var columns: some View {
        VStack(alignment: .leading, spacing: 28) {
            if !needsAttention.isEmpty { attentionSection }
            ProjectTodoSection(projectID: project.id)
            recentSection
        }
    }

    // MARK: - Header

    private var sandboxEnabled: Bool { project.sandbox?.enabled == true }

    private var header: some View {
        VStack(alignment: .leading, spacing: 6) {
            HStack(alignment: .center) {
                Text(project.name)
                    .font(.system(size: 34, weight: .bold))
                // Nucleic Control projects show an atom next to the title; plain sandboxed
                // projects show the shield (matches the sidebar).
                if project.isNucleicControlled {
                    Image(systemName: "atom")
                        .font(.title3)
                        .foregroundStyle(Color(red: 0.62, green: 0.51, blue: 0.93))
                        // Center alignment sits the atom on the title's full line box, which reads
                        // slightly high; nudge down to balance against the cap height of the name.
                        .offset(y: 2)
                        .help("Nucleic Control project")
                } else if sandboxEnabled {
                    Image(systemName: "shield.lefthalf.filled")
                        .font(.title3)
                        .foregroundStyle(.tint)
                        .help("Sessions run in a sandbox container")
                }
                Spacer()
                BuildRunControls(project: project)
                Button {
                    showingSettings = true
                } label: {
                    Label("Settings", systemImage: "gearshape")
                }
                .buttonStyle(.bordered)
                Button {
                    Task { await store.newSession(in: project) }
                } label: {
                    Label("New chat", systemImage: "plus")
                }
                .buttonStyle(.borderedProminent)
            }
            HStack(spacing: 6) {
                // The leading slot swaps between the on-disk path and the "Nucleic Control
                // active" marker — Control replaces the path (the project lives behind the
                // control bundle, so the local path isn't meaningful) while the branch stays
                // pinned to the right of the row either way. Each keeps its own styling: the
                // path stays plain gray caption, the marker stays bold lavender.
                if project.isNucleicControlled {
                    Text("Nucleic Control active")
                        .font(.headline.weight(.semibold))
                        .foregroundStyle(Color(red: 0.62, green: 0.51, blue: 0.93))
                } else {
                    Image(systemName: "folder")
                    Text(project.rootPath)
                        .lineLimit(1)
                        .truncationMode(.middle)
                }
                Text("·")
                Image(systemName: "arrow.triangle.branch")
                Text(project.defaultBranch.value)
            }
            .font(.caption)
            .foregroundStyle(.secondary)
        }
    }

    // MARK: - Stats

    private var statRow: some View {
        HStack(spacing: 16) {
            ProjectStat(value: active.count, label: "Chats", icon: "bubble.left.and.bubble.right")
            ProjectStat(value: activeCount, label: "Active", icon: "bolt")
            ProjectStat(value: needsAttention.count, label: "Needs you", icon: "hand.raised",
                        tint: needsAttention.isEmpty ? nil : palette.attention)
            ProjectStat(value: changedFiles, label: "Changed files", icon: "doc.on.doc")
        }
    }

    private var emptyState: some View {
        VStack(alignment: .leading, spacing: 6) {
            Text("No chats yet")
                .font(.callout.weight(.medium))
            Text("Start one with the New chat button above, or the + next to this project in the sidebar.")
                .font(.caption)
                .foregroundStyle(.secondary)
        }
        .frame(maxWidth: .infinity, alignment: .leading)
        .padding(18)
        .background(AppTheme.surface, in: .rect(cornerRadius: 14))
        .overlay(RoundedRectangle(cornerRadius: 14).strokeBorder(AppTheme.hairline, lineWidth: 1))
    }

    // MARK: - Needs attention

    private var attentionSection: some View {
        VStack(alignment: .leading, spacing: 12) {
            Text("Needs your attention").font(.headline)
            VStack(spacing: 0) {
                ForEach(Array(needsAttention.enumerated()), id: \.element.id) { index, session in
                    if index > 0 { Divider() }
                    sessionRow(session)
                }
            }
            .background(AppTheme.surface, in: .rect(cornerRadius: 12))
            .overlay(RoundedRectangle(cornerRadius: 12).strokeBorder(palette.attention.opacity(0.28), lineWidth: 1))
        }
    }

    // MARK: - Recent

    private var recentSection: some View {
        VStack(alignment: .leading, spacing: 12) {
            Text("Recent work").font(.headline)
            if recent.isEmpty {
                emptyState
            } else {
                VStack(spacing: 0) {
                    ForEach(Array(recent.enumerated()), id: \.element.id) { index, session in
                        if index > 0 { Divider() }
                        RecentWorkEntry(session: session) { store.openSessionID = session.id }
                    }
                }
                .background(AppTheme.surface, in: .rect(cornerRadius: 12))
                .overlay(RoundedRectangle(cornerRadius: 12).strokeBorder(AppTheme.hairline, lineWidth: 1))
            }
        }
    }

    // MARK: - Shared rows

    @ViewBuilder
    private func sessionRow(_ session: Session) -> some View {
        Button { store.openSessionID = session.id } label: {
            HStack(spacing: 10) {
                Circle().fill(palette.status(session.status, disposition: session.lastTurnDisposition))
                    .frame(width: 8, height: 8)
                VStack(alignment: .leading, spacing: 2) {
                    Text(session.title).lineLimit(1)
                    metaRow(for: session)
                }
                Spacer()
                Image(systemName: "chevron.right")
                    .font(.caption2)
                    .foregroundStyle(.tertiary)
            }
            .contentShape(Rectangle())
            .padding(.horizontal, 14)
            .padding(.vertical, 10)
        }
        .buttonStyle(.plain)
    }

    /// Status label · relative time · branch · diff — the at-a-glance line.
    private func metaRow(for session: Session) -> some View {
        HStack(spacing: 8) {
            Text(session.status.label(disposition: session.lastTurnDisposition))
            Text("·")
            Text(relativeTime(session.updatedAt))
            if let branch = session.branch {
                Text("·")
                Image(systemName: "arrow.triangle.branch")
                Text(branch).lineLimit(1).truncationMode(.middle)
            }
            if let diff = session.diffStat, diff.filesChanged > 0 {
                Text("·")
                Text("+\(diff.added) −\(diff.removed)").monospaced()
            }
        }
        .font(.caption2)
        .foregroundStyle(.secondary)
    }

}

/// One recent chat in the "Recent work" feed: a clickable header plus the lines
/// distilled from its cached summary, each of which also opens the chat — so a
/// returning user can skim what every chat was up to and jump straight in.
private struct RecentWorkEntry: View {
    @Environment(\.appPalette) private var palette
    let session: Session
    let open: () -> Void

    var body: some View {
        VStack(alignment: .leading, spacing: 6) {
            Button(action: open) {
                HStack(spacing: 8) {
                    Circle().fill(palette.status(session.status)).frame(width: 8, height: 8)
                    Text(session.title).font(.callout.weight(.semibold)).lineLimit(1)
                    Spacer()
                    Text(relativeTime(session.updatedAt))
                        .font(.caption2).foregroundStyle(.secondary)
                    Image(systemName: "chevron.right").font(.caption2).foregroundStyle(.tertiary)
                }
                .contentShape(Rectangle())
            }
            .buttonStyle(.plain)

            let lines = Self.digestLines(session.summary)
            if lines.isEmpty {
                Button(action: open) {
                    Text("No summary yet — open to see the transcript.")
                        .font(.caption).foregroundStyle(.tertiary)
                        .frame(maxWidth: .infinity, alignment: .leading)
                        .contentShape(Rectangle())
                }
                .buttonStyle(.plain)
            } else {
                ForEach(lines) { line in
                    Button(action: open) {
                        HStack(alignment: .firstTextBaseline, spacing: 7) {
                            if line.isNext {
                                Image(systemName: "arrow.turn.down.right")
                                    .font(.system(size: 9, weight: .semibold))
                                    .foregroundStyle(palette.attention)
                                    .frame(width: 12)
                            } else {
                                Text("•").foregroundStyle(.secondary).frame(width: 12)
                            }
                            inlineMarkdown(line.text)
                                .font(.caption)
                                .foregroundStyle(line.isNext ? .secondary : .primary)
                                .frame(maxWidth: .infinity, alignment: .leading)
                        }
                        .contentShape(Rectangle())
                    }
                    .buttonStyle(.plain)
                }
            }
        }
        .padding(.horizontal, 14)
        .padding(.vertical, 10)
        .frame(maxWidth: .infinity, alignment: .leading)
    }

    /// One distilled line from a summary; `isNext` flags a suggested next step
    /// (the summary's "Next:" section) so it can be styled as a hint.
    struct DigestLine: Identifiable {
        let id = UUID()
        let text: String
        let isNext: Bool
    }

    /// Flattens a Markdown summary ("**Done:** / **Just now:** / **Next:**" with
    /// `- ` bullets) into clickable lines, dropping the section headers but keeping
    /// track of which lines were the "Next" suggestion.
    static func digestLines(_ summary: String?) -> [DigestLine] {
        guard let summary, !summary.isEmpty else { return [] }
        var result: [DigestLine] = []
        var inNext = false
        for raw in summary.components(separatedBy: "\n") {
            let line = raw.trimmingCharacters(in: .whitespaces)
            if line.isEmpty { continue }
            // A bold section header on its own line, e.g. "**Done:**" or "**Next:**".
            if line.hasPrefix("**"), line.hasSuffix(":**") {
                let name = line.replacingOccurrences(of: "*", with: "")
                    .trimmingCharacters(in: CharacterSet(charactersIn: ": "))
                inNext = name.lowercased().hasPrefix("next")
                continue
            }
            let text = bulletContent(line) ?? line
            result.append(DigestLine(text: text, isNext: inNext))
        }
        return Array(result.prefix(6))
    }

    /// Content after a `- `, `* ` or `+ ` list marker, else nil.
    private static func bulletContent(_ line: String) -> String? {
        for marker in ["- ", "* ", "+ "] where line.hasPrefix(marker) {
            return String(line.dropFirst(marker.count))
        }
        return nil
    }
}

private func relativeTime(_ date: Date) -> String {
    let formatter = RelativeDateTimeFormatter()
    formatter.unitsStyle = .abbreviated
    return formatter.localizedString(for: date, relativeTo: Date())
}

/// Renders a single line of inline Markdown (bold, `code`, em-dashes) as `Text`.
private func inlineMarkdown(_ string: String) -> Text {
    let options = AttributedString.MarkdownParsingOptions(
        interpretedSyntax: .inlineOnlyPreservingWhitespace)
    if let attributed = try? AttributedString(markdown: string, options: options) {
        return Text(attributed)
    }
    return Text(string)
}

/// A compact stat tile for the project overview, mirroring the home dashboard's
/// `StatCard` look so the two pages feel of a piece.
private struct ProjectStat: View {
    let value: Int
    let label: String
    let icon: String
    var tint: Color? = nil

    var body: some View {
        VStack(alignment: .leading, spacing: 8) {
            Image(systemName: icon).foregroundStyle(tint ?? .secondary)
            Text("\(value)").font(.system(size: 30, weight: .semibold).monospacedDigit())
                .foregroundStyle(tint ?? .primary)
            Text(label).font(.caption).foregroundStyle(.secondary)
        }
        .frame(minWidth: 96, alignment: .leading)
        .padding(16)
        .background(AppTheme.surface, in: .rect(cornerRadius: 12))
        .overlay(RoundedRectangle(cornerRadius: 12).strokeBorder(AppTheme.hairline, lineWidth: 1))
    }
}
