# Familiar - Self-Hosted AI Companion Platform
# Copyright (c) 2026 George Scott Foley
# ORCID: 0009-0006-4957-0540
# Email: Georgescottfoley@proton.me
# Licensed under the MIT License - see LICENSE file for details

"""
First-Run Creature Intro

The complete first-run onboarding experience:
logo → constitution → consent → user name → egg → creature name →
species → colors → birth → channel choice.

Fires when load_creature() returns None (first launch after onboarding).
"""

import os
import time
from dataclasses import dataclass
from typing import Optional

from .display import BOLD, DIM, DIM_CYAN, RESET, CreatureDisplay
from .palette import PALETTE, PALETTE_ORDER, ansi_fg, ansi_reset, resolve_color
from .renderer import render_frame
from .species import SPECIES_INFO, SPECIES_ORDER
from .sprites import EGG_FRAMES
from .state import ActivityState, CreatureState, create_creature

# ANSI extras not in display.py
_VIOLET = "\033[95m"
_CYAN = "\033[96m"


@dataclass
class CreatureIntroResult:
    """Result of the creature intro sequence."""

    creature: CreatureState
    channel_choice: str  # "cli", "telegram", "dashboard"


def run_creature_intro() -> Optional[CreatureIntroResult]:
    """Run the full first-run intro. Returns None if consent refused."""

    # ── 1. Logo ──────────────────────────────────────────────
    print()
    _show_logo()
    print()

    # ── 2-3. Constitution + Consent ──────────────────────────
    _present_constitution()
    if not _ask_consent():
        return None

    # ── 4. Ask owner name ────────────────────────────────────
    owner_name = _ask_owner_name()

    # ── 5-6. Egg + greeting ──────────────────────────────────
    print()
    _show_egg()
    time.sleep(0.5)

    print(f"\n  {DIM}Hello... I'm your Familiar.{RESET}")
    print(f"  {DIM}I don't have a form yet.{RESET}")
    print()

    # ── 7. Creature name ─────────────────────────────────────
    name = ""
    while not name:
        try:
            name = input(f"  {BOLD}What would you like to call me?{RESET} ").strip()
        except (EOFError, KeyboardInterrupt):
            name = "Familiar"
    print()

    # ── 8. Species ───────────────────────────────────────────
    print(f"  {BOLD}What kind of familiar am I?{RESET}\n")
    for i, key in enumerate(SPECIES_ORDER, 1):
        info = SPECIES_INFO[key]
        preview_lines = info.baby_preview
        print(f"    {DIM_CYAN}{i}{RESET}) {BOLD}{info.label}{RESET} — {info.description}")
        for line in preview_lines:
            print(f"       {DIM}{line}{RESET}")
        if i < len(SPECIES_ORDER):
            print()

    print()
    species = _prompt_choice(
        f"  Choose [1-{len(SPECIES_ORDER)}]: ",
        SPECIES_ORDER,
    )
    print()

    # ── 9. Colors ────────────────────────────────────────────
    color_body = _prompt_color("Body color")
    color_eyes = _prompt_color("Eye color")
    color_accent = _prompt_color("Accent color")

    # ── 10. Birth animation ──────────────────────────────────
    print()
    print(f"  {DIM}The egg trembles...{RESET}")
    time.sleep(0.6)

    for i in range(3):
        sparkles = "  " + " ".join(["*" if (i + j) % 2 == 0 else "." for j in range(10)])
        print(f"\r{sparkles}", end="", flush=True)
        time.sleep(0.3)
    print()
    print()

    # ── 11. Create and show ──────────────────────────────────
    creature = create_creature(
        name=name,
        species=species,
        color_body=color_body,
        color_eyes=color_eyes,
        color_accent=color_accent,
        owner_name=owner_name,
        constitution_accepted=True,
    )

    display = CreatureDisplay(creature)
    display.show(ActivityState.IDLE)
    print()
    print(f"  {BOLD}I'm {name} the {SPECIES_INFO[species].label}!{RESET}")
    print()

    # ── 12-13. Channel choice ────────────────────────────────
    channel_choice = _ask_channel_choice()
    creature.preferred_channel = channel_choice
    from .state import save_creature

    save_creature(creature)

    _show_channel_info(channel_choice, creature)

    return CreatureIntroResult(creature=creature, channel_choice=channel_choice)


# ── Constitution presentation ────────────────────────────────────


