import SwiftUI

/// **The Settings Pro section** — subscribe, manage, restore (12-editions.md ▸ Distribution), and
/// the third and last place the app names Lanework Pro (12 ▸ Tier naming; the other two are
/// `AboutBox`'s line and `BoardGitNote`'s contextual one).
///
/// ### Calm, because the rule says calm
///
/// "The free tier presents as a complete app, not a demo ... Nothing on the welcome screen, nothing
/// in banners." This section is where the subscription actually lives, so it is allowed to describe
/// itself — and that is all it does: what Pro adds, in the same sentence the About box uses, then the
/// price and the buttons. No badge, no comparison table, no countdown, no second sentence selling the
/// first. A user who never subscribes should be able to read this pane and feel they are looking at a
/// feature they do not need, not an ad they have to dismiss.
///
/// ### Three states, all of them sentences
///
/// - **Subscribed** — says so, with whatever renewal or expiry date StoreKit exposes, and offers
///   Manage. Restore is not shown: there is nothing to restore onto an account that is already
///   entitled here.
/// - **Not subscribed, product loaded** — the localized price and Subscribe, with Restore Purchases
///   beside it for the account that owns a subscription this Mac has never seen.
/// - **Not subscribed, App Store unreachable** — one quiet line and Try Again, never an indefinite
///   spinner (`ProStorefront`'s own note). Restore stays available: it is the button whose whole job
///   is to reach the App Store, so hiding it in the state where the App Store could not be reached
///   would remove the one thing worth pressing.
///
/// ### The reopen offer
///
/// "Subscribe takes effect at each board's next open ... The purchase flow offers to reopen open
/// boards so the upgrade feels immediate" (12 ▸ The entitlement). The offer is raised **once**, from
/// here, on the one outcome that means an active subscription just landed — and declining costs
/// nothing, today least of all: both tiers bind the native history stack until pro-m1 builds the git
/// provider (`AppModel.makeHistoryProvider`).
struct ProSettingsSection: View {

    @Environment(AppModel.self) private var appModel

    /// Built on appearance rather than at init, because the entitlement it wraps comes from the
    /// environment — and dropped when the pane goes, which is what keeps the networked half of Pro
    /// out of every other part of the app (`ProStorefront`'s note).
    @State private var storefront: ProStorefront?

    /// The reopen offer, raised at most once per activation.
    @State private var isOfferingReopen = false

    /// How many boards the offer was raised over. Captured when the offer is made, like the trash
    /// confirmations capture their counts (`TrashConfirmations`): the user is being asked about the
    /// boards that were open when they subscribed, and a count recomputed at render time could
    /// disagree with the sentence they are reading.
    @State private var openBoardCount = 0

    var body: some View {
        Section {
            content
        } header: {
            Text("Lanework Pro")
        } footer: {
            Text("Lanework Pro adds git-backed board history and sync. It takes effect the next time each board opens.")
        }
        .task {
            let storefront = storefront ?? ProStorefront(entitlement: appModel.entitlement)
            self.storefront = storefront
            await storefront.load()
        }
        .alert("Reopen your open boards?", isPresented: $isOfferingReopen) {
            Button("Reopen") {
                Task { await appModel.reopenOpenBoards() }
            }
            Button("Not Now", role: .cancel) {}
        } message: {
            Text(reopenMessage)
        }
    }

    // MARK: Content

    @ViewBuilder
    private var content: some View {
        if appModel.entitlement.tier == .pro {
            subscribedRows
        } else {
            unsubscribedRows
        }
    }

    /// The subscribed state. One status line and one button — the App Store owns everything else
    /// about a subscription (price changes, cancellation, billing), and duplicating any of it here
    /// would be a second, staler answer.
    @ViewBuilder
    private var subscribedRows: some View {
        // One `Text`, deliberately, rather than a status word beside a date: VoiceOver reads it as
        // the one sentence it is, and nothing about the state is carried by layout
        // (10-accessibility.md — nothing is ever said by position or colour alone).
        Text(subscriptionStatusLine)

        Button("Manage Subscription…") {
            storefront?.openManageSubscriptions()
        }
        .accessibilityHint("Opens your subscriptions in the App Store.")
    }

    @ViewBuilder
    private var unsubscribedRows: some View {
        switch storefront?.availability ?? .idle {
        case .idle, .loading:
            // A determinate, one-line placeholder rather than a progress spinner: the load is short,
            // and a spinner that resolves into a price reads as a slower price.
            Text("Checking the App Store…")
                .foregroundStyle(.secondary)
        case let .ready(product):
            HStack {
                Text(product.displayPrice)
                Spacer()
                Button("Subscribe") {
                    Task { await activate { await storefront?.subscribe() } }
                }
                .disabled(storefront?.isBusy ?? false)
                .accessibilityLabel("Subscribe to Lanework Pro, \(product.displayPrice)")
            }
        case .unreachable:
            Text("Can't reach the App Store right now.")
                .foregroundStyle(.secondary)
            Button("Try Again") {
                Task { await storefront?.load() }
            }
            .disabled(storefront?.isBusy ?? false)
        }

        Button("Restore Purchases") {
            Task { await activate { await storefront?.restore() } }
        }
        .disabled(storefront?.isBusy ?? false)
        .accessibilityHint("Checks this Apple Account for a Lanework Pro subscription.")

        if let message = storefront?.message {
            Text(message)
                .font(.caption)
                .foregroundStyle(.secondary)
                .fixedSize(horizontal: false, vertical: true)
        }
    }

    // MARK: Wording

    /// What a subscriber's status line says, built from exactly what StoreKit cached — and nothing
    /// more. An unknown expiry says the plain fact and stops, rather than inventing a date.
    private var subscriptionStatusLine: String {
        let facts = appModel.entitlement.facts
        guard let expiration = facts.expiration, expiration < .distantFuture else {
            return "Subscribed."
        }
        let date = expiration.formatted(date: .abbreviated, time: .omitted)
        return facts.willAutoRenew ? "Subscribed — renews \(date)." : "Subscribed — ends \(date)."
    }

    private var reopenMessage: String {
        let boards = openBoardCount == 1 ? "the board you have open" : "the \(openBoardCount) boards you have open"
        return "Pro takes effect at each board's next open. Lanework can close and reopen \(boards) now."
    }

    // MARK: Actions

    /// Runs a purchase or a restore and raises the reopen offer on the one outcome that earns it.
    ///
    /// The count is read here, before the alert exists, for the reason `TrashConfirmations` captures
    /// its phrasing at request time. And the offer is skipped outright when nothing is open, because
    /// an offer to reopen no boards is a dialog with nothing behind it.
    private func activate(_ work: () async -> ProStorefront.Outcome?) async {
        guard await work() == .activated else { return }
        let count = appModel.storeRegistry.openBoardCount
        guard count > 0 else { return }
        openBoardCount = count
        isOfferingReopen = true
    }
}
