"""
Routines API - routine management

Routines have ordered steps. Users start sessions to walk through them.
"""

import os
import uuid
import json
from datetime import datetime
import flask
import jwt
import core.auth as auth
import core.postgres as postgres
import core.routines as routines_core
import core.tz as tz


def _get_user_uuid(token):
    try:
        payload = jwt.decode(token, os.getenv("JWT_SECRET"), algorithms=["HS256"])
        return payload.get("sub")
    except (jwt.ExpiredSignatureError, jwt.InvalidTokenError):
        return None


def _auth(request):
    """Extract and verify token. Returns user_uuid or None."""
    header = request.headers.get("Authorization", "")
    if not header.startswith("Bearer "):
        return None
    token = header[7:]
    user_uuid = _get_user_uuid(token)
    if not user_uuid or not auth.verifyLoginToken(token, userUUID=user_uuid):
        return None
    return user_uuid


def _make_aware_utc(dt):
    """Ensure a datetime is timezone-aware; assume naive datetimes are UTC."""
    if dt.tzinfo is None:
        from datetime import timezone as _tz

        return dt.replace(tzinfo=_tz.utc)
    return dt


def _record_step_result(session_id, step_id, step_index, result, session):
    """Record a per-step result (completed or skipped)."""
    try:
        # Compute duration from previous step completion or session start
        prev_results = postgres.select(
            "routine_step_results",
            where={"session_id": session_id},
            order_by="completed_at DESC",
            limit=1,
        )
        now = tz.user_now()
        if prev_results:
            last_completed = prev_results[0].get("completed_at")
            if last_completed:
                if isinstance(last_completed, str):
                    last_completed = datetime.fromisoformat(last_completed)
                last_completed = _make_aware_utc(last_completed)
                duration_seconds = max(0, int((now - last_completed).total_seconds()))
            else:
                duration_seconds = None
        else:
            created_at = session.get("created_at")
            if created_at:
                if isinstance(created_at, str):
                    created_at = datetime.fromisoformat(created_at)
                created_at = _make_aware_utc(created_at)
                duration_seconds = max(0, int((now - created_at).total_seconds()))
            else:
                duration_seconds = None

        postgres.insert(
            "routine_step_results",
            {
                "id": str(uuid.uuid4()),
                "session_id": session_id,
                "step_id": step_id,
                "step_index": step_index,
                "result": result,
                "duration_seconds": duration_seconds,
                "completed_at": now.isoformat(),
            },
        )
    except Exception:
        pass  # Don't fail the step completion if tracking fails


def _complete_session_with_celebration(session_id, user_uuid, session):
    """Complete a session and return celebration data."""
    now = tz.user_now()
    created_at = session.get("created_at")
    if created_at:
        if isinstance(created_at, str):
            created_at = datetime.fromisoformat(created_at)
        created_at = _make_aware_utc(created_at)
        duration_minutes = max(0, round((now - created_at).total_seconds() / 60, 1))
    else:
        duration_minutes = 0

    # Update session as completed with duration — this MUST succeed
    postgres.update(
        "routine_sessions",
        {
            "status": "completed",
            "completed_at": now.isoformat(),
            "actual_duration_minutes": int(duration_minutes),
        },
        {"id": session_id},
    )

    # Gather celebration stats — failures here should not break completion
    streak_current = 1
    streak_longest = 1
    streak_milestone = None
    steps_completed = 0
    steps_skipped = 0
    total_completions = 1

    try:
        streak_result = routines_core._update_streak(user_uuid, session["routine_id"])
        streak = postgres.select_one(
            "routine_streaks",
            {
                "user_uuid": user_uuid,
                "routine_id": session["routine_id"],
            },
        )
        if streak:
            streak_current = streak["current_streak"]
            streak_longest = streak["longest_streak"]
        streak_milestone = streak_result.get("milestone") if streak_result else None
    except Exception:
        pass

    try:
        step_results = postgres.select(
            "routine_step_results", {"session_id": session_id}
        )
        steps_completed = sum(1 for r in step_results if r.get("result") == "completed")
        steps_skipped = sum(1 for r in step_results if r.get("result") == "skipped")
    except Exception:
        pass

    try:
        all_completed = postgres.select(
            "routine_sessions",
            {
                "routine_id": session["routine_id"],
                "user_uuid": user_uuid,
                "status": "completed",
            },
        )
        total_completions = len(all_completed)
    except Exception:
        pass

    result = {
        "streak_current": streak_current,
        "streak_longest": streak_longest,
        "session_duration_minutes": duration_minutes,
        "total_completions": total_completions,
        "steps_completed": steps_completed,
        "steps_skipped": steps_skipped,
    }
    if streak_milestone:
        result["streak_milestone"] = streak_milestone
    return result