def _present_constitution() -> None:
    """Scroll the Constitution articles as a thematic bond."""
    from ..core.constitution import CONSTITUTION_ARTICLES_HUMAN

    print(f"  {DIM}{_VIOLET}Before I take form, there is a bond we must share...{RESET}")
    print()
    time.sleep(0.4)

    # Show articles in groups of 2-3
    groups = [
        CONSTITUTION_ARTICLES_HUMAN[0:3],
        CONSTITUTION_ARTICLES_HUMAN[3:5],
        CONSTITUTION_ARTICLES_HUMAN[5:8],
    ]

    for group in groups:
        for article in group:
            print(f"  {DIM_CYAN}{article['title']}{RESET}")
            print(f"    {DIM}{article['summary']}{RESET}")
            print()
        try:
            input(f"  {DIM}[Enter to continue]{RESET}")
        except (EOFError, KeyboardInterrupt):
            pass
        print()


def _ask_consent() -> bool:
    """Ask if the user accepts the bond. Returns True if accepted."""
    print(f"  {_VIOLET}This is the bond between us.{RESET}")
    print()

    while True:
        try:
            answer = input(f"  {BOLD}Do you accept? [yes/no]{RESET} ").strip().lower()
        except (EOFError, KeyboardInterrupt):
            answer = "no"

        if answer in ("yes", "y"):
            print()
            print(f"  {DIM}{_CYAN}The bond is sealed.{RESET}")
            print()
            return True
        elif answer in ("no", "n"):
            print()
            print(f"  {DIM}I understand. Perhaps another time.{RESET}")
            print()
            return False


def _ask_owner_name() -> str:
    """Ask the user's name."""
    print(f"  {DIM}{_VIOLET}And you... who are you?{RESET}")
    print()

    while True:
        try:
            name = input(f"  {BOLD}What is your name?{RESET} ").strip()
        except (EOFError, KeyboardInterrupt):
            return ""
        if name:
            print()
            print(f"  {DIM}Welcome, {name}.{RESET}")
            print()
            return name


# ── Channel choice ───────────────────────────────────────────────


def _ask_channel_choice() -> str:
    """Ask how the user wants to interact."""
    print(f"  {BOLD}How would you like to talk to me?{RESET}")
    print()
    print(f"    {DIM_CYAN}1{RESET}) {BOLD}Console{RESET} — right here in the terminal")
    print(f"    {DIM_CYAN}2{RESET}) {BOLD}Telegram{RESET} — chat with me on Telegram")
    print(f"    {DIM_CYAN}3{RESET}) {BOLD}Dashboard{RESET} — web interface at localhost")
    print()

    options = ["cli", "telegram", "dashboard"]
    return _prompt_choice("  Choose [1-3]: ", options)


def _show_channel_info(channel: str, creature: CreatureState) -> None:
    """Show connection info based on channel choice."""
    print()
    if channel == "cli":
        print(f"  {DIM}Let's begin. Type anything to talk to me.{RESET}")
    elif channel == "dashboard":
        print(f"  {BOLD}{_CYAN}Web Dashboard:{RESET}  {BOLD}http://127.0.0.1:5000{RESET}")
        api_key = os.environ.get("FAMILIAR_DASHBOARD_KEY")
        if not api_key:
            env_path = os.path.expanduser("~/.familiar/.env")
            if os.path.exists(env_path):
                try:
                    with open(env_path) as f:
                        for line in f:
                            line = line.strip()
                            if line.startswith("FAMILIAR_DASHBOARD_KEY="):
                                api_key = line.split("=", 1)[1].strip()
                                break
                except OSError:
                    pass
        if api_key:
            print(f"  {DIM}API Key:{RESET}        {BOLD}{api_key}{RESET}")
        print()
        print(f"  {DIM}Open the dashboard in your browser to control {creature.name}.{RESET}")
    elif channel == "telegram":
        token = os.environ.get("TELEGRAM_BOT_TOKEN")
        if token:
            print(f"  {DIM}Find me on Telegram — send /start!{RESET}")
        else:
            print(f"  {DIM}You'll need to set up a bot token first.{RESET}")
            print(f"  {DIM}Run: python -m familiar --onboard{RESET}")
    print()


# ── Shared helpers ───────────────────────────────────────────────


