

import sys, os, json, time, uuid
from typing import Optional
import requests
from rich import box
from rich.console import Console
from rich.panel import Panel
from rich.prompt import Prompt, Confirm
from rich.table import Table
from rich.text import Text
from rich.align import Align
from rich.progress import BarColumn, Progress
from rich.text import Text

# Local imports
from .api_client import APIClient
from .session_manager import load_session, save_session
from .utils import pretty_time
import threading
import time
from rich.table import Table
from rich.panel import Panel
from rich.live import Live
console = Console()


# =========================
# Main CLI class (Rich-driven)
# =========================
class QuizCLI:
    def __init__(self, api: APIClient):
        self.api = api
        self.session = load_session()
        self.start_time = None

    # ------------------------
    # Startup banner
    # ------------------------
    def display_banner(self):
        console.clear()
        console.rule("[bold cyan]🚀 Starting CODEHOUSE CLOUD[/bold cyan]", style="cyan")
        console.print()
        # Typing effect
        logo_sys = self.api.sys_logo()

        if logo_sys.status_code in (200, 201):
            body = logo_sys.json()
            logo = body.get("logo")
            for ch in logo:
                console.print(ch, end="")
                time.sleep(0.0015)
            console.print("\n")
        
        else:
            console.rule("[bold cyan]This is CODEHOUSE CLOUD[/bold cyan]", style="cyan")

        

        panel = Panel(
            Align.center(Text("CODEHOUSE CLOUD", style="bold cyan")),
            box=box.HEAVY,
            border_style="cyan",
            padding=(1, 3),
            title="[bright_white]Applinet Technology[/bright_white]",
            subtitle="[dim]Empowering Africans through technology[/dim]",
        )
        console.print(panel)
        console.print()





    


    # ------------------------
    # Registration
    # ------------------------
    def register_flow(self):
        console.print(Panel.fit("[bold yellow]Create Account[/bold yellow]"))
        fullname = Prompt.ask("Full name")

        while True:
            email = Prompt.ask("Email")
            if "@" not in email or "." not in email.split("@")[-1]:
                console.print("[red]❌ Invalid email format. Please enter a valid email address.[/red]")
            
            vremail = self.api.check_email_exist(email)
            if vremail.status_code in (400, 404):
                body = vremail.json()
                detail = body.get("detail")
                console.print(Panel.fit(f"[red]❌ {detail} [/red]"))
            
            else:
                break

            
        while True:
            password = Prompt.ask("Password", password=True)

            if len(password) < 6:
                console.print("[red]Password must be at least 6 characters[/red]")
                # return
            else:
                break

        while True:
            confirm_pw = Prompt.ask("Confirm password", password=True)
            if password != confirm_pw:
                console.print("[red]Passwords do not match[/red]")
                # return
            else:
                break

        while True:
            phone = Prompt.ask("Phone (e.g. +234...)", default="")
            vrphone = self.api.check_phone_exist(phone)
            if vrphone.status_code in (400, 404):
                body = vrphone.json()
                detail = body.get("detail")
                console.print(Panel.fit(f"[red]❌ {detail} [/red]"))
            
            else:
                break

        country = Prompt.ask("Country code (e.g. nga, usa)", default="nga")

        r = self.api.register(fullname, email, password, phone, country)
        if r.status_code in (200, 201):
            console.print(Panel.fit("[green]Registration OK — check your email for confirmation token[/green]"))
            token = Prompt.ask("Enter confirmation token (from email)")
            vr = self.api.verify_email(email, token)
            if vr.status_code in (200, 201):
                body = vr.json()
                sti = body.get("sti")
                console.print(Panel.fit(f"[green]Email verified! Your STI: [bold]{sti}[/bold][/green]"))
                # Save minimal session (no token yet)
                self.session.update({"email": email, "sti": sti})
                save_session(self.session)
            else:
                console.print(Panel.fit(f"[red]Verify failed: {vr.status_code} {vr.text}[/red]"))
        else:
            try:
                msg = r.json()
            except Exception:
                msg = r.text
            console.print(Panel.fit(f"[red]Registration failed: {msg}[/red]"))

    # ------------------------
    # Login
    # ------------------------
    def login_flow(self, prefill_sti: Optional[str] = None):
        console.print(Panel.fit("[bold yellow]Login[/bold yellow]"))
        sti = prefill_sti or Prompt.ask("Enter STI")
        password = Prompt.ask("Password", password=True)

        try:
            r = self.api.login(sti, password)
            # print("user logined: ", r.text)
        except Exception as e:
            console.print(Panel.fit(f"[red]Network/login error: {e}[/red]"))
            return

        if r.status_code == 200:
            body = r.json()
            token = body.get("token")
            sti_resp = body.get("sti") or sti

            # Save session locally
            self.api.token = token
            self.api.sti = sti_resp
            self.session.update({"sti": sti_resp, "token": token})
            save_session(self.session)
            # print("user logined: ", r.text)

            console.print(Panel.fit("[green]Login successful![/green]"))
            self.after_login(sti_resp)
        else:
            try:
                body = r.json()
                console.print(Panel.fit(f"[red]Login failed: {body.get('detail') or body}[/red]"))
            except Exception:
                console.print(Panel.fit(f"[red]Login failed: status {r.status_code}[/red]"))

    # ------------------------
    # Password recovery
    # ------------------------
    def password_recovery_flow(self):
        console.print(Panel.fit("[bold yellow]🔑 Password / STI Recovery[/bold yellow]"))
        sti = Prompt.ask("Enter your STI", default="")
        if not sti:
            console.print("[yellow]You must provide STI to request recovery.[/yellow]")
            return

        r = self.api.recover_request(sti)
        if r.status_code in (200, 201):
            console.print(Panel.fit("[green]Recovery token sent to your email.[/green]"))
            token = Prompt.ask("Enter recovery token (from your email)")
            vr = self.api.verify_recovery(token, sti)
            if vr.status_code in (200, 201):
                console.print(Panel.fit("[green]Recovery token verified. Continue to reset password.[/green]"))
                new_pw = Prompt.ask("New password", password=True)
                confirm_pw = Prompt.ask("Confirm password", password=True)
                if new_pw != confirm_pw:
                    console.print("[red]Passwords do not match[/red]")
                    return
                rr = self.api.reset_password(sti, token, new_pw)
                if rr.status_code in (200, 201):
                    console.print("[green]Password reset successful. Please login.[/green]")
                else:
                    console.print(f"[red]Reset failed: {rr.text}[/red]")
            else:
                console.print(f"[red]Verification failed: {vr.status_code}[/red]")
        else:
            console.print(f"[red]Recovery request failed: {r.status_code}[/red]")

        # ------------------------
    # After login + display top scorers
    # ------------------------
    def after_login(self, sti):
        # Check pending quizzes
        pending = self.api.get_pending_quiz(sti)
        if pending.status_code == 200:
            data = pending.json()
            if data.get("has_pending"):
                console.print(Panel.fit(
                    f"[bold yellow]⚠️ You have an unfinished quiz![/bold yellow]\n\n"
                    f"Course: [cyan]{data['course']['title']}[/cyan]\n"
                    f"Last Question: [green]{data['last_q']}[/green]\n",
                    title="Pending Quiz"
                ))

                if Confirm.ask("Do you want to resume your quiz now?"):
                    self.session["selected_course"] = data["course"]
                    self.session["last_q"] = data["last_q"]
                    save_session(self.session)

                    # Show top scorers before resuming
                    self.show_top_scorers(data["course"]["id"], sti)
                    console.print("[bold cyan]Resuming your previous quiz...[/bold cyan]")
                    self.quiz_loop(sti)
                    return

        # Fetch available courses
        r = self.api.get_courses(sti)
        if r.status_code != 200:
            console.print(Panel.fit(f"[red]Failed to fetch courses: {r.status_code}[/red]"))
            return

        courses = r.json()
        table = Table(title="Available Courses", header_style="bold cyan")
        table.add_column("#", justify="center")
        table.add_column("Course ID")
        table.add_column("Title")

        for idx, c in enumerate(courses, 1):
            table.add_row(str(idx), str(c.get("id")), c.get("title"))

        console.print(table)

        choice = Prompt.ask("Choose course number (or 'q' to quit)", default="1")
        if choice.lower() == "q":
            return

        try:
            selected = courses[int(choice) - 1]
        except Exception:
            console.print(Panel.fit("[red]Invalid selection[/red]"))
            return

        self.session["selected_course"] = {
            "id": selected.get("id"),
            "title": selected.get("title"),
        }
        save_session(self.session)

        # Display top 20 scorers before starting the quiz
        self.show_top_scorers(selected.get("id"), sti)

        # Start the quiz
        self.quiz_loop(sti)


        # ------------------------
        # Display top 20 scorers helper
        # ------------------------
    def show_top_scorers(self, course_id, sti):
        console.print(f"\n[bold magenta]🏆 Top 20 Scorers for Course {course_id}[/bold magenta]\n")
        try:
            res = self.api.leaders_top_scorers(course_id, sti)
            if res.status_code == 200:
                data = res.json().get("top_scorers", [])[:20]
                table = Table(title="Top Scorers", header_style="bold green")
                table.add_column("#", justify="center")
                table.add_column("Username", justify="left")
                table.add_column("Avg Score", justify="center")
                table.add_column("Accuracy (%)", justify="center")
                table.add_column("QuizCoins", justify="center")

                for i, s in enumerate(data, 1):
                    uname = s.get("username", "---")
                    avg = s.get("avg_score", 0)
                    acc = s.get("accuracy", 0)
                    coins = s.get("quizcoin_earned", 0)
                    style = "bold yellow" if str(s.get("sti") or "").lower() == str(sti).lower() else ""
                    table.add_row(
                        str(i),
                        f"[{style}]{uname}[/{style}]" if style else uname,
                        str(avg),
                        str(acc),
                        str(coins)
                    )

                console.print(table)
            else:
                console.print("[red]Unable to fetch leaderboard data[/red]")
        except Exception:
            console.print("[red]Error fetching leaderboard[/red]")

    # # ------------------------
    # # After login
    # # ------------------------
    # def after_login(self, sti):

    #     # After login
    #     pending = self.api.get_pending_quiz(sti)
    #     # print("I have pending questions ", pending)

    #     if pending.status_code == 200:
    #         data = pending.json()
    #         if data.get("has_pending"):
    #             console.print(Panel.fit(
    #                 f"[bold yellow]⚠️ You have an unfinished quiz![/bold yellow]\n\n"
    #                 f"Course: [cyan]{data['course']['title']}[/cyan]\n"
    #                 f"Last Question: [green]{data['last_q']}[/green]\n",
    #                 title="Pending Quiz"
    #             ))

    #             if Confirm.ask("Do you want to resume your quiz now?"):
    #                 self.session["selected_course"] = data["course"]
    #                 self.session["last_q"] = data["last_q"]
    #                 save_session(self.session)
    #                 console.print("[bold cyan]Resuming your previous quiz...[/bold cyan]")
    #                 self.quiz_loop(sti)  # same method you shared
    #                 return


    #     r = self.api.get_courses(sti)
    #     if r.status_code != 200:
    #         console.print(Panel.fit(f"[red]Failed to fetch courses: {r.status_code}[/red]"))
    #         return

    #     courses = r.json()
    #     table = Table(title="Available Courses", header_style="bold cyan")
    #     table.add_column("#", justify="center")
    #     table.add_column("Course ID")
    #     table.add_column("Title")

    #     for idx, c in enumerate(courses, 1):
    #         table.add_row(str(idx), str(c.get("id")), c.get("title"))

    #     console.print(table)

    #     choice = Prompt.ask("Choose course number (or 'q' to quit)", default="1")
    #     if choice.lower() == "q":
    #         return

    #     try:
    #         selected = courses[int(choice) - 1]
    #     except Exception:
    #         console.print(Panel.fit("[red]Invalid selection[/red]"))
    #         return

    #     self.session["selected_course"] = {
    #         "id": selected.get("id"),
    #         "title": selected.get("title"),
    #     }
    #     save_session(self.session)
    #     self.quiz_loop(sti)

    #     # -------------------------------
    # #  CLI Live Leaderboard (Rich)
    # # -------------------------------
    # def start_quiz_with_leaderboard(self, course_id, sti, cli_only=True, refresh_interval=8):
    #     """
    #     Runs the quiz while continuously refreshing the leaderboard data in real-time.
    #     """
    #     console = Console()
    #     stop_refresh = threading.Event()
    #     my_sti = self.api.sti or self.session.get("sti")
    #     last_rank = None

    #     def build_leaderboard():
    #         try:
    #             res = self.api.get_top(course_id, sti, cli_only=cli_only)  # API call to backend
    #             if res.status_code == 200:
    #                 data = res.json().get("top_scorers", [])[:20]
    #                 table = Table(title="🏆 Live Leaderboard", header_style="bold magenta")
    #                 table.add_column("#", justify="center")
    #                 table.add_column("Username", justify="left")
    #                 table.add_column("Avg Score", justify="center")
    #                 table.add_column("Accuracy (%)", justify="center")

    #                 for i, s in enumerate(data, 1):
    #                     uname = str(s.get("username", "---"))
    #                     avg = f"{s.get('avg_score', 0)}"
    #                     acc = f"{s.get('accuracy', 0)}"
    #                     table.add_row(str(i), uname, avg, acc)
    #                 return table
    #         except Exception:
    #             pass

    #         table = Table(title="🏆 Leaderboard (Unavailable)", header_style="bold red")
    #         table.add_column("Info", justify="center")
    #         table.add_row("Unable to fetch leaderboard data.")
    #         return table

    #     def refresh_leaderboard():
    #         nonlocal last_rank
    #         with Live(build_leaderboard(), refresh_per_second=0.5, console=console) as live:
    #             while not stop_refresh.is_set():
    #                 try:
    #                     res = self.api.get_top(course_id, sti, cli_only=cli_only)
    #                     if res.status_code == 200:
    #                         data = res.json().get("top_scorers", [])[:20]
    #                         live.update(build_leaderboard())

    #                         # Notify user if rank changes
    #                         for i, s in enumerate(data, 1):
    #                             user_match = str(s.get("sti") or s.get("username") or "").lower() == str(my_sti or "").lower()
    #                             if user_match and i != last_rank:
    #                                 last_rank = i
    #                                 console.print(
    #                                     Panel.fit(f"[bold green]Leaderboard update: You are now ranked #{i}![/bold green]")
    #                                 )
    #                     time.sleep(refresh_interval)
    #                 except Exception:
    #                     time.sleep(refresh_interval)
    #                     continue

    #     # Start background leaderboard refresh
    #     console.print("[cyan]Fetching leaderboard data...[/cyan]\n")
    #     leaderboard_thread = threading.Thread(target=refresh_leaderboard, daemon=True)
    #     leaderboard_thread.start()

    #     # Run the quiz
    #     try:
    #         console.print("\n[cyan]Starting your quiz... Good luck![/cyan]\n")
    #         self.run_quiz(course_id, sti)
    #     except Exception as e:
    #         console.print(f"[red]Error during quiz: {e}[/red]")
    #     finally:
    #         stop_refresh.set()
    #         leaderboard_thread.join(timeout=3)
    #         console.print("\n[bold magenta]🎉 Quiz completed! Final leaderboard update below:[/bold magenta]\n")
    #         console.print(build_leaderboard())




    # def start_quiz_with_leaderboard(self, course_id, sti):
    #     """
    #     Runs the quiz with a live gamified CLI leaderboard, highlighting:
    #     - Top 3 users with emojis and colors
    #     - Current user in yellow
    #     - Accuracy bars
    #     - Current user's stats
    #     - Mini sparkline showing user's score trend
    #     """
    #     console = Console()
    #     stop_refresh = threading.Event()
    #     my_sti = self.api.sti or self.session.get("sti")
    #     last_rank = None
    #     user_stats = {}
    #     score_trend = []

    #     def build_leaderboard():
    #         """
    #         Build Rich table with gamification and sparkline for current user
    #         """
    #         try:
    #             res = self.api.leaders_top_scorers(course_id, sti)
    #             if res.status_code == 200:
    #                 data = res.json().get("top_scorers", [])[:20]

    #                 table = Table(title="🏆 Live Leaderboard", header_style="bold magenta")
    #                 table.add_column("#", justify="center")
    #                 table.add_column("Username", justify="left")
    #                 table.add_column("Avg Score", justify="center")
    #                 table.add_column("Accuracy (%)", justify="center")
    #                 table.add_column("Progress", justify="left")
    #                 table.add_column("Trend", justify="left")

    #                 for i, s in enumerate(data, 1):
    #                     uname = str(s.get("username", "---"))
    #                     avg = s.get("avg_score", 0)
    #                     acc = s.get("accuracy", 0)
    #                     coins = s.get("quizcoin_earned", 0)

    #                     # Save current user's stats
    #                     if str(s.get("sti") or s.get("username") or "").lower() == str(my_sti or "").lower():
    #                         user_stats.update({"rank": i, "avg_score": avg, "accuracy": acc, "coins": coins})
    #                         score_trend.append(avg)

    #                     # Accuracy bar
    #                     progress_bar = Text("█" * int(acc // 5) + "░" * (20 - int(acc // 5)))

    #                     # Mini sparkline for trend
    #                     max_bar = 20
    #                     if score_trend:
    #                         sparkline = "".join(
    #                             "█" if x >= max(score_trend) * idx / max_bar else "░"
    #                             for idx, x in enumerate(score_trend[-max_bar:])
    #                         )
    #                     else:
    #                         sparkline = ""

    #                     # Determine style and emoji
    #                     style = ""
    #                     emoji = ""
    #                     if str(s.get("sti") or s.get("username") or "").lower() == str(my_sti or "").lower():
    #                         style = "bold yellow"
    #                     elif i == 1:
    #                         style = "bold gold1"
    #                         emoji = "🥇 "
    #                     elif i == 2:
    #                         style = "bold silver"
    #                         emoji = "🥈 "
    #                     elif i == 3:
    #                         style = "bold dark_orange"
    #                         emoji = "🥉 "

    #                     table.add_row(
    #                         str(i),
    #                         f"{emoji}[{style}]{uname}[/{style}]" if style else f"{emoji}{uname}",
    #                         f"[{style}]{avg}[/{style}]" if style else str(avg),
    #                         f"[{style}]{acc}[/{style}]" if style else str(acc),
    #                         f"[{style}]{progress_bar}[/{style}]" if style else progress_bar,
    #                         f"[{style}]{sparkline}[/{style}]" if style else sparkline
    #                     )

    #                 return table
    #         except Exception:
    #             pass

    #         table = Table(title="🏆 Leaderboard (Unavailable)", header_style="bold red")
    #         table.add_column("Info", justify="center")
    #         table.add_row("Unable to fetch leaderboard data.")
    #         return table

    #     def refresh_leaderboard():
    #         nonlocal last_rank
    #         with Live(build_leaderboard(), refresh_per_second=0.5, console=console) as live:
    #             while not stop_refresh.is_set():
    #                 try:
    #                     res = self.api.leaders_top_scorers(course_id, sti)
    #                     if res.status_code == 200:
    #                         data = res.json().get("top_scorers", [])[:20]
    #                         live.update(build_leaderboard())

    #                         # Notify current user if rank changes
    #                         for i, s in enumerate(data, 1):
    #                             user_match = str(s.get("sti") or s.get("username") or "").lower() == str(my_sti or "").lower()
    #                             if user_match and i != last_rank:
    #                                 last_rank = i
    #                                 console.print(
    #                                     Panel.fit(
    #                                         f"🏅 [bold green]Leaderboard Update![/bold green] 🏅\n\n"
    #                                         f"Rank: {user_stats.get('rank')}\n"
    #                                         f"Avg Score: {user_stats.get('avg_score')}\n"
    #                                         f"Accuracy: {user_stats.get('accuracy')}%\n"
    #                                         f"QuizCoins: {user_stats.get('coins')}\n"
    #                                         f"Trend: {' '.join('█' if x >= max(score_trend) * idx / 20 else '░' for idx, x in enumerate(score_trend[-20:]))}",
    #                                         border_style="green",
    #                                         title="🎯 Your Stats"
    #                                     )
    #                                 )
    #                     time.sleep(5)
    #                 except Exception:
    #                     time.sleep(5)
    #                     continue

    #     # Start leaderboard refresh
    #     console.print("[cyan]Fetching leaderboard data...[/cyan]\n")
    #     leaderboard_thread = threading.Thread(target=refresh_leaderboard, daemon=True)
    #     leaderboard_thread.start()

    #     # Run the quiz
    #     try:
    #         console.print("\n[cyan]Starting your quiz... Good luck![/cyan]\n")
    #         self.run()
    #     except Exception as e:
    #         console.print(f"[red]Error during quiz: {e}[/red]")
    #     finally:
    #         stop_refresh.set()
    #         leaderboard_thread.join(timeout=3)
    #         console.print("\n[bold magenta]🎉 Quiz completed! Final leaderboard update below:[/bold magenta]\n")
    #         console.print(build_leaderboard())




    def quiz_loop(self, sti):
        course = self.session.get("selected_course")
        if not course:
            console.print("[red]No course selected[/red]")
            return

        course_id = course["id"]
        
        pending_r = self.api.get_pending_quiz(sti)
        if pending_r.status_code == 200:
            pending_data = pending_r.json()
            if pending_data.get("has_pending"):
                course = pending_data["course"]
                current_attempt_id = pending_data["attempt_id"]
                current_q = int(pending_data.get("last_q", 1))
                console.print(Panel.fit(f"[yellow]Resuming previous quiz on {course['title']}[/yellow]"))
            else:
                current_attempt_id = None
                current_q = 1
        else:
            current_attempt_id = None
            current_q = 1

        console.print(Panel.fit(f"[bold cyan]🚀 Starting Quiz: {course['title']}[/bold cyan]"))



        while True:
            # 1️⃣ Fetch question from API
            q_r = self.api.get_question(course_id, current_q, sti)
            if q_r.status_code != 200:
                console.print(Panel.fit("[red]Error loading question[/red]"))
                return

            q = q_r.json()
            q_id = q.get("id")
            console.print(Panel.fit(
                Text(f"Question {current_q}\n\n{q.get('text')}", style="bold cyan"),
                title=f"{course.get('title')}"
            ))

            options = q.get("options", [])
            for i, opt in enumerate(options, 1):
                console.print(f"[cyan]{i}.[/cyan] {opt}")

            console.print("[yellow]Type the option number. 'e' to exit quiz.[/yellow]")

            # 2️⃣ Capture user answer
            q_start = time.time()
            ans = Prompt.ask("Your answer")
            time_spent = int(time.time() - q_start)

            # if ans.lower() in ("t", "top"):
            #     return self.start_quiz_with_leaderboard(course_id, sti)

                

            if ans.lower() in ("e", "end"):
                if Confirm.ask("Quit and save progress?"):
                    self.session["last_q"] = current_q
                    save_session(self.session)
                    console.print("[cyan]Progress saved. You can resume later.[/cyan]")
                    return
                else:
                    continue

            if not ans.isdigit() or int(ans) < 1 or int(ans) > len(options):
                console.print("[red]Invalid option number[/red]")
                continue

            selected_text = options[int(ans) - 1]

            # 3️⃣ Submit answer to API (auto moves next)
            sub_r = self.api.submit_answer(course_id, q_id, selected_text, time_spent, source="cli", sti=sti)
            if sub_r.status_code not in (200, 201):
                console.print(f"[red]Submit failed: {sub_r.status_code}[/red]")
                continue

            sub = sub_r.json()

            # 4️⃣ Display result feedback
            correct = sub.get("correct", False)
            msg_color = "green" if correct else "red"
            console.print(Panel.fit(
                f"{'✅ Correct!' if correct else '❌ Incorrect!'}  "
                f"({pretty_time(time_spent)})",
                style=msg_color
            ))

            # 5️⃣ Display live performance summary
            console.print(Panel.fit(
                f"[bold yellow]Progress:[/bold yellow] {sub.get('progress', '---')}  "
                f"| [bold cyan]Score:[/bold cyan] {sub.get('score', 0)}%  "
                f"| [bold magenta]Rank:[/bold magenta] #{sub.get('rank', '---')}\n\n"
                f"[bold green]{sub.get('motivation', '')}[/bold green]",
                title="📊 Performance Update",
            ))

            # 6️⃣ Check if quiz completed
            if sub.get("completed"):
                console.print(Panel.fit("[bold cyan]🏁 Quiz Finished![/bold cyan]"))
                console.print(
                    Panel.fit(
                        f"[bold green]Final Score: {sub.get('final_score', 0)}%[/bold green]\n"
                        f"Accuracy: {sub.get('accuracy', 0)}%\n"
                        f"Rank: #{sub.get('rank', '---')}\n\n"
                        f"[cyan]{sub.get('motivation', 'Well done!')}[/cyan]",
                        title="🎯 Result Summary"
                    )
                )
                break

            # 7️⃣ Move to next question
            next_q = sub.get("next_question")
            if next_q:
                current_q += 1
            else:
                console.print("[yellow]No more questions available.[/yellow]")
                break

            # Small pause for readability
            time.sleep(1)
    
    

    def withdraw_flow(self):
        console.print(Panel.fit("[bold cyan]💰 QUIZCOIN ACCOUNT[/bold cyan]"))
        console.print("[red]QUIZCOIN ONLY FOR CODEHOUSE CLOUD STUDENTS[/red]")

        # console.print(Panel.fit("[bold magenta]💸 Request Withdrawal[/bold magenta]"))
        # try:
        #     amount = float(Prompt.ask("Amount in QuizCoin"))
        # except Exception:
        #     console.print("[red]Invalid amount[/red]")
        #     return

        # bank_name = Prompt.ask("Bank name")
        # account_number = Prompt.ask("Account number")
        # ngn_rate = None

        # if Confirm.ask("Provide NGN rate? (optional)", default=False):
        #     try:
        #         ngn_rate = float(Prompt.ask("NGN rate"))
        #     except Exception:
        #         pass

        # if not Confirm.ask(f"Confirm withdraw {amount} QuizCoin to {account_number} at {bank_name}?"):
        #     console.print("[yellow]Cancelled[/yellow]")
        #     return

        # r = self.api.request_withdraw(amount, ngn_rate, bank_name, account_number)
        # if r.status_code in (200, 201):
        #     console.print("[green]Withdrawal request submitted![/green]")
        #     try:
        #         data = r.json()
        #         console.print(f"Withdrawal ID: {data.get('withdrawal_id')}")
        #     except Exception:
        #         pass
        # else:
        #     console.print(f"[red]Withdrawal failed: {r.text}[/red]")

    # ------------------------
    # Run CLI
    # ------------------------
    def run(self):
        # self.display_banner()
        while True:
            try:
                menu = Table.grid(expand=True)
                menu.add_column(justify="center")
                menu.add_row(
                     
                    "[bold cyan]1[/bold cyan] Login    "
                    "[bold cyan]2[/bold cyan] Register    "
                    "[bold cyan]3[/bold cyan] Recover    "
                    "[bold cyan]4[/bold cyan] QUIZCOIN ACCOUNT    "
                    "[bold cyan]5[/bold cyan] Withdraw    "
                    "[bold cyan]6[/bold cyan] Exit"
                )
                console.print(Panel.fit(menu, title="[bold cyan]CODEHOUSE CLOUD[/bold cyan]"))
                choice = Prompt.ask("Select", choices=["1", "2", "3", "4", "5", "6"], default="1")

                if choice == "1":
                    self.login_flow()
                elif choice == "2":
                    self.register_flow()
                elif choice == "3":
                    self.password_recovery_flow()
                elif choice == "4":
                    self.wallet_flow()
                elif choice == "5":
                    self.withdraw_flow()
                else:
                    console.print("[bold]Goodbye![/bold]\n[dim cyan]Powered by Applinet Technology[/dim cyan]")
                    sys.exit(0)
            except KeyboardInterrupt:
                console.print()
                if Confirm.ask("Exit application?"):
                    console.print("Goodbye!", style="blue")
                    sys.exit(0)

