import SwiftUI
import PokerCore
import PokerSession

/// The whole app, laid out for whatever space it has been given.
///
/// There is one rule here and the rest follows from it: a phone is not a small Mac. On a wide
/// screen the screens sit beside a sidebar; on a phone they sit above a tab bar, the seats shrink,
/// and the buttons grow — a decision made with a thumb needs a bigger target than one made with a
/// pointer, not a smaller one.
public struct RootView: View {
    @Environment(AppState.self) var app

    public init() {}

    public var body: some View {
        @Bindable var app = app
        GeometryReader { geo in
            let metrics = Metrics(width: geo.size.width, height: geo.size.height)
            Group {
                if metrics.compact {
                    VStack(spacing: 0) {
                        screen
                        TabBar()
                    }
                } else {
                    HStack(spacing: 0) {
                        Sidebar()
                        Divider().overlay(Color.white.opacity(0.06))
                        screen
                    }
                }
            }
            .environment(\.metrics, metrics)
            .background(Theme.background)
            // Colours and type sizes are both read statically as the screen draws, so nothing is
            // watching them. Changing either identity throws this whole subtree away and builds it
            // again, which is the redraw.
            .id("\(app.palette.rawValue)-\(app.textScale.rawValue)-\(app.cardColours.rawValue)")
            // One presenter for the lot. A view has a single sheet, so six of these stacked up
            // meant handing one over to the next was a dismissal racing a presentation — and the
            // one that lost was the drill report, which is the screen a drill must never skip.
            .sheet(item: $app.sheet, onDismiss: { app.sheetDismissed() }) { sheet in
                Group {
                    switch sheet {
                    case .tutorial:
                        Tutorial().interactiveDismissDisabled()
                    case .review:
                        if let review = app.review { ReviewSheet(review: review) }
                    case .assessmentBrief:
                        AssessmentBrief()
                    case .assessmentReport:
                        if let report = app.assessmentReport {
                            AssessmentReportView(report: report)
                        }
                    case .drillBrief(let target, let retest):
                        DrillBrief(target: target, retest: retest)
                    case .drillReport(let run):
                        DrillReportView(run: run)
                    case .paywall(let dismissable):
                        // Swiping it away when the hands are gone would land on a table that
                        // cannot deal. The only ways out are paying or the Leave button behind it.
                        PaywallView(dismissable: dismissable) { app.sheet = nil }
                            .interactiveDismissDisabled(!dismissable)
                    }
                }
                .environment(app)
                .environment(\.metrics, metrics)
            }
        }
        .background(Theme.background)
    }

    @ViewBuilder private var screen: some View {
        Group {
            switch app.screen {
            case .home: HomeView()
            case .table: TableView()
            case .practice: PracticeView()
            case .leaks: LeaksView()
            case .history: HistoryView()
            case .ladder: LadderView()
            case .settings: SettingsView()
            }
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
    }
}

/// The wide layout: every screen listed down the side, with the rating parked at the bottom.
struct Sidebar: View {
    @Environment(AppState.self) var app

    var body: some View {
        VStack(alignment: .leading, spacing: 3) {
            HStack(spacing: 8) {
                ZStack {
                    RoundedRectangle(cornerRadius: 7, style: .continuous).fill(AdLabMark.olive)
                        .frame(width: 26, height: 26)
                    // Fixed: the spade is a mark inside a 26-point tile, not something to read.
                    Text("♠").font(.system(size: 15, weight: .bold)).foregroundStyle(Theme.background)
                }
                VStack(alignment: .leading, spacing: 1) {
                    Text(app.variant.displayName).heading(capped: true)
                        .foregroundStyle(Theme.text)
                    AdLabMark(size: 9, showRule: false)
                }
            }
            .padding(.bottom, 18)
            .padding(.horizontal, 6)

            ForEach(Screen.allCases) { screen in
                let disabled = screen == .table && !app.isPlaying
                Button {
                    if !disabled { app.screen = screen }
                } label: {
                    HStack(spacing: 9) {
                        ScreenIcon(screen: screen, size: 12).frame(width: 16)
                        // Capped: the sidebar is a fixed 190 points wide.
                        Text(screen.rawValue).label(capped: true)
                        Spacer()
                    }
                    .foregroundStyle(app.screen == screen ? Theme.background : (disabled ? Theme.dim.opacity(0.45) : Theme.dim))
                    .padding(.horizontal, 10).padding(.vertical, 7)
                    .background(
                        RoundedRectangle(cornerRadius: 8, style: .continuous)
                            .fill(app.screen == screen ? Theme.accent : Color.clear)
                    )
                    .contentShape(Rectangle())
                }
                .buttonStyle(.plain)
                .disabled(disabled)
            }

            Spacer()

            VStack(alignment: .leading, spacing: 5) {
                // Named apart from the ladder's. That one is counted over the hands at the table
                // the player is on now and this one over everything ever played, they are both on
                // screen at the same time on a wide window, and they disagree.
                Text("RATING · ALL TIME").label(capped: true)
                    .foregroundStyle(Theme.dim.opacity(0.7))
                // A dash rather than a number until there is a hand behind it. Same refusal as the
                // rating card on Home: an empty history clamps to the middle of the scale, and the
                // middle of a scale printed as a verdict is a verdict about nobody.
                HStack(alignment: .lastTextBaseline, spacing: 5) {
                    let known = app.rating.handsBehindIt > 0
                    Text(known ? "\(app.rating.value)" : "—")
                        .numeric(22, capped: true).foregroundStyle(known ? Theme.accent : Theme.dim)
                    if known {
                        Text(app.rating.label).caption(capped: true).foregroundStyle(Theme.dim)
                    }
                }
                // The hands the rating was actually built on, which is neither the hands this
                // sitting watched — the sidebar read "0 hands" every launch next to a rating built
                // from a year of them — nor every line in the file. The rating drops drill and
                // check hands, so counting them here put "250 hands" under a number that had seen
                // fifty, which is a claim four times firmer than the evidence for it.
                Text(Copy.hands(app.rating.handsBehindIt)).caption(capped: true)
                    .foregroundStyle(Theme.dim.opacity(0.8))
            }
            .padding(.horizontal, 8)
        }
        .padding(16)
        .frame(width: Theme.size(190))
        .background(Theme.panel)
    }
}

/// The phone layout: the same screens, along the bottom where a thumb can reach them.
struct TabBar: View {
    @Environment(AppState.self) var app

    var body: some View {
        HStack(spacing: 0) {
            ForEach(Screen.allCases) { screen in
                let disabled = screen == .table && !app.isPlaying
                let selected = app.screen == screen
                Button {
                    if !disabled { app.screen = screen }
                } label: {
                    VStack(spacing: 3) {
                        ScreenIcon(screen: screen, size: 17)
                        // Capped: six of these share the width of a phone.
                        Text(screen.shortName).label(capped: true)
                    }
                    .foregroundStyle(selected ? Theme.accent : (disabled ? Theme.dim.opacity(0.4) : Theme.dim))
                    .frame(maxWidth: .infinity)
                    .padding(.top, 9)
                    .contentShape(Rectangle())
                }
                .buttonStyle(.plain)
                .disabled(disabled)
            }
        }
        .padding(.bottom, 4)
        .background(
            Theme.panel
                .overlay(alignment: .top) { Rectangle().fill(Color.white.opacity(0.06)).frame(height: 1) }
                .ignoresSafeArea(edges: .bottom)
        )
    }
}

/// A screen's icon, on every platform — see `Symbol`.
struct ScreenIcon: View {
    let screen: Screen
    let size: CGFloat

    var body: some View { Symbol(screen.icon, size: size) }
}
