import SwiftUI
import os

/// The launch-time restoration pass, wearing a window because that is the only place SwiftUI lets
/// work like this run.
///
/// ### Why a window at all
///
/// Restoration has to open windows, and opening a window needs `openWindow`, which is only readable
/// from a view. An `App.init()` cannot do it and `AppDelegate` has no environment. So the app
/// presents one throwaway window at launch — 1×1, plain, ordered straight back out, absent from the
/// Window menu — whose only job is to run the pass and then dismiss itself. It exists for a few
/// hundred milliseconds and never draws.
///
/// It is presented at **every** launch — it is the app's one reliable presenter (see `KanbanApp`'s
/// bootstrap scene for the macOS 26 behavior that forced this), so even the plain launch-to-welcome
/// path runs through it: the pass finds nothing flagged and opens welcome itself.
///
/// ### What the pass does
///
/// Reads the registry's flagged records in `lastOpened` order (`BoardRegistry.restorables()`), opens
/// the available ones, and records the unavailable ones as failures — 02 § Launch and window
/// lifecycle: "Other restorations proceed unaffected — never a launch-time modal chain, never a
/// silent drop." Welcome comes up only if nothing was even attempted; a board that *was* attempted
/// and then failed to load opens welcome from its own host, which is the same rule applied one layer
/// down and keeps this pass from having to wait on loads it did not perform.
///
/// ### And one other pass, for the same reason
///
/// The accessibility audit suite's fixture board (`UITestLaunch`) is built and opened here too. It is
/// the same job with a different source — filesystem work that must happen before the first real
/// window, needing `openWindow` to finish — and giving it a second throwaway window would be a second
/// copy of everything this file explains. Which pass runs is `plan`'s to say and nothing else's.
struct RestoreBootstrapView: View {

    /// Decided in `KanbanApp.init()`; this view only dispatches on it.
    let plan: LaunchPlan

    @Environment(AppModel.self) private var appModel
    @Environment(\.openWindow) private var openWindow
    @Environment(\.dismissWindow) private var dismissWindow

    @State private var windowController = HostedWindowController()

    private static let logger = Logger(subsystem: "dev.rzen.indie.Kanban", category: "launch")

    var body: some View {
        Color.clear
            .frame(width: 1, height: 1)
            .background(WindowAccessor(controller: windowController))
            .onAppear {
                // Out of sight before it can be seen. `orderOut` rather than a hidden style because
                // the scene must still exist — a window SwiftUI never presents never runs its task.
                windowController.onAttach = { window in
                    window.alphaValue = 0
                    window.orderOut(nil)
                }
                if let window = windowController.window {
                    windowController.onAttach?(window)
                }
            }
            .task { await restore() }
    }

    private func restore() async {
        // Captured directly rather than waiting for `CaptureOpenWindow`'s `onAppear`: this task is
        // the app's first act, and `openBoard` needs the action now. The count is a cold Finder-open
        // that arrived before this window did — a board already on its way to the screen, which the
        // pass below must count as an open or it would put welcome up beside the user's document.
        let replayedOpens = appModel.captureWindowActions(open: openWindow, dismiss: dismissWindow)

        switch plan {
        case .uiTestFixture:
            openFixtureBoard()
        case .restoreBoards, .welcome:
            // `.welcome` arrives here by design — this window presents at every launch, because it is
            // the app's one reliable presenter (see `KanbanApp`'s bootstrap scene) — and the pass is
            // its answer: nothing is flagged, so it shows welcome, which is what `.welcome` asked
            // for.
            restoreFlaggedBoards(openedAlready: replayedOpens)
        }

        dismissWindow(id: WindowID.restoreBootstrap)
    }

    private func restoreFlaggedBoards(openedAlready: Int) {
        var attempted = openedAlready
        for board in appModel.boardRegistry.restorables() {
            switch board {
            case let .available(_, url):
                // **The one restored open in the app** (01-storage-format.md § Malformed input, the
                // decision surface): a board that fails here keeps today's retire-to-welcome-row
                // landing — "launch never chains dialogs", and nobody is sitting in front of a
                // restoration waiting to repair four boards at once. The row's retry click is the
                // attended open that then shows the surface.
                appModel.openBoard(at: url, origin: .restored)
                attempted += 1
            case let .unavailable(record):
                Self.logger.error("a flagged board could not be restored — its bookmark no longer resolves")
                appModel.recordLaunchFailure(
                    path: record.lastKnownPath,
                    message: "This board is unavailable. Its volume may be offline, or it may have been moved or deleted."
                )
            }
        }

        if attempted == 0 {
            appModel.showWelcome()
        }
    }

    /// The UI suites' board: built here, opened through the same `openBoard` every other path uses,
    /// so it registers, bookmarks and titles itself exactly like a board the user opened.
    ///
    /// **Which board is the launch arguments' to say** (`UITestLaunch.variant`), and this method does
    /// not care: the malformed variant is built and opened exactly like the other two, and its
    /// failure arrives one layer down as the *loader's*. It opens **attended**, like every other
    /// board a person asks for, so its refusal transforms the loading window into the decision surface
    /// (`BoardWindowHost.handleWalkFailure`) rather than retiring — which is precisely what the
    /// fail-fast UI pass is there to see. Special-casing it here would replace the behaviour under
    /// test with a behaviour about the fixture.
    ///
    /// **A failure to *build* lands on welcome as an ordinary launch failure**, with the fixture's own
    /// path on it. That is deliberate: a suite whose fixture failed to build would otherwise audit an
    /// empty screen and pass, which is the one outcome an accessibility gate must never produce.
    private func openFixtureBoard() {
        let variant = UITestLaunch.variant
        do {
            let url = try UITestLaunch.materializeFixtureBoard(variant)
            appModel.openBoard(at: url)
        } catch {
            Self.logger.error("the UI-test fixture board could not be built: \(error.localizedDescription, privacy: .public)")
            appModel.recordLaunchFailure(
                path: UITestLaunch.fixtureBoardURL(for: variant).path,
                message: "The UI-test fixture board could not be built: \(error.localizedDescription)"
            )
            appModel.showWelcome()
        }
    }
}
