import SwiftUI
import PokerCore
import PokerCoach
import PokerSession

/// What an assessment is, before anybody agrees to sit through one.
///
/// The whole of it is said up front, including the awkward part: the table is arranged rather than
/// dealt, and there is a reason that does not spoil the measurement. Somebody who reads that at the
/// end and not at the beginning is entitled to feel they were not told.
struct AssessmentBrief: View {
    @Environment(AppState.self) var app
    @Environment(\.metrics) var metrics
    @State var showing: GlossaryTerm?

    private var resuming: SessionSummary? { app.resumableAssessment }

    var body: some View {
        VStack(alignment: .leading, spacing: 0) {
            VStack(alignment: .leading, spacing: 8) {
                Text(Copy.assessmentFinishedTitle).title().foregroundStyle(Theme.text)
                Text(Copy.assessmentLength(AssessmentRun.standardLength))
                    .body().foregroundStyle(Theme.dim)
            }
            .padding(metrics.compact ? 16 : 22)

            Divider().overlay(Color.white.opacity(0.07))

            ScrollView {
                VStack(alignment: .leading, spacing: 14) {
                    GlossaryText(Copy.assessmentIntro(AssessmentRun.standardLength),
                                 size: 13, colour: Theme.text, showing: $showing)

                    Point(title: Copy.everyHandIsChosen, text: Copy.everyHandIsChosenBlurb,
                          showing: $showing)

                    Point(title: Copy.cardsNotChosen, text: Copy.cardsNotChosenBlurb,
                          showing: $showing)

                    Point(title: Copy.stopWhenever, text: Copy.stopWheneverBlurb,
                          showing: $showing)

                    Point(title: Copy.noPretending,
                          text: Copy.noPretendingBlurb(AssessmentRun.standardLength),
                          showing: $showing)

                    DefinitionSlot(showing: $showing)
                }
                .padding(metrics.compact ? 18 : 22)
            }

            Divider().overlay(Color.white.opacity(0.07))

            HStack(spacing: 10) {
                if let resuming {
                    PillButton(title: "Carry on",
                               subtitle: "\(resuming.hands) of \(AssessmentRun.standardLength) played",
                               wide: metrics.compact) {
                        app.resumeAssessment()
                    }
                    PillButton(title: "Start again", quiet: true, wide: metrics.compact) {
                        app.beginAssessment()
                    }
                } else {
                    PillButton(title: "Start", wide: metrics.compact) { app.beginAssessment() }
                    PillButton(title: "Not now", quiet: true, wide: metrics.compact) {
                        app.dismissSheet()
                    }
                }
            }
            .padding(18)
        }
        .frame(width: metrics.compact ? nil : Theme.size(620))
        .frame(maxWidth: metrics.compact ? .infinity : nil,
               maxHeight: metrics.compact ? .infinity : nil, alignment: .top)
        .background(Theme.panel)
    }
}

/// One numbered thing in the brief and in the report.
struct Point: View {
    let title: String
    let text: String
    @Binding var showing: GlossaryTerm?

    var body: some View {
        VStack(alignment: .leading, spacing: 4) {
            Text(title).heading().foregroundStyle(Theme.accent)
            GlossaryText(text, size: TextRole.body.base, colour: Theme.dim, showing: $showing)
        }
    }
}

/// The band above the felt while an assessment runs: how far through it is, and what this
/// particular hand is being used to find out.
struct AssessmentBanner: View {
    /// Only so an open definition can be closed when the hand moves on. Ken, 2026-08-22:
    /// "a text box turned up toward the top of the screen giving my advice about position
    /// and it is did not go away in the next hand." A definition is about the hand being
    /// played; it has no business outliving it.
    let handNumber: Int
    let run: AssessmentRun
    @Environment(\.metrics) var metrics
    @State var showing: GlossaryTerm?