# import sys, os, json, time, uuid
# from typing import Optional, Dict, Any, Callable
# from dataclasses import dataclass
# import requests
# from requests import RequestException

# # Rich imports
# from rich import box, print as rprint
# from rich.console import Console
# from rich.panel import Panel
# from rich.align import Align
# from rich.prompt import Prompt, Confirm
# from rich.table import Table
# from rich.live import Live
# from rich.text import Text
# from rich.progress import Progress, SpinnerColumn, TextColumn
# from rich.layout import Layout
# from rich.columns import Columns

# console = Console()
# from rich.console import Console
# from rich.panel import Panel
# from rich.prompt import Prompt, Confirm
# from rich.table import Table
# from rich.text import Text
# from rich import box
# import time

# from .api_client import APIClient
# from .session_manager import load_session, save_session
# from .utils import pretty_time
# from .ascii_logo import LOGO

# console = Console()


# # =========================
# # Main CLI class (Rich-driven)
# # =========================
# class QuizCLI:
#     def __init__(self, api: APIClient, stl:sti):
#         self.api = api
#         self.session = load_session()
#         self.start_time = None

#     # Startup banner with typing effect
#     def display_banner(self):
#         console.clear()
#         console.rule("[bold cyan]🚀 Starting CODEHOUSE CLOUD[/bold cyan]", style="cyan")
#         console.print()
#         # typing effect
#         for ch in LOGO:
#             console.print(ch, end="")
#             time.sleep(0.0015)
#         console.print("\n")
#         panel = Panel(Align.center(Text("CODEHOUSE CLOUD", style="bold cyan")),
#                       box=box.HEAVY, border_style="cyan",
#                       padding=(1, 3),
#                       title="[bright_white]Applinet Technology[/bright_white]",
#                       subtitle="[dim]Empowering Africans through technology[/dim]")
#         console.print(panel)
#         console.print()

