import BurrowConfiguration
import Foundation
import SwiftUI
#if canImport(AuthenticationServices)
import AuthenticationServices
#endif
#if canImport(UIKit)
import UIKit
#elseif canImport(AppKit)
import AppKit
#endif

public struct BurrowView: View {
    @State private var networkViewModel: NetworkViewModel
    @State private var accountStore = NetworkAccountStore()
    @State private var activeSheet: ConfigurationSheet?
    @State private var didRunAutomation = false

    public var body: some View {
        NavigationStack {
            ScrollView {
                VStack(alignment: .leading, spacing: 24) {
                    HStack(alignment: .top) {
                        VStack(alignment: .leading, spacing: 6) {
                            Text("Burrow")
                                .font(.largeTitle)
                                .fontWeight(.bold)
                            if showsHeaderSubtitle {
                                Text("Networks and accounts")
                                    .font(.headline)
                                    .foregroundStyle(.secondary)
                            }
                        }
                        if showsToolbarAddMenu {
                            Spacer()
                            Menu {
                                Button("Add WireGuard Network") {
                                    activeSheet = .wireGuard
                                }
                                Button("Save Tor Account") {
                                    activeSheet = .tor
                                }
                                Button("Add Tailnet Account") {
                                    activeSheet = .tailnet
                                }
                            } label: {
                                Image(systemName: "plus.circle.fill")
                                    .font(.title)
                                    .accessibilityLabel("Add")
                            }
                        }
                    }
                    .padding(.top)

                    if showsInlineQuickActions {
                        quickAddSection
                    }

                    VStack(alignment: .leading, spacing: 12) {
                        sectionHeader(
                            title: "Networks",
                            detail: showsInlineQuickActions
                                ? nil
                                : "Stored daemon networks and their active account selectors"
                        )
                        if let connectionError = networkViewModel.connectionError {
                            Text(connectionError)
                                .font(.footnote)
                                .foregroundStyle(.secondary)
                        }
                        NetworkCarouselView(networks: networkViewModel.cards)
                    }

                    if showsAccountsSection {
                        VStack(alignment: .leading, spacing: 12) {
                            sectionHeader(
                                title: "Accounts",
                                detail: showsInlineQuickActions
                                    ? nil
                                    : "Per-network identities and sign-in state"
                            )
                            if accountStore.accounts.isEmpty {
                                ContentUnavailableView(
                                    "No Accounts Yet",
                                    systemImage: "person.crop.circle.badge.plus",
                                    description: Text("Save a Tor account or sign in to Tailnet to keep network identities ready on this device.")
                                )
                                .frame(maxWidth: .infinity, minHeight: 180)
                            } else {
                                LazyVStack(spacing: 12) {
                                    ForEach(accountStore.accounts) { account in
                                        AccountRowView(
                                            account: account,
                                            hasSecret: accountStore.hasStoredSecret(for: account)
                                        )
                                    }
                                }
                            }
                        }
                    }

                    VStack(alignment: .leading, spacing: 8) {
                        sectionHeader(
                            title: "Tunnel",
                            detail: showsInlineQuickActions ? nil : "Current system extension state"
                        )
                        TunnelStatusView()
                        TunnelButton()
                            .padding(.bottom)
                    }
                }
                .padding()
            }
        }
        .sheet(item: $activeSheet) { sheet in
            ConfigurationSheetView(
                sheet: sheet,
                networkViewModel: networkViewModel,
                accountStore: accountStore
            )
        }
        .onAppear {
            runAutomationIfNeeded()
        }
    }

    public init() {
        _networkViewModel = State(
            initialValue: NetworkViewModel(
                socketURLResult: Result { try Constants.socketURL }
            )
        )
    }

    private func runAutomationIfNeeded() {
        guard !didRunAutomation,
              let automation = BurrowAutomationConfig.current,
              automation.action == .tailnetLogin || automation.action == .tailnetProbe
        else {
            return
        }
        didRunAutomation = true
        activeSheet = .tailnet
    }

    @ViewBuilder
    private var quickAddSection: some View {
        VStack(alignment: .leading, spacing: 12) {
            sectionHeader(title: "Add", detail: nil)
            VStack(spacing: 12) {
                ForEach(ConfigurationSheet.allCases) { sheet in
                    QuickAddButton(sheet: sheet) {
                        activeSheet = sheet
                    }
                }
            }
        }
    }

    @ViewBuilder
    private func sectionHeader(title: String, detail: String?) -> some View {
        VStack(alignment: .leading, spacing: 4) {
            Text(title)
                .font(.title2.weight(.semibold))
            if let detail, !detail.isEmpty {
                Text(detail)
                    .font(.subheadline)
                    .foregroundStyle(.secondary)
            }
        }
    }

    private var showsInlineQuickActions: Bool {
        #if os(iOS)
        true
        #else
        false
        #endif
    }

    private var showsToolbarAddMenu: Bool {
        !showsInlineQuickActions
    }

    private var showsHeaderSubtitle: Bool {
        !showsInlineQuickActions
    }

    private var showsAccountsSection: Bool {
        #if os(iOS)
        !accountStore.accounts.isEmpty
        #else
        true
        #endif
    }
}

private enum ConfigurationSheet: String, CaseIterable, Identifiable {
    case wireGuard
    case tor
    case tailnet

    var id: String { rawValue }

    var kind: AccountNetworkKind {
        switch self {
        case .wireGuard: .wireGuard
        case .tor: .tor
        case .tailnet: .tailnet
        }
    }

    var iconName: String {
        switch self {
        case .wireGuard:
            "wave.3.right"
        case .tor:
            "shield.lefthalf.filled.badge.checkmark"
        case .tailnet:
            "network.badge.shield.half.filled"
        }
    }

    var quickActionTitle: String {
        switch self {
        case .wireGuard:
            "WireGuard"
        case .tor:
            "Tor"
        case .tailnet:
            "Tailnet"
        }
    }

    var quickActionSubtitle: String {
        switch self {
        case .wireGuard:
            "Import a tunnel"
        case .tor:
            "Save an Arti profile"
        case .tailnet:
            "Sign in or save a control plane"
        }
    }