    var body: some View {
        VStack(alignment: .leading, spacing: 7) {
            HStack(alignment: .firstTextBaseline, spacing: 10) {
                Text(run.progressLine).caption(weight: .medium).foregroundStyle(Theme.accent)
                Spacer(minLength: 8)
                Text("\(run.handsLeft) to go").caption().foregroundStyle(Theme.dim)
            }
            ProgressStrip(fraction: run.fraction)
            // The seat leads with its number, as everywhere else — "seat 2 of 9 (the big blind)"
            // says the thing the traditional name was standing in for.
            GlossaryText(run.setup.introduction(atTableOf: TableSession.seatsAtTable),
                         size: 11, colour: Theme.text.opacity(0.85), showing: $showing)
            // What the hand is looking at is deliberately not here. These hands count towards the
            // leak scorecard, and naming the habit being measured while it is being measured
            // changes the thing measured — it is the same objection that keeps drill hands out of
            // the scorecard altogether. The whole list is in the report instead, where saying it
            // costs nothing. The seat and the stacks stay, because the player can see both of them
            // on the felt anyway and hiding what is in plain sight buys the measurement nothing.
            DefinitionSlot(showing: $showing)
        }
        .padding(.horizontal, metrics.compact ? 14 : 22)
        .padding(.vertical, 9)
        .frame(maxWidth: .infinity, alignment: .leading)
        .background(Theme.panelRaised)
        .onChange(of: handNumber) { _, _ in showing = nil }
    }
}

struct ProgressStrip: View {
    let fraction: Double

    var body: some View {
        GeometryReader { geo in
            ZStack(alignment: .leading) {
                Capsule().fill(Color.white.opacity(0.08))
                Capsule().fill(Theme.accent)
                    .frame(width: max(2, geo.size.width * min(1, max(0, fraction))))
            }
        }
        .frame(height: 5)
    }
}

/// What the sitting concluded.
///
/// Read from the top it is: what happened, what it is worth, what I am sure of, what I only
/// suspect, what I never saw, and what I arranged. The order is deliberate — the caveats sit with
/// the findings rather than in a footnote nobody reaches.
struct AssessmentReportView: View {
    @Environment(AppState.self) var app
    @Environment(\.metrics) var metrics
    @State var showing: GlossaryTerm?
    let report: AssessmentReport

    var body: some View {
        VStack(alignment: .leading, spacing: 0) {
            VStack(alignment: .leading, spacing: 8) {
                Text(!report.finished ? Copy.assessmentStoppedTitle
                     : (app.assessmentJustFinished
                        ? Copy.assessmentJustFinishedTitle(hands: report.hands)
                        : Copy.assessmentFinishedTitle))
                    .title().foregroundStyle(Theme.text)
                GlossaryText(report.headline, size: TextRole.body.base, colour: Theme.dim, showing: $showing)
            }
            .padding(metrics.compact ? 16 : 22)

            Divider().overlay(Color.white.opacity(0.07))

            ScrollView {
                VStack(alignment: .leading, spacing: 20) {
                    rating
                    if !report.named.isEmpty { findings(report.named, title: Copy.alreadyClear) }
                    if !report.watching.isEmpty {
                        findings(report.watching, title: Copy.whereToLookFirst,
                                 note: Copy.whereToLookFirstNote)
                    }
                    if report.named.isEmpty, report.watching.isEmpty {
                        Panel(title: Copy.nothingToName) {
                            GlossaryText(report.quietNote, size: TextRole.body.base, colour: Theme.dim, showing: $showing)
                        }
                    }
                    coverage
                    steering
                    DefinitionSlot(showing: $showing)
                }
                .padding(metrics.compact ? 18 : 22)
            }

            Divider().overlay(Color.white.opacity(0.07))

            HStack(spacing: 10) {
                PillButton(title: "See my game", wide: metrics.compact) {
                    app.dismissSheet()
                    app.scope = .allTime
                    app.screen = .leaks
                }
                PillButton(title: "Close", quiet: true, wide: metrics.compact) {
                    app.dismissSheet()
                }
            }
            .padding(18)
        }
        .frame(width: metrics.compact ? nil : Theme.size(660))
        .frame(maxWidth: metrics.compact ? .infinity : nil,
               maxHeight: metrics.compact ? .infinity : nil, alignment: .top)
        .background(Theme.panel)
    }