#     # ---------- flows ----------
#     def register_flow(self):
#         console.print(Panel.fit("[bold yellow]Create Account[/bold yellow]"))
#         fullname = Prompt.ask("Full name")
#         email = Prompt.ask("Email")
#         password = Prompt.ask("Password", password=True)
#         if len(password) < 6:
#             console.print("[red]Password must be at least 6 characters[/red]")
#             return
#         confirm_pw = Prompt.ask("Confirm password", password=True)
#         if password != confirm_pw:
#             console.print("[red]Passwords do not match[/red]")
#             return
#         phone = Prompt.ask("Phone (e.g. +234...)", default="")
#         country = Prompt.ask("Country code (e.g. nga, usa)", default="nga")
#         r = self.api.register(fullname, email, password, phone, country)
#         if r.status_code in (200, 201):
#             console.print(Panel.fit("[green]Registration OK — check your email for confirmation token[/green]"))
#             token = Prompt.ask("Enter confirmation token (from email)")
#             vr = self.api.verify_email(email, token)
#             if vr.status_code in (200, 201):
#                 body = vr.json()
#                 sti = body.get("sti")
#                 console.print(Panel.fit(f"[green]Email verified! Your STI: [bold]{sti}[/bold][/green]"))
#                 # save minimal session (no token yet)
#                 self.session.update({"email": email, "sti": sti})
#                 save_session(self.session)
#             else:
#                 console.print(Panel.fit(f"[red]Verify failed: {vr.status_code} {getattr(vr, 'text', '')}[/red]"))
#         else:
#             try:
#                 msg = r.json()
#             except Exception:
#                 msg = r.text
#             console.print(Panel.fit(f"[red]Registration failed: {msg}[/red]"))

