"""First-run wizard.

Five steps, driven by `st.session_state["wizard_step"]`:
    intro → profile → zones → races → data → done

The wizard only **writes** at the end of `profile` (creates the DB on first
`get_conn`), at the end of `zones` (writes openrun.toml), and at the end of
`races` (re-writes openrun.toml). The data step is the optional bit — users
can skip and use the Manual log page instead.

Anyone can re-run the wizard from this page at any time; existing values
pre-fill every field, so it's safe to revisit.
"""

from __future__ import annotations

import tempfile
from pathlib import Path

import streamlit as st

from openrun import (
    BanisterParams,
    Config,
    HRZones,
    UserProfile,
    default_config,
    write_config,
)
from openrun.config import _find_config, reset_default_config
from openrun.ingest.garmin_export import ingest_path
from openrun.setup import init_workspace
from openrun.web._helpers import get_conn, show_sidebar


st.set_page_config(page_title="Welcome · openrun", page_icon="👋", layout="wide")
show_sidebar()
st.title("👋 Welcome to openrun")


# ---------------------------------------------------------------------------
# Setup-status detection — visible at every step.
# ---------------------------------------------------------------------------

def _setup_status() -> dict:
    cfg_path = _find_config()
    activity_count = 0
    try:
        activity_count = get_conn().execute("SELECT COUNT(*) FROM activities").fetchone()[0]
    except Exception:  # noqa: BLE001
        pass
    return {
        "config_exists": cfg_path is not None,
        "config_path": cfg_path,
        "activity_count": activity_count,
    }


status = _setup_status()
if status["config_exists"] and status["activity_count"] > 0:
    st.success(
        f"You're already set up — `{status['config_path'].name}` is in place and "
        f"the DB has **{status['activity_count']:,}** activities. Use the wizard "
        "below to tweak your profile, or jump straight to the Dashboard."
    )
    if st.button("Open Dashboard", type="primary"):
        st.switch_page("pages/1_Dashboard.py")
    st.divider()


# ---------------------------------------------------------------------------
# Wizard state machine
# ---------------------------------------------------------------------------

STEPS = ("intro", "profile", "zones", "races", "data", "done")
STEP_LABELS = {
    "intro":   "Intro",
    "profile": "Your profile",
    "zones":   "HR zones",
    "races":   "Race calendar",
    "data":    "Get your data",
    "done":    "Done",
}


def _set_step(target: str) -> None:
    st.session_state.wizard_step = target


def _build_config() -> Config:
    """Materialise a Config from the current draft."""
    d = st.session_state.draft
    profile = UserProfile(
        name=d["name"],
        height_cm=d["height_cm"],
        weight_kg=d["weight_kg"],
        hr_max=int(d["hr_max"]),
        lthr=d["lthr"],
        resting_hr=d["resting_hr"],
        zones=d["zones"],
    )
    return Config(
        user=profile,
        db_path=default_config().db_path,
        banister=d["banister"],
        races=tuple(d["races"]),
    )


if "wizard_step" not in st.session_state:
    st.session_state.wizard_step = "intro"
if "draft" not in st.session_state:
    # Seed from current config so revisits pre-fill every field.
    cur = default_config()
    st.session_state.draft = {
        "name":       cur.user.name,
        "height_cm":  cur.user.height_cm,
        "weight_kg":  cur.user.weight_kg,
        "hr_max":     cur.user.hr_max,
        "lthr":       cur.user.lthr,
        "resting_hr": cur.user.resting_hr,
        "zones":      cur.user.hr_zones(),
        "races":      list(cur.races),
        "banister":   cur.banister,
    }

step = st.session_state.wizard_step
idx = STEPS.index(step)
st.progress((idx + 1) / len(STEPS), text=f"Step {idx + 1} / {len(STEPS)} — {STEP_LABELS[step]}")


# ---------------------------------------------------------------------------
# Step: intro
# ---------------------------------------------------------------------------

