import SwiftUI

/// The root of the boards navigation stack: board list → lanes → cards → card detail
/// (`BoardRoute`'s destinations) — and, now that the tab bar is gone, the app's root screen.
/// Settings is presented from the leading gear button rather than a tab of its own.
///
/// Renders both of `BoardIndexStore.Phase`'s live states and, above the list, the iCloud notice —
/// which is a row, not a wall (softened 2026-08-08). A phone with no account still has a device home
/// and therefore still has boards, so the missing half of the app is reported next to the half that
/// works rather than in place of it. A segmented control in the toolbar lets the list be sorted by
/// name or by most recent change, remembered across launches.
struct BoardsTabView: View {
    @Environment(BoardIndexStore.self) private var index

    @State private var isPresentingNewBoard = false
    @State private var isPresentingSettings = false

    /// Persisted as its raw value, not the enum itself — `AppStorage` needs a property-list type, and
    /// the raw `String` is exactly what `BoardSortOrder` promises to keep stable.
    @AppStorage("boardListSortOrder") private var sortOrderRaw = BoardSortOrder.recent.rawValue

    /// The board whose settings sheet is up. A value, not a URL: the sheet renders the row's own
    /// summary, and a successful move dismisses it before the stale copy could matter.
    @State private var boardInSettings: BoardSummary?

    var body: some View {
        NavigationStack {
            content
                .navigationTitle("Boards")
                .toolbar {
                    // Unconditional — settings (About, license, changelog) must stay reachable
                    // even while the index is still loading or the board list is empty.
                    ToolbarItem(placement: .topBarLeading) {
                        Button("Settings", systemImage: "gearshape") {
                            isPresentingSettings = true
                        }
                    }
                }
                .toolbar {
                    if case .ready = index.phase {
                        Button("New Board", systemImage: "plus") {
                            isPresentingNewBoard = true
                        }
                    }
                }
                .toolbar {
                    sortToolbarItem
                }
                .navigationDestination(for: BoardRoute.self) { route in
                    switch route {
                    case let .lanes(boardRoot):
                        BoardScreen(boardRoot: boardRoot)
                    case let .cards(boardRoot, laneID):
                        LaneScreen(boardRoot: boardRoot, laneID: laneID)
                    case let .card(boardRoot, laneID, cardID):
                        CardDetailScreen(boardRoot: boardRoot, laneID: laneID, cardID: cardID)
                    }
                }
        }
        .task { index.start() }
        .titlePromptAlert(
            "New Board",
            isPresented: $isPresentingNewBoard,
            // Said only when it is news. With iCloud available the answer is the one every board
            // already gives; without it, where the board lands is the thing the user most needs to
            // know before naming it.
            message: index.cloudUnavailable == nil ? nil : "This board will be created on this iPhone.",
            placeholder: "Board Title"
        ) { title in
            Task { await index.createBoard(titled: title) }
        }
        .sheet(item: $boardInSettings) { board in
            BoardSettingsSheet(board: board)
        }
        .sheet(isPresented: $isPresentingSettings) {
            SettingsScreen()
        }
    }

    /// The stored raw value as the enum, defaulting to `.recent` on anything the store didn't write
    /// itself — an unset key or a stale raw value from a build that no longer has this case.
    private var sortOrder: Binding<BoardSortOrder> {
        Binding(
            get: { BoardSortOrder(rawValue: sortOrderRaw) ?? .recent },
            set: { sortOrderRaw = $0.rawValue }
        )
    }

    private var sortedBoards: [BoardSummary] {
        sortOrder.wrappedValue.sorted(index.boards)
    }

    /// Shown only once there is something to sort — an empty or not-yet-loaded list has no rows
    /// for the segments to reorder.
    @ToolbarContentBuilder
    private var sortToolbarItem: some ToolbarContent {
        if case .ready = index.phase, !index.boards.isEmpty {
            ToolbarItem(placement: .principal) {
                Picker("Sort", selection: sortOrder) {
                    Label("Name", systemImage: "textformat.abc").tag(BoardSortOrder.name)
                    Label("Recent", systemImage: "clock").tag(BoardSortOrder.recent)
                }
                .pickerStyle(.segmented)
                .frame(maxWidth: 260)
            }
        }
    }