#     def login_flow(self, prefill_sti: Optional[str] = None):
#         console.print(Panel.fit("[bold yellow]Login[/bold yellow]"))
#         sti = prefill_sti or Prompt.ask("Enter STI")
#         password = Prompt.ask("Password", password=True)
#         try:
#             r = self.api.login(sti, password)
#         except Exception as e:
#             console.print(Panel.fit(f"[red]Network/login error: {e}[/red]"))
#             return
#         if r.status_code == 200:
#             body = r.json()
#             token = body.get("token")
#             sti_resp = body.get("sti") or sti
#             # Save session locally
#             self.api.token = token
#             self.api.sti = sti_resp
#             self.session.update({"sti": sti_resp, "token": token})
#             sti=save_session(self.session)
#             print("session stored: ", sti_resp)
#             console.print(Panel.fit("[green]Login successful![/green]"))
#             # create login history handled on server (server will enrich location)
#             self.after_login(sti_resp)
#         else:
#             try:
#                 body = r.json()
#                 console.print(Panel.fit(f"[red]Login failed: {body.get('detail') or body}[/red]"))
#             except Exception:
#                 console.print(Panel.fit(f"[red]Login failed: status {r.status_code}[/red]"))

#     def password_recovery_flow(self, stl):
#         console.print(Panel.fit("[bold yellow]🔑 Password / STI Recovery[/bold yellow]"))
#         sti = Prompt.ask("Enter your STI (or leave blank if unknown)", default="")
#         if not sti:
#             console.print("[yellow]You must provide STI to request recovery. If you don't have STI, contact support.[/yellow]")
#             return
#         r = self.api.request_recovery(sti)
#         if r.status_code in (200, 201):
#             console.print(Panel.fit("[green]If your account exists, a recovery token was sent to your CodeHouse Cloud email.[/green]"))
#             # proceed to verify
#             token = Prompt.ask("Enter recovery token (from your CodeHouse Cloud email)")
#             vr = self.api.verify_recovery(token, stl)
#             if vr.status_code in (200, 201):
#                 console.print(Panel.fit("[green]Recovery token verified. Continue to reset password.[/green]"))
#                 new_pw = Prompt.ask("New password", password=True)
#                 confirm_pw = Prompt.ask("Confirm password", password=True)
#                 if new_pw != confirm_pw:
#                     console.print(Panel.fit("[red]Passwords do not match[/red]"))
#                     return
#                 rr = self.api.reset_password(self.session.get("temp_token") or token, new_pw, stl)
#                 if rr.status_code in (200, 201):
#                     console.print(Panel.fit("[green]Password reset successful. Please login.[/green]"))
#                 else:
#                     try:
#                         msg = rr.json()
#                     except Exception:
#                         msg = rr.text
#                     console.print(Panel.fit(f"[red]Reset failed: {msg}[/red]"))
#             else:
#                 try:
#                     msg = vr.json()
#                 except Exception:
#                     msg = vr.text
#                 console.print(Panel.fit(f"[red]Verification failed: {msg}[/red]"))
#         else:
#             console.print(Panel.fit(f"[red]Recovery request failed: {r.status_code}[/red]"))