    var quickActionColor: Color {
        switch self {
        case .wireGuard:
            .blue
        case .tor, .tailnet:
            kind.accentColor
        }
    }
}

private struct QuickAddButton: View {
    let sheet: ConfigurationSheet
    let action: () -> Void

    var body: some View {
        Button(action: action) {
            HStack(spacing: 14) {
                Image(systemName: sheet.iconName)
                    .font(.title3.weight(.semibold))
                    .frame(width: 24)

                VStack(alignment: .leading, spacing: 4) {
                    Text(sheet.quickActionTitle)
                        .font(.headline)
                    Text(sheet.quickActionSubtitle)
                        .font(.caption)
                        .opacity(0.88)
                }

                Spacer()
            }
            .frame(maxWidth: .infinity, minHeight: 64, alignment: .leading)
        }
        .accessibilityIdentifier("quick-add-\(sheet.rawValue)")
        .buttonStyle(.floating(color: sheet.quickActionColor, cornerRadius: 18))
    }
}

private struct AccountDraft {
    var title = ""
    var accountName = ""
    var identityName = ""
    var wireGuardConfig = ""

    var discoveryEmail = ""
    var authority = ""
    var tailnet = ""
    var hostname = ProcessInfo.processInfo.hostName
    var username = ""
    var secret = ""
    var authMode: AccountAuthMode = .none

    var torAddresses = "100.64.0.2/32"
    var torDNS = "1.1.1.1, 1.0.0.1"
    var torMTU = "1400"
    var torListen = "127.0.0.1:9040"

    init(sheet: ConfigurationSheet) {
        switch sheet {
        case .wireGuard:
            break
        case .tor:
            title = "Default Tor"
            accountName = "default"
            identityName = "apple"
        case .tailnet:
            title = "Tailnet"
            accountName = "default"
            identityName = "apple"
            authority = TailnetProvider.tailscale.defaultAuthority ?? ""
            authMode = .web
        }
    }
}

private struct ConfigurationSheetView: View {
    @Environment(\.dismiss) private var dismiss

    let sheet: ConfigurationSheet
    let networkViewModel: NetworkViewModel
    let accountStore: NetworkAccountStore

    @State private var draft: AccountDraft
    @State private var isSubmitting = false
    @State private var errorMessage: String?
    @State private var discoveryStatus: TailnetDiscoveryResponse?
    @State private var discoveryError: String?
    @State private var isDiscoveringTailnet = false
    @State private var authorityProbeStatus: TailnetAuthorityProbeStatus?
    @State private var authorityProbeError: String?
    @State private var isProbingAuthority = false
    @State private var tailnetLoginStatus: TailnetLoginStatus?
    @State private var tailnetLoginError: String?
    @State private var tailnetLoginSessionID: String?
    @State private var isStartingTailnetLogin = false
    @State private var tailnetPresentedAuthURL: URL?
    @State private var preserveTailnetLoginSession = false
    @State private var usesCustomTailnetAuthority = false
    @State private var showsAdvancedTailnetSettings = false
    @State private var browserAuthenticator = TailnetBrowserAuthenticator()
    @State private var tailnetLoginPollTask: Task<Void, Never>?
    @State private var tailnetDiscoveryTask: Task<Void, Never>?
    @State private var tailnetProbeTask: Task<Void, Never>?
    @State private var didRunAutomation = false

    init(
        sheet: ConfigurationSheet,
        networkViewModel: NetworkViewModel,
        accountStore: NetworkAccountStore
    ) {
        self.sheet = sheet
        self.networkViewModel = networkViewModel
        self.accountStore = accountStore
        _draft = State(initialValue: AccountDraft(sheet: sheet))
    }

    var body: some View {
        NavigationStack {
            Form {
                Section {
                    sheetSummaryCard
                }
                .listRowInsets(.init(top: 4, leading: 0, bottom: 4, trailing: 0))
                .listRowBackground(Color.clear)

                if showsIdentitySection {
                    Section("Identity") {
                        identityFields
                    }
                }

                switch sheet {
                case .wireGuard:
                    Section("WireGuard Configuration") {
                        TextEditor(text: $draft.wireGuardConfig)
                            .font(.body.monospaced())
                            .frame(minHeight: wireGuardEditorHeight)
                            .contextMenu {
                                wireGuardContextActions
                            }
                    }
                case .tor:
                    Section("Tor Preferences") {
                        TextField("Virtual Addresses", text: $draft.torAddresses)
                        TextField("DNS Resolvers", text: $draft.torDNS)
                        TextField("MTU", text: $draft.torMTU)
                        TextField("Transparent Listener", text: $draft.torListen)
                    }
                case .tailnet:
                    tailnetSections
                }

                if let errorMessage {
                    Section {
                        Text(errorMessage)
                            .foregroundStyle(.red)
                    }
                }
            }
            .navigationTitle(sheet.kind.title)
            #if os(iOS)
            .navigationBarTitleDisplayMode(.inline)
            #endif
            .toolbar {
                ToolbarItem(placement: .cancellationAction) {
                    Button("Cancel") {
                        Task { @MainActor in
                            await cancelTailnetLoginIfNeeded()
                            dismiss()
                        }
                    }
                }
                #if os(iOS)
                ToolbarItem(placement: .topBarTrailing) {
                    Menu {
                        sheetMenuActions
                    } label: {
                        Image(systemName: "ellipsis.circle")
                    }
                    .accessibilityLabel("More")
                }
                #else
                ToolbarItem(placement: .primaryAction) {
                    Menu {
                        sheetMenuActions
                    } label: {
                        Image(systemName: "ellipsis.circle")
                    }
                    .accessibilityLabel("More")
                }
                #endif
                if !showsBottomActionButton {
                    ToolbarItem(placement: .confirmationAction) {
                        Button(confirmationTitle) {
                            submit()
                        }
                        .disabled(isSubmitting || submissionDisabled)
                    }
                }
            }
        }
        #if os(macOS)
        .frame(minWidth: 520, minHeight: 620)
        #endif
        .safeAreaInset(edge: .bottom) {
            if showsBottomActionButton {
                bottomActionBar
            }
        }
        .onAppear {
            runAutomationIfNeeded()
        }
        .onChange(of: draft.authority) { _, _ in
            resetAuthorityProbe()
            if sheet == .tailnet, usesCustomTailnetAuthority {
                scheduleTailnetAuthorityProbe()
            }
        }
        .onChange(of: draft.discoveryEmail) { _, _ in
            resetTailnetDiscoveryFeedback()
            if sheet == .tailnet, !usesCustomTailnetAuthority {
                scheduleTailnetDiscovery()
            }
        }
        .onChange(of: draft.authMode) { _, newMode in
            guard newMode != .web else { return }
            Task { @MainActor in
                await cancelTailnetLoginIfNeeded()
            }
        }
        .onDisappear {
            tailnetLoginPollTask?.cancel()
            tailnetDiscoveryTask?.cancel()
            tailnetProbeTask?.cancel()
            browserAuthenticator.cancel()
            if !preserveTailnetLoginSession {
                Task { @MainActor in
                    await cancelTailnetLoginIfNeeded()
                }
            }
        }
    }