def register(app):
    # ── Routines CRUD ─────────────────────────────────────────────

    @app.route("/api/routines", methods=["GET"])
    def api_listRoutines():
        """List all routines for the logged-in user."""
        user_uuid = _auth(flask.request)
        if not user_uuid:
            return flask.jsonify({"error": "unauthorized"}), 401
        routines = postgres.select(
            "routines", where={"user_uuid": user_uuid}, order_by="name"
        )
        return flask.jsonify(routines), 200

    @app.route("/api/routines", methods=["POST"])
    def api_createRoutine():
        """Create a new routine. Body: {name, description?, icon?}"""
        user_uuid = _auth(flask.request)
        if not user_uuid:
            return flask.jsonify({"error": "unauthorized"}), 401
        data = flask.request.get_json()
        if not data:
            return flask.jsonify({"error": "missing body"}), 400
        if not data.get("name"):
            return flask.jsonify({"error": "missing required field: name"}), 400
        data["id"] = str(uuid.uuid4())
        data["user_uuid"] = user_uuid
        routine = postgres.insert("routines", data)
        return flask.jsonify(routine), 201

    @app.route("/api/routines/<routine_id>", methods=["GET"])
    def api_getRoutine(routine_id):
        """Get a routine with its steps."""
        user_uuid = _auth(flask.request)
        if not user_uuid:
            return flask.jsonify({"error": "unauthorized"}), 401
        routine = postgres.select_one(
            "routines", {"id": routine_id, "user_uuid": user_uuid}
        )
        if not routine:
            return flask.jsonify({"error": "not found"}), 404
        steps = postgres.select(
            "routine_steps",
            where={"routine_id": routine_id},
            order_by="position",
        )
        return flask.jsonify({"routine": routine, "steps": steps}), 200

    @app.route("/api/routines/<routine_id>", methods=["PUT"])
    def api_updateRoutine(routine_id):
        """Update routine details. Body: {name?, description?, icon?}"""
        user_uuid = _auth(flask.request)
        if not user_uuid:
            return flask.jsonify({"error": "unauthorized"}), 401
        data = flask.request.get_json()
        if not data:
            return flask.jsonify({"error": "missing body"}), 400
        existing = postgres.select_one(
            "routines", {"id": routine_id, "user_uuid": user_uuid}
        )
        if not existing:
            return flask.jsonify({"error": "not found"}), 404
        allowed = [
            "name",
            "description",
            "icon",
            "location",
            "environment_prompts",
            "habit_stack_after",
        ]
        updates = {k: v for k, v in data.items() if k in allowed}
        if not updates:
            return flask.jsonify({"error": "no valid fields to update"}), 400
        result = postgres.update(
            "routines", updates, {"id": routine_id, "user_uuid": user_uuid}
        )
        return flask.jsonify(result[0] if result else {}), 200

    @app.route("/api/routines/<routine_id>", methods=["DELETE"])
    def api_deleteRoutine(routine_id):
        """Delete a routine and all its steps/sessions."""
        user_uuid = _auth(flask.request)
        if not user_uuid:
            return flask.jsonify({"error": "unauthorized"}), 401
        existing = postgres.select_one(
            "routines", {"id": routine_id, "user_uuid": user_uuid}
        )
        if not existing:
            return flask.jsonify({"error": "not found"}), 404
        postgres.delete("routine_sessions", {"routine_id": routine_id})
        postgres.delete("routine_steps", {"routine_id": routine_id})
        postgres.delete("routine_schedules", {"routine_id": routine_id})
        postgres.delete("routines", {"id": routine_id, "user_uuid": user_uuid})
        return flask.jsonify({"deleted": True}), 200

    # ── Steps CRUD ────────────────────────────────────────────────

    @app.route("/api/routines/<routine_id>/steps", methods=["GET"])
    def api_listSteps(routine_id):
        """List steps for a routine, ordered by position."""
        user_uuid = _auth(flask.request)
        if not user_uuid:
            return flask.jsonify({"error": "unauthorized"}), 401
        routine = postgres.select_one(
            "routines", {"id": routine_id, "user_uuid": user_uuid}
        )
        if not routine:
            return flask.jsonify({"error": "not found"}), 404
        steps = postgres.select(
            "routine_steps",
            where={"routine_id": routine_id},
            order_by="position",
        )
        return flask.jsonify(steps), 200

    @app.route("/api/routines/<routine_id>/steps", methods=["POST"])
    def api_addStep(routine_id):
        """Add a step to a routine. Body: {name, duration_minutes?, position?}"""
        user_uuid = _auth(flask.request)
        if not user_uuid:
            return flask.jsonify({"error": "unauthorized"}), 401
        routine = postgres.select_one(
            "routines", {"id": routine_id, "user_uuid": user_uuid}
        )
        if not routine:
            return flask.jsonify({"error": "not found"}), 404
        data = flask.request.get_json()
        if not data:
            return flask.jsonify({"error": "missing body"}), 400
        if not data.get("name"):
            return flask.jsonify({"error": "missing required field: name"}), 400
        max_pos = postgres.select(
            "routine_steps",
            where={"routine_id": routine_id},
            order_by="position DESC",
            limit=1,
        )
        next_pos = (max_pos[0]["position"] + 1) if max_pos else 1
        step = {
            "id": str(uuid.uuid4()),
            "routine_id": routine_id,
            "name": data["name"],
            "duration_minutes": data.get("duration_minutes"),
            "position": data.get("position", next_pos),
        }
        result = postgres.insert("routine_steps", step)
        return flask.jsonify(result), 201

    @app.route("/api/routines/<routine_id>/steps/<step_id>", methods=["PUT"])
    def api_updateStep(routine_id, step_id):
        """Update a step. Body: {name?, duration_minutes?, position?}"""
        user_uuid = _auth(flask.request)
        if not user_uuid:
            return flask.jsonify({"error": "unauthorized"}), 401
        routine = postgres.select_one(
            "routines", {"id": routine_id, "user_uuid": user_uuid}
        )
        if not routine:
            return flask.jsonify({"error": "not found"}), 404
        data = flask.request.get_json()
        if not data:
            return flask.jsonify({"error": "missing body"}), 400
        existing = postgres.select_one(
            "routine_steps", {"id": step_id, "routine_id": routine_id}
        )
        if not existing:
            return flask.jsonify({"error": "step not found"}), 404
        allowed = ["name", "duration_minutes", "position"]
        updates = {k: v for k, v in data.items() if k in allowed}
        if not updates:
            return flask.jsonify({"error": "no valid fields to update"}), 400
        result = postgres.update(
            "routine_steps", updates, {"id": step_id, "routine_id": routine_id}
        )
        return flask.jsonify(result[0] if result else {}), 200

    @app.route("/api/routines/<routine_id>/steps/<step_id>", methods=["DELETE"])
    def api_deleteStep(routine_id, step_id):
        """Delete a step from a routine."""
        user_uuid = _auth(flask.request)
        if not user_uuid:
            return flask.jsonify({"error": "unauthorized"}), 401
        routine = postgres.select_one(
            "routines", {"id": routine_id, "user_uuid": user_uuid}
        )
        if not routine:
            return flask.jsonify({"error": "not found"}), 404
        existing = postgres.select_one(
            "routine_steps", {"id": step_id, "routine_id": routine_id}
        )
        if not existing:
            return flask.jsonify({"error": "step not found"}), 404
        postgres.delete("routine_steps", {"id": step_id, "routine_id": routine_id})
        return flask.jsonify({"deleted": True}), 200

    @app.route("/api/routines/<routine_id>/steps/reorder", methods=["PUT"])
    def api_reorderSteps(routine_id):
        """Reorder steps. Body: {step_ids: [ordered list of step UUIDs]}"""
        user_uuid = _auth(flask.request)
        if not user_uuid:
            return flask.jsonify({"error": "unauthorized"}), 401
        routine = postgres.select_one(
            "routines", {"id": routine_id, "user_uuid": user_uuid}
        )
        if not routine:
            return flask.jsonify({"error": "not found"}), 404
        data = flask.request.get_json()
        if not data or not data.get("step_ids"):
            return flask.jsonify({"error": "missing step_ids"}), 400
        step_ids = data["step_ids"]
        for i, step_id in enumerate(step_ids):
            postgres.update(
                "routine_steps",
                {"position": i + 1},
                {"id": step_id, "routine_id": routine_id},
            )
        steps = postgres.select(
            "routine_steps",
            where={"routine_id": routine_id},
            order_by="position",
        )
        return flask.jsonify(steps), 200

    # ── Routine Sessions (active run-through) ─────────────────────

    @app.route("/api/routines/<routine_id>/start", methods=["POST"])
    def api_startRoutine(routine_id):
        """Start a routine session. Returns the session with first step."""
        user_uuid = _auth(flask.request)
        if not user_uuid:
            return flask.jsonify({"error": "unauthorized"}), 401
        routine = postgres.select_one(
            "routines", {"id": routine_id, "user_uuid": user_uuid}
        )
        if not routine:
            return flask.jsonify({"error": "not found"}), 404
        active = postgres.select_one(
            "routine_sessions", {"user_uuid": user_uuid, "status": "active"}
        )
        if not active:
            active = postgres.select_one(
                "routine_sessions", {"user_uuid": user_uuid, "status": "paused"}
            )
        if active:
            return flask.jsonify(
                {"error": "already have active session", "session_id": active["id"]}
            ), 409
        steps = postgres.select(
            "routine_steps",
            where={"routine_id": routine_id},
            order_by="position",
        )
        if not steps:
            return flask.jsonify({"error": "no steps in routine"}), 400
        session = {
            "id": str(uuid.uuid4()),
            "routine_id": routine_id,
            "user_uuid": user_uuid,
            "status": "active",
            "current_step_index": 0,
        }
        result = postgres.insert("routine_sessions", session)
        return flask.jsonify({"session": result, "current_step": steps[0]}), 201

    @app.route("/api/sessions/active", methods=["GET"])
    def api_getActiveSession():
        """Get the user's currently active routine session, if any."""
        user_uuid = _auth(flask.request)
        if not user_uuid:
            return flask.jsonify({"error": "unauthorized"}), 401
        session = postgres.select_one(
            "routine_sessions", {"user_uuid": user_uuid, "status": "active"}
        )
        if not session:
            session = postgres.select_one(
                "routine_sessions", {"user_uuid": user_uuid, "status": "paused"}
            )
        if not session:
            return flask.jsonify({"error": "no active session"}), 404
        routine = postgres.select_one("routines", {"id": session["routine_id"]})
        steps = postgres.select(
            "routine_steps",
            where={"routine_id": session["routine_id"]},
            order_by="position",
        )
        current_step = (
            steps[session["current_step_index"]]
            if session["current_step_index"] < len(steps)
            else None
        )
        return flask.jsonify(
            {"session": session, "routine": routine, "current_step": current_step}
        ), 200

    @app.route("/api/sessions/<session_id>/complete-step", methods=["POST"])
    def api_completeStep(session_id):
        """Mark current step done, advance to next. Body: {step_id}"""
        user_uuid = _auth(flask.request)
        if not user_uuid:
            return flask.jsonify({"error": "unauthorized"}), 401
        session = postgres.select_one(
            "routine_sessions", {"id": session_id, "user_uuid": user_uuid}
        )
        if not session:
            return flask.jsonify({"error": "not found"}), 404
        if session["status"] not in ("active", "paused"):
            return flask.jsonify({"error": "session not active"}), 400
        # Auto-resume if paused
        if session["status"] == "paused":
            postgres.update(
                "routine_sessions",
                {"status": "active", "paused_at": None},
                {"id": session_id},
            )
        data = flask.request.get_json() or {}
        steps = postgres.select(
            "routine_steps",
            where={"routine_id": session["routine_id"]},
            order_by="position",
        )
        current_index = session["current_step_index"]
        current_step = steps[current_index] if current_index < len(steps) else None

        # Record step result
        if current_step:
            _record_step_result(
                session_id, current_step["id"], current_index, "completed", session
            )

        next_index = current_index + 1
        if next_index >= len(steps):
            # Session complete — compute celebration data
            celebration = _complete_session_with_celebration(
                session_id, user_uuid, session
            )
            return flask.jsonify(
                {
                    "session": {"status": "completed"},
                    "next_step": None,
                    "celebration": celebration,
                }
            ), 200
        postgres.update(
            "routine_sessions", {"current_step_index": next_index}, {"id": session_id}
        )
        return flask.jsonify(
            {
                "session": {"current_step_index": next_index},
                "next_step": steps[next_index],
            }
        ), 200

    @app.route("/api/sessions/<session_id>/skip-step", methods=["POST"])
    def api_skipStep(session_id):
        """Skip current step, advance to next. Body: {step_id}"""
        user_uuid = _auth(flask.request)
        if not user_uuid:
            return flask.jsonify({"error": "unauthorized"}), 401
        session = postgres.select_one(
            "routine_sessions", {"id": session_id, "user_uuid": user_uuid}
        )
        if not session:
            return flask.jsonify({"error": "not found"}), 404
        if session["status"] not in ("active", "paused"):
            return flask.jsonify({"error": "session not active"}), 400
        # Auto-resume if paused
        if session["status"] == "paused":
            postgres.update(
                "routine_sessions",
                {"status": "active", "paused_at": None},
                {"id": session_id},
            )
        steps = postgres.select(
            "routine_steps",
            where={"routine_id": session["routine_id"]},
            order_by="position",
        )
        current_index = session["current_step_index"]
        current_step = steps[current_index] if current_index < len(steps) else None

        # Record step result as skipped
        if current_step:
            _record_step_result(
                session_id, current_step["id"], current_index, "skipped", session
            )

        next_index = current_index + 1
        if next_index >= len(steps):
            celebration = _complete_session_with_celebration(
                session_id, user_uuid, session
            )
            return flask.jsonify(
                {
                    "session": {"status": "completed"},
                    "next_step": None,
                    "celebration": celebration,
                }
            ), 200
        postgres.update(
            "routine_sessions", {"current_step_index": next_index}, {"id": session_id}
        )
        return flask.jsonify(
            {
                "session": {"current_step_index": next_index},
                "next_step": steps[next_index],
            }
        ), 200

    @app.route("/api/sessions/<session_id>/cancel", methods=["POST"])
    def api_cancelSession(session_id):
        """Cancel an active routine session."""
        user_uuid = _auth(flask.request)
        if not user_uuid:
            return flask.jsonify({"error": "unauthorized"}), 401
        session = postgres.select_one(
            "routine_sessions", {"id": session_id, "user_uuid": user_uuid}
        )
        if not session:
            return flask.jsonify({"error": "not found"}), 404
        postgres.update("routine_sessions", {"status": "cancelled"}, {"id": session_id})
        return flask.jsonify({"session": {"status": "cancelled"}}), 200

    # ── Routine History / Stats ───────────────────────────────────

    @app.route("/api/routines/<routine_id>/history", methods=["GET"])
    def api_routineHistory(routine_id):
        """Get past sessions for a routine. Query: ?days=7"""
        user_uuid = _auth(flask.request)
        if not user_uuid:
            return flask.jsonify({"error": "unauthorized"}), 401
        routine = postgres.select_one(
            "routines", {"id": routine_id, "user_uuid": user_uuid}
        )
        if not routine:
            return flask.jsonify({"error": "not found"}), 404
        days = flask.request.args.get("days", 7, type=int)
        sessions = postgres.select(
            "routine_sessions",
            where={"routine_id": routine_id},
            order_by="created_at DESC",
            limit=days,
        )
        return flask.jsonify(sessions), 200

    # ── Routine Scheduling ────────────────────────────────────────

    @app.route("/api/routines/schedules", methods=["GET"])
    def api_listAllSchedules():
        """Get all schedules for the user's routines with routine metadata."""
        user_uuid = _auth(flask.request)
        if not user_uuid:
            return flask.jsonify({"error": "unauthorized"}), 401
        routines = postgres.select("routines", where={"user_uuid": user_uuid})
        if not routines:
            return flask.jsonify([]), 200

        result = []
        for r in routines:
            sched = postgres.select_one("routine_schedules", {"routine_id": r["id"]})
            if not sched:
                continue
            steps = postgres.select("routine_steps", where={"routine_id": r["id"]})
            total_duration = sum(s.get("duration_minutes") or 0 for s in steps)
            result.append(
                {
                    "routine_id": r["id"],
                    "routine_name": r.get("name", ""),
                    "routine_icon": r.get("icon", ""),
                    "days": sched.get("days", []),
                    "time": sched.get("time"),
                    "remind": sched.get("remind", True),
                    "total_duration_minutes": total_duration,
                }
            )
        return flask.jsonify(result), 200

    @app.route("/api/routines/<routine_id>/schedule", methods=["PUT"])
    def api_setRoutineSchedule(routine_id):
        """Set when this routine should run. Body: {days: ["mon","tue",...], time: "08:00", remind: true}"""
        user_uuid = _auth(flask.request)
        if not user_uuid:
            return flask.jsonify({"error": "unauthorized"}), 401
        routine = postgres.select_one(
            "routines", {"id": routine_id, "user_uuid": user_uuid}
        )
        if not routine:
            return flask.jsonify({"error": "not found"}), 404
        data = flask.request.get_json()
        if not data:
            return flask.jsonify({"error": "missing body"}), 400
        existing = postgres.select_one("routine_schedules", {"routine_id": routine_id})
        schedule_data = {
            "routine_id": routine_id,
            "days": json.dumps(data.get("days", [])),
            "time": data.get("time"),
            "remind": data.get("remind", True),
        }
        if existing:
            result = postgres.update(
                "routine_schedules", schedule_data, {"routine_id": routine_id}
            )
            return flask.jsonify(result[0] if result else {}), 200
        else:
            schedule_data["id"] = str(uuid.uuid4())
            result = postgres.insert("routine_schedules", schedule_data)
            return flask.jsonify(result), 201

    @app.route("/api/routines/<routine_id>/schedule", methods=["GET"])
    def api_getRoutineSchedule(routine_id):
        """Get the schedule for a routine."""
        user_uuid = _auth(flask.request)
        if not user_uuid:
            return flask.jsonify({"error": "unauthorized"}), 401
        routine = postgres.select_one(
            "routines", {"id": routine_id, "user_uuid": user_uuid}
        )
        if not routine:
            return flask.jsonify({"error": "not found"}), 404
        schedule = postgres.select_one("routine_schedules", {"routine_id": routine_id})
        if not schedule:
            return flask.jsonify({"error": "no schedule set"}), 404
        return flask.jsonify(schedule), 200

    @app.route("/api/routines/<routine_id>/schedule", methods=["DELETE"])
    def api_deleteRoutineSchedule(routine_id):
        """Remove the schedule from a routine."""
        user_uuid = _auth(flask.request)
        if not user_uuid:
            return flask.jsonify({"error": "unauthorized"}), 401
        routine = postgres.select_one(
            "routines", {"id": routine_id, "user_uuid": user_uuid}
        )
        if not routine:
            return flask.jsonify({"error": "not found"}), 404
        postgres.delete("routine_schedules", {"routine_id": routine_id})
        return flask.jsonify({"deleted": True}), 200