#     def after_login(self, sti):
#         # fetch courses
#         r = self.api.get_courses(sti)
#         if r.status_code != 200:
#             console.print(Panel.fit(f"[red]Failed to fetch courses: {r.status_code}[/red]"))
#             return
#         courses = r.json()
#         table = Table(title="Available Courses", header_style="bold cyan")
#         table.add_column("#", justify="center")
#         table.add_column("Course ID")
#         table.add_column("Title")
#         for idx, c in enumerate(courses, 1):
#             table.add_row(str(idx), str(c.get("id")), c.get("title"))
#         console.print(table)
#         choice = Prompt.ask("Choose course number (or 'q' to quit)", default="1")
#         if choice.lower() == "q":
#             return
#         try:
#             selected = courses[int(choice)-1]
#         except Exception:
#             console.print(Panel.fit("[red]Invalid selection[/red]"))
#             return
#         self.session['selected_course'] = {'id': selected.get("id"), 'title': selected.get("title")}
#         save_session(self.session)
#         self.quiz_loop(stl)

#     def quiz_loop(self, stl):

#         course = self.session.get("selected_course")
#         if not course:
#             console.print(Panel.fit("[red]No course selected[/red]"))
#             return
#         course_id = course['id']

#         # show leaderboard snapshot
#         try:
#             # 🏆 Show Top Scorers
#             top_r = self.api.get_top(course_id, stl)
#             print('top', top_r)
#             if top_r.status_code == 200:
#                 top = top_r.json().get("top_scorers", [])[:20]
#                 if top:
#                     t = Table(title="🏆 Top Scorers", header_style="bold magenta")
#                     t.add_column("#", justify="center")
#                     t.add_column("Username", justify="left")
#                     t.add_column("Score (%)", justify="center")