    @ViewBuilder
    private var identityFields: some View {
        TextField("Title", text: $draft.title)
        TextField("Account", text: $draft.accountName)
        TextField("Identity", text: $draft.identityName)
        if sheet == .tailnet {
            TextField("Hostname", text: $draft.hostname)
                .burrowLoginField()
                .autocorrectionDisabled()
        }
    }

    @ViewBuilder
    private var tailnetSections: some View {
        Section("Connection") {
            TextField("Email address", text: $draft.discoveryEmail)
                .burrowEmailField()
                .burrowLoginField()
                .autocorrectionDisabled()
                .accessibilityIdentifier("tailnet-discovery-email")
            .submitLabel(.continue)
            .onSubmit {
                if !usesCustomTailnetAuthority {
                    scheduleTailnetDiscovery(immediate: true)
                }
            }

            tailnetServerCard

            if showsAdvancedTailnetSettings {
                if usesCustomTailnetAuthority {
                    TextField("Server URL", text: $draft.authority)
                        .burrowLoginField()
                        .autocorrectionDisabled()
                        .accessibilityIdentifier("tailnet-authority")
                } else {
                    TextField("Tailnet", text: $draft.tailnet)
                        .burrowLoginField()
                        .autocorrectionDisabled()
                        .accessibilityIdentifier("tailnet-name")
                }
            }
        }

        Section("Authentication") {
            if showsAdvancedTailnetSettings {
                Picker("Authentication", selection: $draft.authMode) {
                    ForEach(availableTailnetAuthModes) { mode in
                        Text(mode.title).tag(mode)
                    }
                }
                .pickerStyle(.menu)
            }

            if draft.authMode == .web {
                Button {
                    startTailnetLogin()
                } label: {
                    Label {
                        Text(isStartingTailnetLogin ? "Starting Sign-In" : tailnetSignInActionTitle)
                    } icon: {
                        Image(systemName: isStartingTailnetLogin ? "hourglass" : "person.badge.key")
                    }
                }
                .buttonStyle(.borderless)
                .disabled(isStartingTailnetLogin || tailnetLoginActionDisabled)
                .accessibilityIdentifier("tailnet-start-sign-in")

                if let tailnetLoginStatus {
                    tailnetLoginCard(status: tailnetLoginStatus, failure: nil)
                } else if let tailnetLoginError {
                    tailnetLoginCard(status: nil, failure: tailnetLoginError)
                }
            } else {
                TextField("Username", text: $draft.username)
                    .burrowLoginField()
                    .autocorrectionDisabled()
                if draft.authMode != .none {
                    SecureField(
                        draft.authMode == .password ? "Password" : "Preauth Key",
                        text: $draft.secret
                    )
                }
            }

            Text(tailnetAuthenticationFootnote)
                .font(.footnote)
                .foregroundStyle(.secondary)
        }
    }

    private var sheetSummaryCard: some View {
        VStack(alignment: .leading, spacing: 10) {
            HStack(spacing: 12) {
                Image(systemName: sheet.iconName)
                    .font(.title3.weight(.semibold))
                    .foregroundStyle(sheetAccentColor)
                    .frame(width: 28, height: 28)
                    .background(
                        Circle()
                            .fill(sheetAccentColor.opacity(0.14))
                    )

                VStack(alignment: .leading, spacing: 3) {
                    Text(summaryTitle)
                        .font(.headline)
                    Text(sheet.kind.subtitle)
                        .font(.footnote)
                        .foregroundStyle(.secondary)
                }

                Spacer()
            }

            if let availabilityNote = sheet.kind.availabilityNote {
                Text(availabilityNote)
                    .font(.footnote)
                    .foregroundStyle(.secondary)
            }

            if sheet == .tailnet {
                labeledValue("Server", tailnetServerDisplayLabel)
                if let connectionSummary = tailnetConnectionSummary {
                    Text(connectionSummary)
                        .font(.footnote.weight(.medium))
                        .foregroundStyle(tailnetConnectionSummaryColor)
                }
                if tailnetLoginStatus?.running == true {
                    HStack(spacing: 8) {
                        summaryBadge("Signed In")
                    }
                }
            }
        }
        .padding(14)
        .background(
            RoundedRectangle(cornerRadius: 18)
                .fill(.thinMaterial)
        )
    }

    private var tailnetServerCard: some View {
        VStack(alignment: .leading, spacing: 8) {
            HStack(alignment: .top, spacing: 12) {
                VStack(alignment: .leading, spacing: 4) {
                    Text(usesCustomTailnetAuthority ? "Custom Server" : "Server")
                        .font(.subheadline.weight(.medium))
                    Text(tailnetServerDisplayLabel)
                        .font(.footnote.monospaced())
                        .foregroundStyle(.secondary)
                        .textSelection(.enabled)
                }

                Spacer()

                if isDiscoveringTailnet || isProbingAuthority {
                    ProgressView()
                        .controlSize(.small)
                } else if let summary = tailnetConnectionSummary {
                    Text(summary)
                        .font(.caption.weight(.medium))
                        .foregroundStyle(tailnetConnectionSummaryColor)
                }
            }

            if let detail = tailnetServerDetail {
                Text(detail)
                    .font(.footnote)
                    .foregroundStyle(.secondary)
            }
        }
        .padding(12)
        .background(
            RoundedRectangle(cornerRadius: 16)
                .fill(.thinMaterial)
        )
        .accessibilityIdentifier("tailnet-server-card")
    }