if step == "intro":
    st.markdown(
        """
        Five quick steps to get you running:

        1. **Profile** — your max HR, threshold HR, weight (optional)
        2. **HR zones** — auto-derived from your max HR; tweak if needed
        3. **Race calendar** — upcoming races (optional)
        4. **Get your data** — upload a Garmin export, or skip and log manually
        5. **Done** — straight to the dashboard

        No CLI required. You can come back and rerun this any time.
        """
    )
    if st.button("Start", type="primary"):
        _set_step("profile")
        st.rerun()


# ---------------------------------------------------------------------------
# Step: profile
# ---------------------------------------------------------------------------

elif step == "profile":
    with st.form("profile_form"):
        st.subheader("Your profile")
        st.caption(
            "These numbers personalise pace, zone, and load calculations. "
            "If you don't know your max HR, leave the default and we'll auto-derive zones."
        )
        c1, c2 = st.columns(2)
        name = c1.text_input("Display name", value=st.session_state.draft["name"])
        hr_max = c2.number_input("Max HR (bpm)",
                                 min_value=120, max_value=240,
                                 value=int(st.session_state.draft["hr_max"]))
        lthr = c1.number_input("Lactate-threshold HR (bpm)",
                               min_value=80, max_value=230,
                               value=int(st.session_state.draft["lthr"] or hr_max - 25))
        rhr = c2.number_input("Resting HR (bpm)",
                              min_value=30, max_value=100,
                              value=int(st.session_state.draft["resting_hr"] or 60))
        c3, c4 = st.columns(2)
        height = c3.number_input("Height (cm, optional)",
                                 min_value=0.0, max_value=250.0,
                                 value=float(st.session_state.draft["height_cm"] or 0.0),
                                 step=0.5)
        weight = c4.number_input("Weight (kg, optional)",
                                 min_value=0.0, max_value=200.0,
                                 value=float(st.session_state.draft["weight_kg"] or 0.0),
                                 step=0.1)

        cols = st.columns([1, 1, 4])
        back = cols[0].form_submit_button("← Back")
        nxt = cols[1].form_submit_button("Next →", type="primary")

    if back:
        _set_step("intro")
        st.rerun()
    if nxt:
        st.session_state.draft.update({
            "name": name.strip() or "Athlete",
            "hr_max": int(hr_max),
            "lthr": int(lthr),
            "resting_hr": int(rhr),
            "height_cm": float(height) if height > 0 else None,
            "weight_kg": float(weight) if weight > 0 else None,
            "zones": HRZones.from_pct_hr_max(int(hr_max)),  # reset zones to %HRmax derived
        })
        _set_step("zones")
        st.rerun()


# ---------------------------------------------------------------------------
# Step: zones
# ---------------------------------------------------------------------------

elif step == "zones":
    st.subheader("HR zones")
    st.caption(
        "Auto-derived from your max HR (Garmin's `HR_MAX` method). "
        "If you've configured custom zones in Garmin Connect, paste those numbers here instead."
    )
    z = st.session_state.draft["zones"]
    cols = st.columns(5)
    z_inputs: list[tuple[int, int]] = []
    for col, (label, lo, hi) in zip(
        cols,
        (("Z1", *z.z1), ("Z2", *z.z2), ("Z3", *z.z3), ("Z4", *z.z4), ("Z5", *z.z5)),
    ):
        new_lo = col.number_input(f"{label} low",  min_value=40, max_value=240, value=int(lo),  key=f"{label}_lo")
        new_hi = col.number_input(f"{label} high", min_value=40, max_value=240, value=int(hi),  key=f"{label}_hi")
        z_inputs.append((int(new_lo), int(new_hi)))

    new_zones = HRZones(z1=z_inputs[0], z2=z_inputs[1], z3=z_inputs[2],
                        z4=z_inputs[3], z5=z_inputs[4])

    c1, c2, _ = st.columns([1, 1, 4])
    if c1.button("← Back"):
        _set_step("profile")
        st.rerun()
    if c2.button("Next →", type="primary"):
        st.session_state.draft["zones"] = new_zones
        _set_step("races")
        st.rerun()


# ---------------------------------------------------------------------------
# Step: races
# ---------------------------------------------------------------------------