#                     for i, s in enumerate(top, 1):
#                         t.add_row(str(i), str(s.get("username", "---")), f"{s.get('percentage', 0)}%")

#                     console.print(t)
#                 else:
#                     console.print("[yellow]No leaderboard yet. Be the first to take the quiz![/yellow]")
#             else:
#                 console.print("[red]Unable to fetch top scorers.[/red]")

#             console.print("\n[cyan]Starting your quiz...[/cyan]\n")

#             # top_r = self.api.get_top(course_id)
#             # if top_r.status_code == 200:
#             #     top = top_r.json()[:20]
#             #     t = Table(title="Top 20 - Before Start", header_style="bold magenta")
#             #     t.add_column("Rank"); t.add_column("Name/STI"); t.add_column("Score")
#             #     for i, s in enumerate(top, 1):
#             #         t.add_row(str(i), str(s.get('name') or s.get('sti') or '---'), str(s.get('score', '---')))
#             #     console.print(t)
#         except Exception:
#             pass

#         # get status
#         status_r = self.api.get_status(course_id, stl)
#         if status_r.status_code != 200:
#             console.print(Panel.fit("[red]Could not retrieve quiz status[/red]"))
#             return
#         status = status_r.json()
#         current_q = status.get("current_q", 1)
#         total_q = status.get("total_q", 10)