    private var rating: some View {
        Panel(title: Copy.startingRating) {
            HStack(alignment: .lastTextBaseline, spacing: 10) {
                Text("\(report.rating.value)").numeric(42, weight: .bold, capped: true)
                    .foregroundStyle(Theme.accent)
                VStack(alignment: .leading, spacing: 2) {
                    Text(report.rating.label).heading().foregroundStyle(Theme.text)
                    Text(Copy.ratingRangeLine(low: report.ratingRange.lowerBound,
                                              high: report.ratingRange.upperBound,
                                              hands: report.hands))
                        .caption().foregroundStyle(Theme.dim)
                }
            }
            GlossaryText(report.precisionNote, size: TextRole.body.base, colour: Theme.dim, showing: $showing)
        }
    }

    private func findings(_ list: [AssessmentReport.Named], title: String,
                          note: String? = nil) -> some View {
        // The free tier: the one habit in full, the others blurred with the count over them. The
        // choice is `AppState.freeView`'s, read over everything played — the same habit the
        // scorecard shows, so the two screens cannot name different ones.
        let free = app.freeView
        let hiddenHere = list.filter { free.isBlurred(leakID: $0.id) }.count
        return Panel(title: title, subtitle: note) {
            VStack(alignment: .leading, spacing: 18) {
                if free.isRestricted, hiddenHere > 0 {
                    HiddenCountLine(count: hiddenHere) { app.showPaywall() }
                }
                ForEach(list) { finding in
                    VStack(alignment: .leading, spacing: 6) {
                        Text(finding.leak.name).heading()
                            .foregroundStyle(finding.certain ? Theme.warn : Theme.gold)
                        RateBar(rate: finding.rate, acceptable: finding.leak.acceptableRate)
                        Text(finding.line).caption().foregroundStyle(Theme.dim)
                            .fixedSize(horizontal: false, vertical: true)
                        GlossaryText(finding.leak.explanation, showing: $showing)
                    }
                    .hiddenUntilSubscribed(free.isBlurred(leakID: finding.id)) { app.showPaywall() }
                }
            }
        }
    }

    private var coverage: some View {
        Panel(title: Copy.whatIGotToLookAt,
              subtitle: Copy.decisionsAcrossHands(decisions: report.decisions,
                                                  hands: report.hands)) {
            VStack(spacing: 9) {
                ForEach(report.areas) { area in
                    HStack(spacing: 12) {
                        Text(area.name).body()
                            .foregroundStyle(area.readable ? Theme.text : Theme.dim.opacity(0.6))
                            .frame(width: Theme.size(metrics.compact ? 140 : 210), alignment: .leading)
                        ProgressStrip(fraction: area.certainty)
                        Text(area.chances > 0 ? "\(area.mistakes)/\(area.chances)" : "—")
                            .numericSmall().foregroundStyle(Theme.dim)
                            .frame(width: Theme.size(52), alignment: .trailing)
                    }
                }
            }
            if let thin = report.thinNote {
                GlossaryText(thin, size: TextRole.caption.base, colour: Theme.gold.opacity(0.9), showing: $showing)
            }
        }
    }

    private var steering: some View {
        Panel(title: Copy.whatIArranged) {
            GlossaryText(report.steeringNote, size: TextRole.body.base, colour: Theme.dim, showing: $showing)
            // Each arrangement, and what it was there to look at. The second half is only shown
            // here: on the felt it would have told the player which habit was being counted while
            // it was being counted.
            VStack(alignment: .leading, spacing: 9) {
                ForEach(report.steering) { steer in
                    HStack(alignment: .firstTextBaseline, spacing: 8) {
                        Text("\(steer.hands)").numericSmall().foregroundStyle(Theme.accent)
                            .frame(width: Theme.size(20), alignment: .trailing)
                        VStack(alignment: .leading, spacing: 2) {
                            Text(steer.setup.summary(atTableOf: TableSession.seatsAtTable))
                                .caption().foregroundStyle(Theme.text.opacity(0.85))
                                .fixedSize(horizontal: false, vertical: true)
                            GlossaryText(steer.setup.watching, size: TextRole.caption.base, colour: Theme.dim,
                                         showing: $showing)
                        }
                    }
                }
            }
            GlossaryText(report.fairnessNote, size: TextRole.body.base, colour: Theme.text.opacity(0.85),
                         showing: $showing)
        }
    }
}