def _show_logo() -> None:
    """Display the Familiar ASCII art logo in retro colors."""
    C1 = "\033[96m"  # Cyan
    C2 = "\033[94m"  # Blue
    C3 = "\033[95m"  # Magenta/Violet
    B = "\033[1m"  # Bold
    D = "\033[2m"  # Dim
    R = RESET

    logo_lines = [
        f"  {B}{C1}███████{R}{B}{C1}  ██████  {R}{B}{C1}███    ███{R}{B}{C2} ██{R}{B}{C2} ██      {R}{B}{C2} ██{R}{B}{C3}  ██████  {R}{B}{C3} ██████{R}",
        f"  {B}{C1}██       {R}{B}{C1}██    ██ {R}{B}{C1}████  ████{R}{B}{C2} ██{R}{B}{C2} ██      {R}{B}{C2} ██{R}{B}{C3} ██    ██ {R}{B}{C3}██    ██{R}",
        f"  {B}{C1}█████{R}{B}{C1}   {R}{B}{C1}████████ {R}{B}{C1}██ ████ ██{R}{B}{C2} ██{R}{B}{C2} ██      {R}{B}{C2} ██{R}{B}{C3} ████████ {R}{B}{C3}██████{R}",
        f"  {B}{C1}██{R}{B}{C1}      {R}{B}{C1}██    ██ {R}{B}{C1}██  ██  ██{R}{B}{C2} ██{R}{B}{C2} ██      {R}{B}{C2} ██{R}{B}{C3} ██    ██ {R}{B}{C3}██   ██{R}",
        f"  {B}{C1}██{R}{B}{C1}      {R}{B}{C1}██    ██ {R}{B}{C1}██      ██{R}{B}{C2} ██{R}{B}{C2} ███████ {R}{B}{C2} ██{R}{B}{C3} ██    ██ {R}{B}{C3}██    ██{R}",
    ]

    for line in logo_lines:
        print(line)

    tagline = "Self-Hosted AI Companion"
    print(f"\n  {D}{C1}{'─' * 58}{R}")
    print(f"  {D}{C2}{tagline:^58}{R}")
    print(f"  {D}{C1}{'─' * 58}{R}")


def _show_egg() -> None:
    """Display the generic egg sprite."""
    colors = {
        "B": (180, 180, 180),
        "H": (220, 220, 220),
        "#": (80, 80, 80),
    }
    rendered = render_frame(EGG_FRAMES[0], colors)
    for line in rendered:
        print(f"       {line}")


def _prompt_choice(prompt: str, options: list[str]) -> str:
    """Prompt for a numbered choice from a list."""
    while True:
        try:
            raw = input(prompt).strip()
        except (EOFError, KeyboardInterrupt):
            return options[0]
        try:
            idx = int(raw) - 1
            if 0 <= idx < len(options):
                return options[idx]
        except ValueError:
            pass
        print(f"  {DIM}Enter a number 1-{len(options)}{RESET}")


def _prompt_color(label: str) -> str:
    """Prompt for a color selection from the palette or a hex code."""
    print(f"  {BOLD}{label}:{RESET}")

    cols = 4
    for row_start in range(0, len(PALETTE_ORDER), cols):
        row_items = PALETTE_ORDER[row_start : row_start + cols]
        parts = []
        for i, name in enumerate(row_items):
            num = row_start + i + 1
            r, g, b = PALETTE[name]
            swatch = f"{ansi_fg(r, g, b)}\u2588\u2588{ansi_reset()}"
            parts.append(f"  {num:>2}) {swatch} {name:<10}")
        print("".join(parts))

    print(f"\n  {DIM}Or enter a hex code like #FF6B2E{RESET}")

    while True:
        try:
            raw = input(f"  Choose [1-{len(PALETTE_ORDER)} or hex]: ").strip()
        except (EOFError, KeyboardInterrupt):
            return "#4A90D9"

        try:
            idx = int(raw) - 1
            if 0 <= idx < len(PALETTE_ORDER):
                chosen_name = PALETTE_ORDER[idx]
                r, g, b = PALETTE[chosen_name]
                print(f"  {ansi_fg(r, g, b)}\u2588\u2588\u2588{ansi_reset()} {chosen_name}\n")
                return f"#{r:02X}{g:02X}{b:02X}"
        except ValueError:
            pass

        if raw.startswith("#"):
            rgb = resolve_color(raw)
            if rgb:
                r, g, b = rgb
                print(f"  {ansi_fg(r, g, b)}\u2588\u2588\u2588{ansi_reset()} {raw}\n")
                return raw

        print(f"  {DIM}Enter a number 1-{len(PALETTE_ORDER)} or a hex code like #FF6B2E{RESET}")