    private func tailnetAuthorityProbeCard(
        status: TailnetAuthorityProbeStatus?,
        failure: String?
    ) -> some View {
        VStack(alignment: .leading, spacing: 6) {
            if let status {
                Text(status.summary)
                    .font(.subheadline.weight(.medium))
                Text(status.detail ?? "HTTP \(status.statusCode) from \(status.authority)")
                    .font(.footnote)
                    .foregroundStyle(.secondary)
                    .textSelection(.enabled)
            } else if let failure {
                Text("Connection failed")
                    .font(.subheadline.weight(.medium))
                    .foregroundStyle(.red)
                Text(failure)
                    .font(.footnote)
                    .foregroundStyle(.secondary)
            }
        }
        .padding(12)
        .background(
            RoundedRectangle(cornerRadius: 16)
                .fill(.thinMaterial)
        )
        .accessibilityIdentifier("tailnet-authority-probe-card")
    }

    private func tailnetDiscoveryCard(
        status: TailnetDiscoveryResponse?,
        failure: String?
    ) -> some View {
        VStack(alignment: .leading, spacing: 6) {
            if let status {
                Text("Discovered Tailnet Server")
                    .font(.subheadline.weight(.medium))
                Text(status.authority)
                    .font(.footnote.monospaced())
                    .foregroundStyle(.secondary)
                    .textSelection(.enabled)
                Text(status.provider == .tailscale ? "Managed authority" : "Custom authority")
                    .font(.footnote)
                    .foregroundStyle(.secondary)
                if let oidcIssuer = status.oidcIssuer {
                    Text("OIDC: \(oidcIssuer)")
                        .font(.footnote)
                        .foregroundStyle(.secondary)
                        .lineLimit(3)
                        .textSelection(.enabled)
                }
            } else if let failure {
                Text("Discovery failed")
                    .font(.subheadline.weight(.medium))
                    .foregroundStyle(.red)
                Text(failure)
                    .font(.footnote)
                    .foregroundStyle(.secondary)
            }
        }
        .padding(12)
        .background(
            RoundedRectangle(cornerRadius: 16)
                .fill(.thinMaterial)
        )
        .accessibilityIdentifier("tailnet-discovery-card")
    }

    private func tailnetLoginCard(
        status: TailnetLoginStatus?,
        failure: String?
    ) -> some View {
        VStack(alignment: .leading, spacing: 6) {
            if let status {
                Text(status.running ? "Signed In" : status.needsLogin ? "Browser Sign-In Required" : "Checking Sign-In")
                    .font(.subheadline.weight(.medium))
                if let tailnetName = status.tailnetName, !tailnetName.isEmpty {
                    Text("Tailnet: \(tailnetName)")
                        .font(.footnote)
                        .foregroundStyle(.secondary)
                }
                if let selfDNSName = status.selfDNSName, !selfDNSName.isEmpty {
                    Text(selfDNSName)
                        .font(.footnote.monospaced())
                        .foregroundStyle(.secondary)
                        .textSelection(.enabled)
                }
                if !status.tailnetIPs.isEmpty {
                    Text(status.tailnetIPs.joined(separator: ", "))
                        .font(.footnote.monospaced())
                        .foregroundStyle(.secondary)
                        .textSelection(.enabled)
                }
                if !status.health.isEmpty {
                    Text(status.health.joined(separator: " • "))
                        .font(.footnote)
                        .foregroundStyle(.secondary)
                }
            } else if let failure {
                Text("Sign-In failed")
                    .font(.subheadline.weight(.medium))
                    .foregroundStyle(.red)
                Text(failure)
                    .font(.footnote)
                    .foregroundStyle(.secondary)
            }
        }
        .padding(12)
        .background(
            RoundedRectangle(cornerRadius: 16)
                .fill(.thinMaterial)
        )
        .accessibilityIdentifier("tailnet-login-card")
    }

    private func summaryBadge(_ label: String) -> some View {
        Text(label)
            .font(.caption.weight(.medium))
            .foregroundStyle(.secondary)
            .padding(.horizontal, 10)
            .padding(.vertical, 5)
            .background(
                Capsule()
                    .fill(.white.opacity(0.5))
            )
    }

    @ViewBuilder
    private var bottomActionBar: some View {
        VStack(spacing: 0) {
            Divider()
                .overlay(.white.opacity(0.3))
            Button(confirmationTitle) {
                submit()
            }
            .buttonStyle(.floating(color: sheetAccentColor, cornerRadius: 18))
            .disabled(isSubmitting || submissionDisabled)
            .padding(.horizontal)
            .padding(.top, 12)
            .padding(.bottom, 8)
        }
        .background(.ultraThinMaterial)
    }

    @ViewBuilder
    private var sheetMenuActions: some View {
        Button("Use Suggested Identity") {
            applySuggestedIdentity()
        }

        switch sheet {
        case .wireGuard:
            Button("Paste Configuration") {
                pasteWireGuardConfiguration()
            }
            .disabled(clipboardString?.isEmpty ?? true)

            Button("Clear Configuration", role: .destructive) {
                draft.wireGuardConfig = ""
            }
            .disabled(draft.wireGuardConfig.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty)

        case .tor:
            Menu("Presets") {
                Button("Recommended Tor Defaults") {
                    applyTorDefaults()
                }
                Button("Restore Suggested Identity") {
                    applySuggestedIdentity()
                }
            }

        case .tailnet:
            Button(usesCustomTailnetAuthority ? "Use Automatic Server" : "Edit Custom Server") {
                toggleTailnetAuthorityMode()
            }

            Button(showsAdvancedTailnetSettings ? "Hide Advanced Settings" : "Show Advanced Settings") {
                showsAdvancedTailnetSettings.toggle()
            }

            if showsAdvancedTailnetSettings, availableTailnetAuthModes.count > 1 {
                Menu("Authentication") {
                    ForEach(availableTailnetAuthModes) { mode in
                        Button(mode.title) {
                            draft.authMode = mode
                            if mode == .none {
                                draft.secret = ""
                            }
                        }
                    }
                }
            }

            Button("Refresh Server Lookup") {
                scheduleTailnetDiscovery(immediate: true)
            }
            .disabled(usesCustomTailnetAuthority || normalizedOptional(draft.discoveryEmail) == nil)
        }
    }