elif step == "races":
    import pandas as pd

    st.subheader("Race calendar (optional)")
    st.caption(
        "Future races your training is pointed at. Used by the Race plan page "
        "to land your taper. Empty is fine — you can add them later."
    )
    races_in = st.session_state.draft["races"]
    races_df = pd.DataFrame(races_in, columns=["label", "date"])
    if races_df.empty:
        races_df = pd.DataFrame([{"label": "", "date": ""}])

    edited = st.data_editor(
        races_df,
        num_rows="dynamic",
        width="stretch",
        column_config={
            "label": st.column_config.TextColumn("Label",
                                                 help="e.g. 'wk 17 — Cascade Crest 50M'"),
            "date":  st.column_config.TextColumn("Date (YYYY-MM-DD)"),
        },
        hide_index=True,
        key="races_editor",
    )

    c1, c2, _ = st.columns([1, 1, 4])
    if c1.button("← Back"):
        _set_step("zones")
        st.rerun()
    if c2.button("Next →", type="primary"):
        keep = [
            (str(r["label"]).strip(), str(r["date"]).strip())
            for _, r in edited.iterrows()
            if str(r["label"]).strip() and str(r["date"]).strip()
        ]
        st.session_state.draft["races"] = keep

        # Write the config now so the user has a real openrun.toml even if they bail.
        config = _build_config()
        cfg_target = Path.cwd() / "openrun.toml"
        write_config(config, cfg_target)
        # Init the workspace (idempotent) so the DB exists.
        init_workspace(config=config)
        # Reset the process-wide config cache so other pages see the new values.
        reset_default_config()
        st.cache_data.clear()
        _set_step("data")
        st.rerun()


# ---------------------------------------------------------------------------
# Step: data
# ---------------------------------------------------------------------------

elif step == "data":
    st.subheader("Get your data")
    st.caption(
        "Optional but recommended — without data the dashboards are empty. "
        "Upload a Garmin Connect export (`.zip`) and we'll ingest it now. "
        "Or skip and start logging activities by hand from the Manual log page."
    )

    uploaded = st.file_uploader("Upload connect.zip", type=["zip"])
    cols = st.columns([1, 1, 1, 3])
    back = cols[0].button("← Back")
    ingest = cols[1].button("Ingest", disabled=uploaded is None, type="primary")
    skip = cols[2].button("Skip for now")

    if back:
        _set_step("races")
        st.rerun()
    if skip:
        _set_step("done")
        st.rerun()
    if ingest and uploaded is not None:
        with tempfile.NamedTemporaryFile(suffix=".zip", delete=False) as tmp:
            tmp.write(uploaded.read())
            tmp_path = Path(tmp.name)
        try:
            with st.status(f"Ingesting {uploaded.name}…", expanded=True) as bar:
                summary = ingest_path(tmp_path, conn=get_conn(), progress=bar.write)
                bar.update(label="Ingest complete", state="complete")
        finally:
            tmp_path.unlink(missing_ok=True)
        st.cache_data.clear()
        st.success(
            f"Ingested **{sum(summary['counts'].values()):,}** JSON rows and "
            f"**{summary['fits']}** FIT files."
        )
        _set_step("done")
        st.rerun()


# ---------------------------------------------------------------------------
# Step: done
# ---------------------------------------------------------------------------

elif step == "done":
    st.subheader("You're set 🎉")
    st.markdown(
        f"""
        Your config is at **`{Path.cwd() / 'openrun.toml'}`** — edit it directly any time.
        The DB lives at **`{default_config().db_path}`**.

        **Next:**
        - 📊 **Dashboard** — your fitness/fatigue/form right now
        - 📋 **Activities** — browse + drill into individual runs
        - 📈 **Race plan** — periodised plan with projected race-day TSB
        - 📝 **Manual log** — record off-watch sessions
        """
    )
    if st.button("Open Dashboard →", type="primary"):
        st.switch_page("pages/1_Dashboard.py")
    if st.button("Restart wizard"):
        st.session_state.wizard_step = "intro"
        st.rerun()