#         last_rank = None
#         while True:
#             q_r = self.api.get_question(course_id, current_q, stl)
#             if q_r.status_code != 200:
#                 console.print(Panel.fit("[red]Error loading question[/red]"))
#                 return
#             q = q_r.json()
#             console.print(Panel.fit(Text(f"Question {current_q}/{total_q}\n\n{q.get('text')}", style="bold cyan"), title=course.get("title")))
#             options = q.get("options", [])
#             for i, opt in enumerate(options, 1):
#                 console.print(f"[cyan]{i}.[/cyan] {opt}")
#             console.print("[yellow]Type option number. 't' to view top scorers, 'e' to exit quiz.[/yellow]")
#             q_start = time.time()
#             ans = Prompt.ask("Your answer")
#             time_spent = int(time.time() - q_start)
#             if ans.lower() in ("e", "end"):
#                 if Confirm.ask("Quit and save progress?"):
#                     self.session['last_q'] = current_q
#                     save_session(self.session)
#                     console.print("[cyan]Progress saved. You can resume later.[/cyan]")
#                     return
#                 else:
#                     continue
#             if ans.lower() in ("t", "top"):
#                 top_r = self.api.get_top(course_id, stl)
#                 if top_r.status_code == 200:
#                     top = top_r.json()[:20]
#                     t = Table(title="Top 20 Right Now", header_style="bold magenta")
#                     t.add_column("Rank"); t.add_column("Name/STI"); t.add_column("Score")
#                     for i, s in enumerate(top, 1):
#                         t.add_row(str(i), str(s.get('name') or s.get('sti') or '---'), str(s.get('score', '---')))
#                     console.print(t)
#                 else:
#                     console.print("[yellow]Top scorers unavailable.[/yellow]")
#                 continue
#             if not ans.isdigit() or int(ans) < 1 or int(ans) > len(options):
#                 console.print("[red]Invalid option number[/red]")
#                 continue
#             selected_text = options[int(ans)-1]
#             sub_r = self.api.submit_answer(course_id, current_q, selected_text, time_spent, source="cli", stl)
#             if sub_r.status_code not in (200, 201):
#                 console.print(Panel.fit(f"[red]Submit failed: {sub_r.status_code} {sub_r.text}[/red]"))
#                 continue
#             sub = sub_r.json()
#             print(sub)
#             correct = sub.get("correct", False)
#             console.print(Panel.fit(f"{'✅ Correct!' if correct else '❌ Incorrect!'}  Time: {pretty_time(time_spent)}", style="green" if correct else "red"))
#             # update leaderboards if any
#             try:
#                 top_r2 = self.api.get_top(course_id, stl)
#                 if top_r2.status_code == 200:
#                     top2 = top_r2.json()[:20]
#                     my_sti = self.api.sti or self.session.get("sti")
#                     my_rank = None
#                     for i, s in enumerate(top2, 1):
#                         if str(s.get("sti") or s.get("name") or "").lower() == str(my_sti or "").lower():
#                             my_rank = i; break
#                     if my_rank and my_rank != last_rank:
#                         last_rank = my_rank
#                         console.print(Panel.fit(f"[bold green]Leaderboard update: You are now ranked #{my_rank}[/bold green]"))
#             except Exception:
#                 pass
#             current_q = sub.get("next_q") or current_q + 1
#             if sub.get("finished"):
#                 console.print(Panel.fit("[bold cyan]🏁 Quiz Finished! Fetching summary...[/bold cyan]"))
#                 fin = self.api.finish(course_id, stl)
#                 result = fin.json() if getattr(fin, "json", None) else {}
#                 table = Table(title="Quiz Result Summary", header_style="bold green")
#                 table.add_column("Attempt"); table.add_column("Score (%)"); table.add_column("Time Spent")
#                 table.add_row(str(result.get("attempt_number", 1)), str(result.get("score", 0)), pretty_time(result.get("time_spent", 0)))
#                 console.print(table)
#                 # show final top
#                 try:
#                     final_top = self.api.get_top(course_id, stl)
#                     if final_top.status_code == 200:
#                         ft = final_top.json()[:20]
#                         t2 = Table(title="Top 20 (Final)", header_style="bold magenta")
#                         t2.add_column("Rank"); t2.add_column("Name/STI"); t2.add_column("Score")
#                         for i, s in enumerate(ft,1):
#                             t2.add_row(str(i), str(s.get("name") or s.get("sti") or '---'), str(s.get("score", '---')))
#                         console.print(t2)
#                 except Exception:
#                     pass