    @ViewBuilder
    private var wireGuardContextActions: some View {
        Button("Paste Configuration") {
            pasteWireGuardConfiguration()
        }
        .disabled(clipboardString?.isEmpty ?? true)

        Button("Clear", role: .destructive) {
            draft.wireGuardConfig = ""
        }
        .disabled(draft.wireGuardConfig.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty)
    }

    private var sheetAccentColor: Color {
        switch sheet {
        case .wireGuard:
            .blue
        case .tor, .tailnet:
            sheet.kind.accentColor
        }
    }

    private var summaryTitle: String {
        switch sheet {
        case .wireGuard:
            "Import WireGuard"
        case .tor:
            "Configure Tor"
        case .tailnet:
            "Connect Tailnet"
        }
    }

    private var showsBottomActionButton: Bool {
        #if os(iOS)
        return true
        #else
        return false
        #endif
    }

    private var showsIdentitySection: Bool {
        switch sheet {
        case .wireGuard, .tor:
            return true
        case .tailnet:
            return showsAdvancedTailnetSettings
        }
    }

    private var wireGuardEditorHeight: CGFloat {
        #if os(iOS)
        180
        #else
        220
        #endif
    }

    private var confirmationTitle: String {
        switch sheet {
        case .wireGuard:
            return "Add Network"
        case .tor:
            return "Save Account"
        case .tailnet:
            return "Save Account"
        }
    }

    private var tailnetLoginActionDisabled: Bool {
        switch sheet {
        case .tailnet:
            if usesCustomTailnetAuthority {
                return normalizedOptional(draft.authority) == nil
            }
            return false
        case .wireGuard, .tor:
            return true
        }
    }

    private var submissionDisabled: Bool {
        switch sheet {
        case .wireGuard:
            return draft.wireGuardConfig.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
        case .tor:
            return normalizedOptional(draft.accountName) == nil || normalizedOptional(draft.identityName) == nil
        case .tailnet:
            if normalizedOptional(draft.accountName) == nil || normalizedOptional(draft.identityName) == nil {
                return true
            }
            if normalizedOptional(draft.authority) == nil {
                return true
            }
            if draft.authMode == .web {
                return tailnetLoginStatus?.running != true
            }
            if draft.authMode != .none && normalizedOptional(draft.secret) == nil {
                return true
            }
            return false
        }
    }

    private var tailnetServerDisplayLabel: String {
        if usesCustomTailnetAuthority {
            return normalizedOptional(draft.authority)
                ?? "Enter a custom Tailnet server"
        }
        return TailnetProvider.tailscale.defaultAuthority ?? "Tailscale managed"
    }

    private var tailnetServerDetail: String? {
        if usesCustomTailnetAuthority {
            if let discovery = discoveryStatus {
                return "Discovered from \(discovery.domain)."
            }
            if let discoveryError {
                return discoveryError
            }
            return "Use a custom Tailnet authority when your domain does not advertise one."
        }
        return "Continue with Tailscale, or open advanced settings to use a custom server."
    }

    private var tailnetConnectionSummary: String? {
        if isDiscoveringTailnet {
            return "Finding server"
        }
        if isProbingAuthority {
            return "Checking"
        }
        if let authorityProbeStatus {
            return authorityProbeStatus.summary
        }
        if authorityProbeError != nil {
            return "Unavailable"
        }
        return nil
    }

    private var tailnetConnectionSummaryColor: Color {
        if authorityProbeError != nil {
            return .red
        }
        return .secondary
    }

    private func submit() {
        isSubmitting = true
        errorMessage = nil

        Task { @MainActor in
            defer { isSubmitting = false }
            do {
                switch sheet {
                case .wireGuard:
                    try await submitWireGuard()
                    dismiss()
                case .tor:
                    try submitTor()
                    dismiss()
                case .tailnet:
                    try await submitTailnet()
                }
            } catch {
                errorMessage = error.localizedDescription
            }
        }
    }

    private func submitWireGuard() async throws {
        let networkID = try await networkViewModel.addWireGuardNetwork(
            configText: draft.wireGuardConfig
        )

        let title = titleOrFallback("WireGuard \(networkID)")
        let record = NetworkAccountRecord(
            id: UUID(),
            kind: .wireGuard,
            title: title,
            authority: nil,
            provider: nil,
            accountName: normalized(draft.accountName, fallback: "default"),
            identityName: normalized(draft.identityName, fallback: "network-\(networkID)"),
            hostname: nil,
            username: nil,
            tailnet: nil,
            authMode: .none,
            note: "Linked to daemon network #\(networkID).",
            createdAt: .now,
            updatedAt: .now
        )
        try accountStore.upsert(record, secret: nil)
    }

    private func submitTor() throws {
        let title = titleOrFallback("Tor \(normalized(draft.identityName, fallback: "apple"))")
        let note = [
            "Addresses: \(csvSummary(draft.torAddresses))",
            "DNS: \(csvSummary(draft.torDNS))",
            "MTU: \(normalized(draft.torMTU, fallback: "1400"))",
            "Listen: \(normalized(draft.torListen, fallback: "127.0.0.1:9040"))",
        ].joined(separator: " • ")

        let record = NetworkAccountRecord(
            id: UUID(),
            kind: .tor,
            title: title,
            authority: "arti://local",
            provider: nil,
            accountName: normalized(draft.accountName, fallback: "default"),
            identityName: normalized(draft.identityName, fallback: "apple"),
            hostname: nil,
            username: nil,
            tailnet: nil,
            authMode: .none,
            note: note,
            createdAt: .now,
            updatedAt: .now
        )
        try accountStore.upsert(record, secret: nil)
    }

    private func submitTailnet() async throws {
        let secret = (draft.authMode == .none || draft.authMode == .web) ? nil : draft.secret
        let username = normalizedOptional(draft.username)
        preserveTailnetLoginSession = draft.authMode == .web && tailnetLoginStatus?.running == true
        try await saveTailnetAccount(secret: secret, username: username)
        dismiss()
    }