    @ViewBuilder
    private var content: some View {
        switch index.phase {
        case .idle, .loading:
            ProgressView("Looking for boards")

        case .ready:
            List {
                if let reason = index.cloudUnavailable {
                    cloudNotice(reason)
                }
                if index.boards.isEmpty {
                    Section {
                        ContentUnavailableView(
                            "No Boards Yet",
                            systemImage: "rectangle.stack",
                            description: Text(emptyDescription)
                        )
                    }
                } else {
                    ForEach(sortedBoards) { board in
                        NavigationLink(value: BoardRoute.lanes(boardRoot: board.rootURL)) {
                            BoardSummaryRow(board: board)
                        }
                        .swipeActions(edge: .trailing) {
                            // Neutral tint: this reveals a screen, it does not destroy anything, and
                            // the one destructive-looking thing behind it has its own confirmation.
                            Button("Settings", systemImage: "gearshape") {
                                boardInSettings = board
                            }
                            .tint(.gray)
                        }
                    }
                }
            }
            .refreshable { index.refresh() }
        }
    }

    private var emptyDescription: String {
        index.cloudUnavailable == nil
            ? "Boards in your iCloud Drive will appear here."
            : "New boards will be saved on this iPhone."
    }

    /// The demoted wall. Same two sentences it used to say full-screen, and the same retry button —
    /// `start()` re-attempts the container resolution and nothing else, because everything else
    /// already resolved.
    private func cloudNotice(_ reason: CloudHomeUnavailable) -> some View {
        Section {
            Label {
                VStack(alignment: .leading, spacing: 2) {
                    Text("iCloud Unavailable")
                    Text(reason == .noAccount
                         ? "Sign into iCloud in Settings to sync your boards."
                         : "Lanework can't reach its iCloud Drive folder right now.")
                        .font(.caption)
                        .foregroundStyle(.secondary)
                }
            } icon: {
                Image(systemName: "icloud.slash")
            }
            Button("Try Again") { index.start() }
        } footer: {
            Text("Boards on this iPhone still work, and can be moved to iCloud later.")
        }
    }
}

/// One board row: its icon when it has one, title, lane/card counts, modified date, a marker for a
/// board that is not in iCloud, and — while the package is not fully current — a download-state
/// subtitle in place of the counts a shallow scan cannot yet answer.
///
/// Only the local side is marked. iCloud is where a board is expected to be, so saying so on every
/// row would be noise; "Local" is the exception, and the exception is what a marker is for.
private struct BoardSummaryRow: View {
    let board: BoardSummary

    var body: some View {
        HStack {
            ItemIconView(icon: board.icon, iconColor: board.iconColor)
            VStack(alignment: .leading, spacing: 2) {
                Text(board.title)
                HStack(spacing: 6) {
                    if board.location == .local {
                        Label("Local", systemImage: "iphone")
                    }
                    Text(subtitle)
                }
                .font(.caption)
                .foregroundStyle(.secondary)
            }
        }
    }

    private var subtitle: String {
        switch board.download {
        case .notDownloaded:
            "Waiting to download"
        case let .downloading(fraction):
            fraction.map { "Downloading \(Int($0 * 100))%" } ?? "Downloading"
        case .current, .unknown:
            countsAndModified
        }
    }

    private var countsAndModified: String {
        let counts: String
        if let lanes = board.laneCount, let cards = board.cardCount {
            counts = "\(lanes) lane\(lanes == 1 ? "" : "s") · \(cards) card\(cards == 1 ? "" : "s")"
        } else {
            counts = "—"
        }
        guard let modified = board.modified else { return counts }
        return "\(counts) · \(modified.formatted(.relative(presentation: .named)))"
    }
}