#             import requests

#             fin = requests.post(
#                 f"https://cli.codehouse.cloud/quiz/quiz/{course_id}/finish/",
#                 headers=self._headers(stl),  # include auth headers
#                 timeout=10
#             )
#             result = fin.json() if fin.status_code == 200 else {}


#             if fin.status_code == 200 and result:
#                 console.print(Panel.fit(f"[green]{result.get('message', 'Quiz completed successfully!')}[/green]"))

#                 # Show result summary
#                 table = Table(title="Result Summary", header_style="bold green")
#                 table.add_column("Score (%)", justify="center")
#                 table.add_column("QuizCoin Earned", justify="center")
#                 table.add_column("Wallet Balance", justify="center")
#                 table.add_row(
#                     str(result.get("percentage", 0)),
#                     str(result.get("quizcoin_earned", 0)),
#                     str(result.get("wallet_balance", 0))
#                 )
#                 console.print(table)

#                 # 📧 Fetch and email result PDF
#                 if "result_id" in result:
#                     pdf_r = self.api.get_result_pdf(result["result_id"])
#                     if pdf_r.status_code == 200:
#                         msg = pdf_r.json().get("message")
#                         if msg:
#                             console.print(Panel.fit(f"[cyan]{msg}[/cyan]"))
#                     else:
#                         console.print("[yellow]Result PDF not available yet.[/yellow]")

#             else:
#                 console.print("[red]Failed to finalize quiz or no result available.[/red]")


#                 console.print(Panel.fit("[cyan]Thank you for taking the quiz! Results also emailed to you.[/cyan]"))
#                 return
#             # save progress
#             self.session['last_q'] = current_q
#             save_session(self.session)

#     # Wallet & Withdraw flows
#     def wallet_flow(self):
#         console.print(Panel.fit("[bold cyan]💰 Wallet Overview[/bold cyan]"))
#         r = self.api.get_wallet()
#         if r.status_code != 200:
#             console.print(Panel.fit("[red]Failed to fetch wallet[/red]"))
#             return
#         try:
#             w = r.json()
#         except Exception:
#             console.print(Panel.fit("[red]Invalid wallet response[/red]"))
#             return
#         # expected fields: points_balance, quizcoin_balance, usd_balance, ngn_balance
#         table = Table(title="Wallet Balances", header_style="bold magenta")
#         table.add_column("Currency"); table.add_column("Amount", justify="right")
#         table.add_row("Points", str(w.get("points_balance", 0)))
#         table.add_row("QuizCoin", str(w.get("quizcoin_balance", 0)))
#         table.add_row("USD", f"${w.get('usd_balance', 0):,.2f}")
#         table.add_row("NGN", f"₦{w.get('ngn_balance', 0):,.2f}")
#         console.print(table)

#     def withdraw_flow(self):
#         console.print(Panel.fit("[bold magenta]💸 Request Withdrawal[/bold magenta]"))
#         try:
#             amount = float(Prompt.ask("Amount in QuizCoin"))
#         except Exception:
#             console.print("[red]Invalid amount[/red]"); return
#         if amount <= 0:
#             console.print("[red]Amount must be positive[/red]"); return
#         bank_name = Prompt.ask("Bank name")
#         account_number = Prompt.ask("Account number")
#         ngn_rate = None
#         if Confirm.ask("Do you want to provide current NGN rate? (optional)", default=False):
#             try:
#                 ngn_rate = float(Prompt.ask("NGN rate (USD->NGN)"))
#             except Exception:
#                 ngn_rate = None
#         if not Confirm.ask(f"Confirm withdraw {amount} QuizCoin to {account_number} at {bank_name}?"):
#             console.print("[yellow]Cancelled[/yellow]"); return
#         r = self.api.request_withdraw(amount, ngn_rate, bank_name, account_number)
#         if r.status_code in (200,201):
#             console.print(Panel.fit("[green]Withdrawal request submitted. Admin will process payout.[/green]"))
#             try:
#                 data = r.json()
#                 console.print(f"Withdrawal ID: {data.get('withdrawal_id')} Status: {data.get('status')}")
#             except Exception:
#                 pass
#         else:
#             try:
#                 err = r.json()
#             except Exception:
#                 err = r.text
#             console.print(Panel.fit(f"[red]Withdrawal failed: {err}[/red]"))

#     # main run
#     def run(self):
#         self.display_banner()
#         while True:
#             try:
#                 menu = Table.grid(expand=True)
#                 menu.add_column(justify="center")
#                 menu.add_row("[bold cyan]1[/bold cyan] Login    [bold cyan]2[/bold cyan] Register    [bold cyan]3[/bold cyan] Recover    [bold cyan]4[/bold cyan] Wallet    [bold cyan]5[/bold cyan] Withdraw    [bold cyan]6[/bold cyan] Exit")
#                 console.print(Panel.fit(menu, title="[bold cyan]CODEHOUSE CLOUD[/bold cyan]"))
#                 choice = Prompt.ask("Select", choices=["1","2","3","4","5","6"], default="1")
#                 if choice == "1":
#                     self.login_flow()
#                 elif choice == "2":
#                     self.register_flow()
#                 elif choice == "3":
#                     self.password_recovery_flow()
#                 elif choice == "4":
#                     self.wallet_flow()
#                 elif choice == "5":
#                     self.withdraw_flow()
#                 else:
#                     console.print(Panel.fit("[bold]Goodbye![/bold]\n[dim cyan]Powered by Applinet Technology[/dim cyan]"))
#                     sys.exit(0)
#             except KeyboardInterrupt:
#                 console.print()
#                 if Confirm.ask("Exit application?"):
#                     console.print(Panel.fit("Goodbye!", style="blue"))
#                     sys.exit(0)
#                 else:
#                     continue