    private func runAutomationIfNeeded() {
        guard !didRunAutomation,
              sheet == .tailnet,
              let automation = BurrowAutomationConfig.current,
              automation.action == .tailnetLogin || automation.action == .tailnetProbe
        else {
            return
        }

        didRunAutomation = true
        draft.title = automation.title ?? draft.title
        draft.accountName = automation.accountName ?? draft.accountName
        draft.identityName = automation.identityName ?? draft.identityName
        draft.hostname = automation.hostname ?? draft.hostname

        Task { @MainActor in
            switch automation.action {
            case .tailnetLogin:
                applyTailnetDefaults(for: .tailscale)
                startTailnetLogin()
            case .tailnetProbe:
                usesCustomTailnetAuthority = true
                showsAdvancedTailnetSettings = true
                draft.authority = automation.authority ?? TailnetProvider.headscale.defaultAuthority ?? draft.authority
                probeTailnetAuthority()
            }
        }
    }

    private func saveTailnetAccount(secret: String?, username: String?) async throws {
        let provider = inferredTailnetProvider
        let title = titleOrFallback(
            hostnameFallback(from: draft.authority, fallback: "Tailnet")
        )

        let payload = TailnetNetworkPayload(
            provider: provider,
            authority: normalizedOptional(draft.authority) ?? normalizedOptional(provider.defaultAuthority ?? ""),
            account: normalized(draft.accountName, fallback: "default"),
            identity: normalized(draft.identityName, fallback: "apple"),
            tailnet: normalizedOptional(draft.tailnet),
            hostname: normalizedOptional(draft.hostname)
        )

        var noteParts: [String] = [
            "Server: \(hostnameFallback(from: payload.authority ?? "", fallback: "tailnet"))",
        ]

        if showsAdvancedTailnetSettings || draft.authMode != .web {
            noteParts.append("Auth: \(draft.authMode.title)")
        }

        if draft.authMode == .web, tailnetLoginStatus?.running == true {
            noteParts.append("Browser sign-in complete")
        }

        do {
            let networkID = try await networkViewModel.addTailnetNetwork(payload: payload)
            noteParts.append("Linked to daemon network #\(networkID)")
        } catch {
            noteParts.append("Daemon network add pending")
        }

        let record = NetworkAccountRecord(
            id: UUID(),
            kind: .tailnet,
            title: title,
            authority: payload.authority,
            provider: provider,
            accountName: payload.account,
            identityName: payload.identity,
            hostname: payload.hostname,
            username: username,
            tailnet: payload.tailnet,
            authMode: draft.authMode,
            note: noteParts.joined(separator: " • "),
            createdAt: .now,
            updatedAt: .now
        )
        try accountStore.upsert(record, secret: secret)
    }

