import SwiftUI

/// One board's own settings, reached by swiping its row in the boards list.
///
/// **Reached from the list on purpose.** Its one setting today — which home the board is stored in —
/// is the only thing in the app that changes a board's URL, and a board's URL is its identity
/// (`BoardSummary.id`). Presenting this from the list means no screen is holding the old identity
/// when it retires; the sheet dismisses itself on success and the row underneath is already the new
/// board.
struct BoardSettingsSheet: View {

    /// A snapshot, taken when the swipe action fired. It goes stale exactly once — at the moment a
    /// move succeeds — and the sheet is gone by then.
    let board: BoardSummary

    @Environment(BoardIndexStore.self) private var index
    @Environment(\.dismiss) private var dismiss

    @State private var isConfirmingMove = false
    @State private var isMoving = false

    /// The last move's refusal, shown in the section footer rather than as an alert: the failure is
    /// about the row the user is looking at, and every one of them is a "try again" rather than a
    /// decision to make.
    @State private var failure: String?

    /// There are two homes, so there is one destination and the button can name it instead of asking.
    private var destination: BoardLocation {
        board.location == .icloud ? .local : .icloud
    }

    /// iCloud is a destination only when it resolved. In practice a run with no iCloud lists no iCloud
    /// boards at all — the query is dead — so this guards a state the app should not be able to reach
    /// rather than one it routinely does.
    private var canMove: Bool {
        destination == .local || index.cloudUnavailable == nil
    }

    var body: some View {
        NavigationStack {
            Form {
                Section {
                    LabeledContent("Stored In", value: board.location.name)
                    Button {
                        isConfirmingMove = true
                    } label: {
                        HStack {
                            Text("Move to \(destination.name)")
                            if isMoving {
                                Spacer()
                                ProgressView()
                            }
                        }
                    }
                    .disabled(isMoving || !canMove)
                } header: {
                    Text("Location")
                } footer: {
                    Text(footer)
                }
            }
            .navigationTitle("Board Settings")
            .navigationBarTitleDisplayMode(.inline)
            .toolbar {
                ToolbarItem(placement: .confirmationAction) {
                    Button("Done") { dismiss() }
                }
            }
            .confirmationDialog(
                confirmationTitle,
                isPresented: $isConfirmingMove,
                titleVisibility: .visible
            ) {
                // Destructive only in the direction that removes protection: a board leaving iCloud
                // stops syncing and stops being backed up by anything but this phone. The return trip
                // only adds, so it is an ordinary button.
                Button("Move", role: destination == .local ? .destructive : nil) { move() }
                Button("Cancel", role: .cancel) {}
            } message: {
                Text(confirmationMessage)
            }
        }
    }

    private var footer: String {
        if let failure { return failure }
        if !canMove { return "Sign into iCloud to move boards there." }
        switch board.location {
        case .icloud: return "This board is in iCloud Drive and syncs to your other devices."
        case .local: return "This board is on this iPhone only."
        }
    }

    private var confirmationTitle: String {
        destination == .local
            ? "Move “\(board.title)” out of iCloud?"
            : "Move “\(board.title)” to iCloud?"
    }

    private var confirmationMessage: String {
        destination == .local
            ? "It will stop syncing and live only on this iPhone. Your other devices won't see it."
            : "It will sync to your other devices."
    }

    private func move() {
        isMoving = true
        failure = nil
        Task {
            let outcome = await index.relocateBoard(at: board.rootURL, to: destination)
            isMoving = false
            switch outcome {
            case .success:
                // The list underneath has already refreshed, and this sheet's `board` names a package
                // that is no longer there.
                dismiss()
            case let .failure(reason):
                failure = reason.description
            }
        }
    }
}