    private func applySuggestedIdentity() {
        let defaults = AccountDraft(sheet: sheet)
        if draft.title.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
            draft.title = defaults.title
        }
        draft.accountName = defaults.accountName
        draft.identityName = defaults.identityName
        if sheet == .tailnet {
            draft.hostname = defaults.hostname
        }
    }

    private func applyTorDefaults() {
        let defaults = AccountDraft(sheet: .tor)
        draft.title = defaults.title
        draft.accountName = defaults.accountName
        draft.identityName = defaults.identityName
        draft.torAddresses = defaults.torAddresses
        draft.torDNS = defaults.torDNS
        draft.torMTU = defaults.torMTU
        draft.torListen = defaults.torListen
    }

    private func applyTailnetDefaults(for provider: TailnetProvider) {
        resetTailnetDiscoveryFeedback()
        usesCustomTailnetAuthority = provider != .tailscale
        draft.authority = provider.defaultAuthority ?? ""
        if !availableTailnetAuthModes.contains(draft.authMode) {
            draft.authMode = .web
        }
    }

    private func startTailnetLogin() {
        isStartingTailnetLogin = true
        tailnetLoginError = nil
        preserveTailnetLoginSession = false

        Task { @MainActor in
            defer { isStartingTailnetLogin = false }
            do {
                let authority = try await resolveTailnetAuthorityForLogin()
                let status = try await networkViewModel.startTailnetLogin(
                    accountName: normalized(draft.accountName, fallback: "default"),
                    identityName: normalized(draft.identityName, fallback: "apple"),
                    hostname: normalizedOptional(draft.hostname),
                    authority: authority
                )
                tailnetLoginSessionID = status.sessionID
                updateTailnetLoginStatus(status)
                beginTailnetLoginPolling(sessionID: status.sessionID)
            } catch {
                tailnetLoginError = error.localizedDescription
            }
        }
    }

    private func probeTailnetAuthority() {
        guard let authority = normalizedOptional(draft.authority) else {
            authorityProbeStatus = nil
            authorityProbeError = "Enter a server URL first."
            return
        }

        isProbingAuthority = true
        authorityProbeStatus = nil
        authorityProbeError = nil

        Task { @MainActor in
            defer { isProbingAuthority = false }
            do {
                authorityProbeStatus = try await networkViewModel.probeTailnetAuthority(authority)
            } catch {
                authorityProbeError = error.localizedDescription
            }
        }
    }

    private func resetAuthorityProbe() {
        tailnetProbeTask?.cancel()
        authorityProbeStatus = nil
        authorityProbeError = nil
        tailnetLoginError = nil
    }

    private func resetTailnetDiscoveryFeedback() {
        tailnetDiscoveryTask?.cancel()
        discoveryStatus = nil
        discoveryError = nil
    }

    private func discoverTailnetAuthority() {
        guard let email = normalizedOptional(draft.discoveryEmail) else {
            discoveryStatus = nil
            discoveryError = "Enter an email address first."
            return
        }

        isDiscoveringTailnet = true
        discoveryStatus = nil
        discoveryError = nil

        Task { @MainActor in
            defer { isDiscoveringTailnet = false }
            do {
                let discovery = try await networkViewModel.discoverTailnet(email: email)
                discoveryStatus = discovery
                draft.authority = discovery.authority
                probeTailnetAuthority()
            } catch {
                discoveryError = error.localizedDescription
            }
        }
    }

    private func scheduleTailnetDiscovery(immediate: Bool = false) {
        guard sheet == .tailnet else { return }
        tailnetDiscoveryTask?.cancel()

        guard !usesCustomTailnetAuthority else {
            discoveryStatus = nil
            discoveryError = nil
            return
        }

        guard normalizedOptional(draft.discoveryEmail) != nil else {
            discoveryStatus = nil
            discoveryError = nil
            draft.authority = TailnetProvider.tailscale.defaultAuthority ?? ""
            return
        }

        tailnetDiscoveryTask = Task { @MainActor in
            if !immediate {
                try? await Task.sleep(for: .milliseconds(450))
            }
            guard !Task.isCancelled else { return }
            discoverTailnetAuthority()
        }
    }

    private func scheduleTailnetAuthorityProbe() {
        guard sheet == .tailnet else { return }
        tailnetProbeTask?.cancel()
        guard normalizedOptional(draft.authority) != nil else { return }

        tailnetProbeTask = Task { @MainActor in
            try? await Task.sleep(for: .milliseconds(300))
            guard !Task.isCancelled else { return }
            probeTailnetAuthority()
        }
    }

    private func toggleTailnetAuthorityMode() {
        let discoveredAuthority = discoveryStatus?.authority
        usesCustomTailnetAuthority.toggle()
        resetTailnetDiscoveryFeedback()
        resetAuthorityProbe()
        if usesCustomTailnetAuthority {
            draft.authority = discoveredAuthority ?? draft.authority
        } else {
            draft.authority = TailnetProvider.tailscale.defaultAuthority ?? ""
            scheduleTailnetDiscovery(immediate: normalizedOptional(draft.discoveryEmail) != nil)
        }
    }

    private func resolveTailnetAuthorityForLogin() async throws -> String {
        if !usesCustomTailnetAuthority {
            let authority = TailnetProvider.tailscale.defaultAuthority ?? ""
            draft.authority = authority
            scheduleTailnetAuthorityProbe()
            return authority
        }

        if let authority = normalizedOptional(draft.authority) {
            return authority
        }

        if let email = normalizedOptional(draft.discoveryEmail) {
            let discovery = try await networkViewModel.discoverTailnet(email: email)
            discoveryStatus = discovery
            discoveryError = nil
            draft.authority = discovery.authority
            scheduleTailnetAuthorityProbe()
            return discovery.authority
        }

        throw NSError(domain: "BurrowTailnet", code: 1, userInfo: [
            NSLocalizedDescriptionKey: "Enter an email address or a custom server URL first."
        ])
    }

    private func beginTailnetLoginPolling(sessionID: String) {
        tailnetLoginPollTask?.cancel()
        tailnetLoginPollTask = Task { @MainActor in
            while !Task.isCancelled {
                do {
                    let status = try await networkViewModel.tailnetLoginStatus(sessionID: sessionID)
                    updateTailnetLoginStatus(status)
                    if status.running {
                        tailnetLoginPollTask = nil
                        return
                    }
                } catch {
                    tailnetLoginError = error.localizedDescription
                    tailnetLoginPollTask = nil
                    return
                }
                try? await Task.sleep(for: .seconds(1))
            }
        }
    }

    private func updateTailnetLoginStatus(_ status: TailnetLoginStatus) {
        tailnetLoginStatus = status
        tailnetLoginError = nil
        tailnetLoginSessionID = status.sessionID

        if status.running {
            browserAuthenticator.cancel()
            tailnetPresentedAuthURL = nil
            return
        }

        guard let authURL = status.authURL else {
            return
        }

        if tailnetPresentedAuthURL != authURL {
            tailnetPresentedAuthURL = authURL
            browserAuthenticator.start(url: authURL) { [sessionID = status.sessionID] in
                Task { @MainActor in
                    if tailnetLoginStatus?.running != true {
                        tailnetLoginSessionID = sessionID
                    }
                }
            }
        }
    }

    private func cancelTailnetLoginIfNeeded() async {
        tailnetLoginPollTask?.cancel()
        tailnetLoginPollTask = nil
        browserAuthenticator.cancel()
        tailnetPresentedAuthURL = nil

        guard tailnetLoginStatus?.running != true,
              let sessionID = tailnetLoginSessionID
        else {
            return
        }

        do {
            try await networkViewModel.cancelTailnetLogin(sessionID: sessionID)
        } catch {
            tailnetLoginError = error.localizedDescription
        }

        tailnetLoginStatus = nil
        tailnetLoginSessionID = nil
    }

    private func pasteWireGuardConfiguration() {
        guard let clipboardString else { return }
        draft.wireGuardConfig = clipboardString
    }

    private var clipboardString: String? {
        #if canImport(UIKit)
        UIPasteboard.general.string
        #elseif canImport(AppKit)
        NSPasteboard.general.string(forType: .string)
        #else
        nil
        #endif
    }

    private func normalized(_ value: String, fallback: String) -> String {
        let trimmed = value.trimmingCharacters(in: .whitespacesAndNewlines)
        return trimmed.isEmpty ? fallback : trimmed
    }

    private func normalizedOptional(_ value: String) -> String? {
        let trimmed = value.trimmingCharacters(in: .whitespacesAndNewlines)
        return trimmed.isEmpty ? nil : trimmed
    }

    private func titleOrFallback(_ fallback: String) -> String {
        normalized(draft.title, fallback: fallback)
    }

    private func csvSummary(_ value: String) -> String {
        value
            .split(separator: ",")
            .map { $0.trimmingCharacters(in: .whitespacesAndNewlines) }
            .filter { !$0.isEmpty }
            .joined(separator: ", ")
    }

    private func hostnameFallback(from value: String, fallback: String) -> String {
        guard let url = URL(string: value), let host = url.host, !host.isEmpty else {
            let trimmed = value.trimmingCharacters(in: .whitespacesAndNewlines)
            return trimmed.isEmpty ? fallback : trimmed
        }
        return host
    }

    private var availableTailnetAuthModes: [AccountAuthMode] {
        [.web, .none, .password, .preauthKey]
    }

    private var tailnetSignInActionTitle: String {
        if tailnetLoginStatus?.running == true {
            return "Signed In"
        }
        if tailnetLoginSessionID != nil {
            return "Resume Sign-In"
        }
        return "Continue with Tailscale"
    }

    private var tailnetAuthenticationFootnote: String {
        switch draft.authMode {
        case .web:
            if usesCustomTailnetAuthority {
                return "Burrow signs in through the daemon using your custom Tailnet server."
            }
            return "Burrow signs in through the daemon using Tailscale's managed browser flow."
        case .none:
            return "Save the authority only. Useful when the control plane handles authentication elsewhere."
        case .password, .preauthKey:
            return "Tailnet account material stays on-device. Burrow stores the authority and credentials for daemon-managed registration and refresh."
        }
    }

    private var inferredTailnetProvider: TailnetProvider {
        TailnetProvider.inferred(
            authority: normalizedOptional(draft.authority),
            explicit: discoveryStatus?.provider
        )
    }

    @ViewBuilder
    private func labeledValue(_ label: String, _ value: String) -> some View {
        VStack(alignment: .leading, spacing: 2) {
            Text(label)
                .font(.caption)
                .foregroundStyle(.secondary)
            Text(value)
                .font(.body.monospaced())
        }
    }
}

private struct AccountRowView: View {
    let account: NetworkAccountRecord
    let hasSecret: Bool

    var body: some View {
        VStack(alignment: .leading, spacing: 10) {
            HStack(alignment: .top) {
                VStack(alignment: .leading, spacing: 4) {
                    Text(account.title)
                        .font(.headline)
                    Text(account.kind.title)
                    .font(.subheadline)
                    .foregroundStyle(account.kind.accentColor)
                }
                Spacer()
                if hasSecret {
                    Label("Credential stored", systemImage: "key.fill")
                        .font(.caption)
                        .foregroundStyle(.secondary)
                }
            }

            if let authority = account.authority {
                labeledValue("Authority", authority)
            }

            labeledValue("Account", account.accountName)
            labeledValue("Identity", account.identityName)

            if let hostname = account.hostname {
                labeledValue("Hostname", hostname)
            }

            if let username = account.username {
                labeledValue("Username", username)
            }

            if let tailnet = account.tailnet {
                labeledValue("Tailnet", tailnet)
            }

            if let note = account.note {
                Text(note)
                    .font(.footnote)
                    .foregroundStyle(.secondary)
            }
        }
        .padding()
        .frame(maxWidth: .infinity, alignment: .leading)
        .background(
            RoundedRectangle(cornerRadius: 16)
                .fill(.thinMaterial)
        )
    }

    @ViewBuilder
    private func labeledValue(_ label: String, _ value: String) -> some View {
        VStack(alignment: .leading, spacing: 2) {
            Text(label)
                .font(.caption)
                .foregroundStyle(.secondary)
            Text(value)
                .font(.body.monospaced())
        }
    }
}

private extension View {
    @ViewBuilder
    func burrowLoginField() -> some View {
        #if os(iOS)
        textInputAutocapitalization(.never)
        #else
        self
        #endif
    }

    @ViewBuilder
    func burrowEmailField() -> some View {
        #if os(iOS)
        textInputAutocapitalization(.never)
            .keyboardType(.emailAddress)
        #else
        self
        #endif
    }
}

#if canImport(AuthenticationServices)
@MainActor
private final class TailnetBrowserAuthenticator: NSObject {
    private var session: ASWebAuthenticationSession?
    private static var prefersEphemeralSessionForCurrentProcess: Bool {
        let rawValue = ProcessInfo.processInfo.environment["BURROW_UI_TEST_EPHEMERAL_AUTH"]?
            .trimmingCharacters(in: .whitespacesAndNewlines)
            .lowercased()
        return rawValue == "1" || rawValue == "true" || rawValue == "yes"
    }

    func start(url: URL, onDismiss: @escaping @Sendable () -> Void) {
        cancel()
        let session = ASWebAuthenticationSession(url: url, callbackURLScheme: nil) { _, _ in
            onDismiss()
        }
        session.presentationContextProvider = self
        session.prefersEphemeralWebBrowserSession = Self.prefersEphemeralSessionForCurrentProcess
        self.session = session
        _ = session.start()
    }

    func cancel() {
        session?.cancel()
        session = nil
    }
}

extension TailnetBrowserAuthenticator: ASWebAuthenticationPresentationContextProviding {
    func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor {
        #if canImport(AppKit)
        return NSApplication.shared.keyWindow
            ?? NSApplication.shared.windows.first
            ?? ASPresentationAnchor()
        #elseif canImport(UIKit)
        return ASPresentationAnchor()
        #else
        return ASPresentationAnchor()
        #endif
    }
}
#else
@MainActor
private final class TailnetBrowserAuthenticator {
    func start(url: URL, onDismiss: @escaping @Sendable () -> Void) {
        _ = url
        onDismiss()
    }

    func cancel() {}
}
#endif

private struct BurrowAutomationConfig {
    enum Action: String {
        case tailnetLogin = "tailnet-login"
        case tailnetProbe = "tailnet-probe"
    }

    let action: Action
    let title: String?
    let accountName: String?
    let identityName: String?
    let hostname: String?
    let authority: String?

    static let current: BurrowAutomationConfig? = {
        let environment = ProcessInfo.processInfo.environment
        guard let rawAction = environment["BURROW_UI_AUTOMATION"],
              let action = Action(rawValue: rawAction)
        else {
            return nil
        }

        return BurrowAutomationConfig(
            action: action,
            title: environment["BURROW_UI_AUTOMATION_TITLE"],
            accountName: environment["BURROW_UI_AUTOMATION_ACCOUNT"],
            identityName: environment["BURROW_UI_AUTOMATION_IDENTITY"],
            hostname: environment["BURROW_UI_AUTOMATION_HOSTNAME"],
            authority: environment["BURROW_UI_AUTOMATION_AUTHORITY"]
        )
    }()
}

#if DEBUG
struct NetworkView_Previews: PreviewProvider {
    static var previews: some View {
        BurrowView()
            .environment(\.tunnel, PreviewTunnel())
    }
}
#endif
