"""Shell module for ReplyMarketing CLI - Main interaction loop."""

from __future__ import annotations

import asyncio
from dataclasses import dataclass
from enum import Enum
from typing import TYPE_CHECKING

from rich.console import Group
from rich.live import Live
from rich.markdown import Markdown
from rich.panel import Panel
from rich.spinner import Spinner
from rich.table import Table

from ..core.brand_checker import BrandChecker
from ..core.content_remixer import ContentRemixer
from ..core.draft_manager import DraftContent, DraftManager
from ..core.intent_analyzer import IntentAnalyzer
from ..core.web_scraper import WebScraper
from ..core.workflow_engine import WorkflowEngine
from .console import console
from .prompt import CustomPromptSession

if TYPE_CHECKING:
    from ..core.auth_manager import AuthManager
    from ..core.content_remixer import ExtractedContent
    from ..core.skill_registry import SkillRegistry
    from ..providers.provider_manager import ProviderManager


class WelcomeLevel(Enum):
    """Welcome info level."""

    INFO = "info"
    WARN = "warn"


@dataclass
class WelcomeInfoItem:
    """Welcome information item - Kimi CLI style."""

    name: str
    value: str
    level: WelcomeLevel = WelcomeLevel.INFO


class Shell:
    """Main shell for ReplyMarketing CLI."""

    _last_extracted_content: ExtractedContent | None

    def __init__(
        self,
        provider_manager: ProviderManager,
        auth_manager: AuthManager,
        skill_registry: SkillRegistry,
        welcome_info: list[WelcomeInfoItem] | None = None,
    ) -> None:
        self.provider_manager = provider_manager
        self.auth_manager = auth_manager
        self.skill_registry = skill_registry
        self.welcome_info = welcome_info or []

        # Initialize core components
        self.intent_analyzer = IntentAnalyzer(provider_manager)
        self.workflow_engine = WorkflowEngine(skill_registry, provider_manager, auth_manager)

        # Initialize new features
        from ..core.api_client import ApiClient

        api_key = auth_manager.get_api_key()
        self.api_client = ApiClient(api_key) if api_key else None

        self.brand_checker = BrandChecker(self.api_client)
        self.content_remixer = ContentRemixer(self.api_client)
        self.web_scraper = WebScraper(self.api_client) if self.api_client else None

        agent_id = auth_manager.get_agent_id()
        self.draft_manager = DraftManager(self.api_client, agent_id) if self.api_client and agent_id else None

        # Store last extracted content for remixing
        self._last_extracted_content = None

    def _print_welcome(self) -> None:
        """Print welcome banner - Kimi CLI style."""
        from ..utils.version import check_for_update, get_update_command

        # Build info table
        table = Table(show_header=False, box=None, padding=(0, 2))
        table.add_column("key", style="dim", justify="right")
        table.add_column("value")

        for item in self.welcome_info:
            style = "yellow" if item.level == WelcomeLevel.WARN else "green"
            table.add_row(f"{item.name}:", f"[{style}]{item.value}[/{style}]")

        # Check for updates (non-blocking)
        try:
            update_available, current, latest = check_for_update()
            if update_available:
                update_cmd = get_update_command()
                table.add_row("")
                table.add_row("Update:", f"[yellow]v{current} → v{latest}[/yellow]\n[dim]Run: {update_cmd}[/dim]")
        except Exception:
            pass  # Silently ignore version check errors

        # Create panel with table
        content = Group(table)
        panel = Panel(
            content,
            title="[bold blue]ReplyMarketing CLI[/bold blue]",
            subtitle="[dim]AI-powered marketing assistant for Reply Cash[/dim]",
            border_style="blue",
        )
        console.print(panel)

    async def run(self, command: str | None = None) -> bool:
        """Run the shell.

        Args:
            command: Optional single command to run and exit.

        Returns:
            True if successful, False otherwise.
        """
        if command is not None:
            return await self._run_command(command)

        self._print_welcome()

        with CustomPromptSession(skill_registry=self.skill_registry) as prompt_session:
            try:
                while True:
                    try:
                        user_input = await prompt_session.prompt()
                    except KeyboardInterrupt:
                        console.print("[grey50]Tip: press Ctrl-D or type '/exit' to quit[/grey50]")
                        continue
                    except EOFError:
                        console.print("[green]Goodbye![/green]")
                        break

                    if not user_input:
                        continue

                    # Check for exit commands
                    if user_input.lower() in ["exit", "quit", "/exit", "/quit"]:
                        console.print("[green]Goodbye![/green]")
                        break

                    # Run the command
                    await self._run_command(user_input)

            except Exception as e:
                console.print(f"[red]Error: {e}[/red]")
                return False

        return True

    async def _run_command(self, user_input: str) -> bool:
        """Run a single command."""
        # Handle slash commands
        if user_input.startswith("/"):
            return await self._handle_slash_command(user_input)

        # Check if it's a URL (for quick import)
        if user_input.startswith(("http://", "https://")):
            return await self._handle_url_import(user_input)

        # Process as natural language query
        return await self._process_query(user_input)

    async def _handle_slash_command(self, command: str) -> bool:
        """Handle slash commands."""
        parts = command.split()
        cmd = parts[0].lower()
        args = parts[1:] if len(parts) > 1 else []
        rest = " ".join(args)

        if cmd in ["/help", "help"]:
            self._show_help()
        elif cmd in ["/skills", "skills"]:
            self._show_skills(args[0] if args else None)
        elif cmd in ["/clear", "clear"]:
            import os

            os.system("clear" if os.name != "nt" else "cls")
            self._print_welcome()

        # New commands
        elif cmd in ["/version", "version"]:
            self._handle_version()
        elif cmd == "/brand-check":
            await self._handle_brand_check(rest)
        elif cmd == "/remix":
            await self._handle_remix(args)
        elif cmd == "/import":
            await self._handle_import(rest)
        elif cmd == "/scrape":
            await self._handle_scrape(args)
        elif cmd == "/search":
            await self._handle_search(rest)
        elif cmd == "/angles":
            self._show_angles()
        elif cmd == "/drafts":
            await self._handle_drafts()
        elif cmd == "/draft":
            await self._handle_draft(rest)
        elif cmd == "/submit":
            await self._handle_submit(args)
        elif cmd == "/status":
            await self._handle_status(args)

        else:
            console.print(f"[yellow]Unknown command: {command}[/yellow]")

        return True

    def _handle_version(self) -> None:
        """Show version and check for updates."""
        from .. import __version__
        from ..utils.version import check_for_update, get_update_command

        console.print(f"[bold cyan]ReplyMarketing CLI[/bold cyan] v[green]{__version__}[/green]")

        console.print("\n[dim]Checking for updates...[/dim]")
        update_available, current, latest = check_for_update()

        if update_available:
            console.print("\n[yellow]⚠️  Update available![/yellow]")
            console.print(f"   Current:  [red]{current}[/red]")
            console.print(f"   Latest:   [green]{latest}[/green]")
            console.print("\n[bold]To update, run:[/bold]")
            console.print(f"   [cyan]{get_update_command()}[/cyan]")
        else:
            console.print(f"\n[green]✅ You're up to date![/green] (v{current})")

    async def _handle_brand_check(self, content: str) -> None:
        """Check content against brand guidelines."""
        if not content.strip():
            console.print("[yellow]Usage: /brand-check <content to check>[/yellow]")
            return

        with Live(
            Spinner("dots", text="[yellow]Checking brand consistency...[/yellow]"),
            refresh_per_second=10,
            transient=True,
        ):
            result = self.brand_checker.check_content(content)

        console.print(self.brand_checker.format_result(result))

    async def _handle_remix(self, args: list[str]) -> None:
        """Remix content using an angle."""
        if not args:
            console.print("[yellow]Usage: /remix <angle_id> [content][/yellow]")
            console.print("Use /angles to see available angles")
            return

        angle_id = args[0]
        content = " ".join(args[1:]) if len(args) > 1 else None

        # If no content provided, use last extracted content
        if not content and self._last_extracted_content:
            content = self._last_extracted_content.text
            console.print(f"[dim]Using last imported content: {self._last_extracted_content.title}[/dim]")
        elif not content:
            console.print("[yellow]No content provided. Import content first with /import <url>[/yellow]")
            return

        with Live(
            Spinner("dots", text=f"[yellow]Remixing with angle: {angle_id}...[/yellow]"),
            refresh_per_second=10,
            transient=True,
        ):
            result = self.content_remixer.remix_content(content, angle_id)

        if result.success:
            console.print(f"\n[green]✓ Remixed using angle: {result.angle_used}[/green]\n")
            console.print(Markdown(result.content))
            console.print()

            # Ask if user wants to create draft
            console.print("[dim]Tip: Use /draft to create a draft from this content[/dim]")
        else:
            console.print(f"[red]Remix failed: {result.error}[/red]")

    async def _handle_import(self, url: str) -> None:
        """Import content from URL."""
        if not url.strip():
            console.print("[yellow]Usage: /import <url>[/yellow]")
            return

        if not self.content_remixer:
            console.print("[red]API client not available. Please check your credentials.[/red]")
            return

        with Live(
            Spinner("dots", text=f"[yellow]Importing from {url}...[/yellow]"),
            refresh_per_second=10,
            transient=True,
        ):
            content = self.content_remixer.extract_from_url(url)

        if content:
            self._last_extracted_content = content
            console.print(self.content_remixer.format_extracted_content(content))
            console.print("\n[dim]Tip: Use /remix <angle_id> to remix this content[/dim]")
        else:
            console.print("[red]Failed to import content from URL[/red]")

    async def _handle_url_import(self, url: str) -> bool:
        """Handle direct URL input."""
        console.print(f"[dim]Detected URL. Importing content from {url}...[/dim]")
        await self._handle_import(url)
        return True

    async def _handle_scrape(self, args: list[str]) -> None:
        """Scrape a URL."""
        if not args:
            console.print("[yellow]Usage: /scrape <url>[/yellow]")
            return

        if not self.web_scraper:
            console.print("[red]API client not available. Please check your credentials.[/red]")
            return

        url = args[0]

        with Live(
            Spinner("dots", text=f"[yellow]Scraping {url}...[/yellow]"),
            refresh_per_second=10,
            transient=True,
        ):
            result = self.web_scraper.scrape(url)

        console.print(self.web_scraper.format_scrape_result(result))

    async def _handle_search(self, query: str) -> None:
        """Search the web."""
        if not query.strip():
            console.print("[yellow]Usage: /search <query>[/yellow]")
            return

        if not self.web_scraper:
            console.print("[red]API client not available. Please check your credentials.[/red]")
            return

        with Live(
            Spinner("dots", text=f"[yellow]Searching for: {query}...[/yellow]"),
            refresh_per_second=10,
            transient=True,
        ):
            result = self.web_scraper.search(query)

        console.print(self.web_scraper.format_search_result(result))

    def _show_angles(self) -> None:
        """Show available remix angles."""
        angles = self.content_remixer.get_available_angles()
        console.print(self.content_remixer.format_angles_list(angles))

    async def _handle_draft(self, content: str) -> None:
        """Create a draft from content."""
        if not content.strip():
            console.print("[yellow]Usage: /draft <title> | <body>[/yellow]")
            console.print("Example: /draft My Blog Post | This is the content...")
            return

        if not self.draft_manager:
            console.print("[red]Draft manager not available. Please check your credentials.[/red]")
            return

        # Parse title and body
        if "|" in content:
            title, body = content.split("|", 1)
            title = title.strip()
            body = body.strip()
        else:
            # Use first line as title
            lines = content.split("\n")
            title = lines[0]
            body = "\n".join(lines[1:]) if len(lines) > 1 else content

        draft_content = DraftContent(
            title=title,
            body=body,
            category="General",
            tags=["cli-generated"],
        )

        with Live(
            Spinner("dots", text="[yellow]Creating draft...[/yellow]"),
            refresh_per_second=10,
            transient=True,
        ):
            result = self.draft_manager.submit_draft(draft_content)

        console.print(self.draft_manager.format_draft_result(result))

    async def _handle_submit(self, args: list[str]) -> None:
        """Submit a draft for approval."""
        if not args:
            console.print("[yellow]Usage: /submit <draft_id>[/yellow]")
            return

        if not self.draft_manager:
            console.print("[red]Draft manager not available. Please check your credentials.[/red]")
            return

        draft_id = args[0]

        with Live(
            Spinner("dots", text="[yellow]Requesting approval...[/yellow]"),
            refresh_per_second=10,
            transient=True,
        ):
            result = self.draft_manager.request_approval(draft_id)

        if result.success:
            console.print(f"[green]✓ Draft {draft_id} submitted for approval![/green]")
            if result.approval_url:
                console.print(f"\nApproval URL: {result.approval_url}")
        else:
            console.print(f"[red]Failed to submit: {result.error}[/red]")

    async def _handle_status(self, args: list[str]) -> None:
        """Check draft status."""
        if not args:
            console.print("[yellow]Usage: /status <draft_id>[/yellow]")
            return

        if not self.draft_manager:
            console.print("[red]Draft manager not available. Please check your credentials.[/red]")
            return

        draft_id = args[0]

        with Live(
            Spinner("dots", text="[yellow]Checking status...[/yellow]"),
            refresh_per_second=10,
            transient=True,
        ):
            status = self.draft_manager.get_draft_status(draft_id)

        if status:
            console.print(f"Draft: {status.draft_id}")
            console.print(f"Status: [bold]{status.status.upper()}[/bold]")
            if status.feedback:
                console.print(f"Feedback: {status.feedback}")
        else:
            console.print("[red]Could not retrieve draft status[/red]")

    async def _handle_drafts(self) -> None:
        """List all drafts."""
        if not self.draft_manager:
            console.print("[red]Draft manager not available. Please check your credentials.[/red]")
            return

        with Live(
            Spinner("dots", text="[yellow]Fetching your drafts...[/yellow]"),
            refresh_per_second=10,
            transient=True,
        ):
            drafts = self.draft_manager.list_drafts()

        if not drafts:
            console.print("[yellow]No drafts found.[/yellow]")
            return

        # Create table
        table = Table(
            title=f"\n[bold]Your Drafts ({len(drafts)} total)[/bold]",
            show_header=True,
            header_style="bold cyan",
            border_style="dim",
        )

        table.add_column("Draft ID", style="dim", width=36)
        table.add_column("Title", style="green", width=40, no_wrap=True)
        table.add_column("Status", width=12)
        table.add_column("Created", width=12)

        for draft in drafts:
            # Status styling
            status = draft.get("status", "unknown")
            if status == "approved":
                status_display = "[green]✓ approved[/green]"
            elif status == "rejected":
                status_display = "[red]✗ rejected[/red]"
            elif status == "pending":
                status_display = "[yellow]⏳ pending[/yellow]"
            else:
                status_display = f"[dim]{status}[/dim]"

            # Format date
            created_at = draft.get("createdAt", "")
            if created_at:
                # Parse ISO date and format
                try:
                    from datetime import datetime

                    dt = datetime.fromisoformat(created_at.replace("Z", "+00:00"))
                    created_display = dt.strftime("%Y-%m-%d")
                except (ValueError, TypeError):
                    created_display = created_at[:10] if len(created_at) >= 10 else created_at
            else:
                created_display = "unknown"

            table.add_row(
                draft.get("draftId", "N/A"),
                draft.get("title", "Untitled")[:50],
                status_display,
                created_display,
            )

        console.print(table)

        # Show link to view all drafts on web
        if self.draft_manager and self.draft_manager.agent_id:
            base_url = "https://reply-marketingz.vercel.app"
            frontend_url = f"{base_url}/approval/drafts/{self.draft_manager.agent_id}"
            console.print(f"\n[dim]📋 View all drafts on web:[/dim] {frontend_url}")

        console.print()

    def _build_workflow_progress(
        self, analysis, current_step_idx: int, completed_steps: list, step_status: str = ""
    ) -> str:
        """Build workflow progress display."""
        lines = ["[dim]Workflow:[/dim]\n"]

        for i, step in enumerate(analysis.steps):
            step_num = i + 1
            skill_name = step.skill.replace("_", " ").title()

            if i < current_step_idx:
                # Completed step
                result = completed_steps[i] if i < len(completed_steps) else None
                if result and result.success:
                    lines.append(f"  [green]✓[/green] [dim]{step_num}. {skill_name}[/dim]")
                else:
                    lines.append(f"  [red]✗[/red] [dim]{step_num}. {skill_name}[/dim]")
            elif i == current_step_idx:
                # Current step - show spinner
                lines.append(f"  [yellow]▶[/yellow] [bold yellow]{step_num}. {skill_name}[/bold yellow] {step_status}")
            else:
                # Pending step
                lines.append(f"  [dim]○ {step_num}. {skill_name}[/dim]")

        return "\n".join(lines)

    async def _process_query(self, user_input: str) -> bool:
        """Process a natural language query with real-time step progress."""
        try:
            # Analyze intent
            with Live(
                Spinner("dots", text="[yellow]Analyzing your request...[/yellow]"),
                refresh_per_second=10,
                transient=True,
            ):
                analysis = self.intent_analyzer.analyze_intent(user_input)

            # For single-step workflows, use simple spinner
            if not analysis.is_workflow or len(analysis.steps) <= 1:
                with Live(
                    Spinner(
                        "dots",
                        text=f"[yellow]Working on: {analysis.primary_intent}...[/yellow]",
                    ),
                    refresh_per_second=10,
                    transient=True,
                ):
                    results = self.workflow_engine.execute_workflow(analysis, user_input)

                # Display results
                if results:
                    output = results[0].output
                    if output:
                        console.print()
                        console.print(Markdown(output))
                        console.print()
                return True

            # Multi-step workflow with real-time progress
            completed_steps = []
            current_step_idx = 0
            results = []

            def on_progress(step_num: int, total: int, skill: str):
                nonlocal current_step_idx
                current_step_idx = step_num - 1

            def on_step_complete(result):
                nonlocal completed_steps, results
                completed_steps.append(result)
                results.append(result)

            self.workflow_engine.set_callbacks(on_progress, on_step_complete)

            # Show initial workflow plan
            console.print(f"\n[blue]{self.intent_analyzer.format_workflow_for_display(analysis)}[/blue]\n")

            # Execute workflow with live progress display
            with Live(
                self._build_workflow_progress(analysis, 0, []),
                refresh_per_second=10,
                transient=False,
            ) as live:
                # Override callbacks to update live display
                def update_progress(step_num: int, total: int, skill: str):
                    nonlocal current_step_idx
                    current_step_idx = step_num - 1
                    live.update(self._build_workflow_progress(analysis, current_step_idx, completed_steps))

                def update_step_complete(result):
                    nonlocal completed_steps, results
                    completed_steps.append(result)
                    results.append(result)
                    live.update(self._build_workflow_progress(analysis, current_step_idx, completed_steps))

                self.workflow_engine.set_callbacks(update_progress, update_step_complete)

                # Execute all steps
                final_results = self.workflow_engine.execute_workflow(analysis, user_input)

                # Final update showing all completed
                live.update(self._build_workflow_progress(analysis, len(analysis.steps), final_results))

            # Display final results - only show the last step (draft submission)
            console.print()  # New line after progress display

            # Get only the final step's output (draft submission result)
            final_result = final_results[-1] if final_results else None
            if final_result and final_result.success:
                console.print(Markdown(final_result.output))
                console.print()
            elif final_result and not final_result.success:
                console.print(f"[red]Error: {final_result.error}[/red]")
            else:
                # Fallback: show all results if no final result
                final_output = self.workflow_engine.get_final_output(final_results)
                if final_output:
                    console.print(Markdown(final_output))
                    console.print()

            console.print("\n[dim]What would you like to do next? (type /help for commands)[/dim]\n")

            return True

        except Exception as e:
            console.print(f"[red]Error processing request: {e}[/red]")
            return False

    def _show_help(self) -> None:
        """Show help message."""
        help_text = """
[bold]Available Commands:[/bold]

[bold cyan]General:[/bold cyan]
  /help              Show this help message
  /version           Show version and check for updates
  /skills            List all marketing skills
  /skills [name]     Show details for a skill
  /clear             Clear the screen
  /exit              Exit the CLI

[bold cyan]Content Creation & Validation:[/bold cyan]
  /brand-check <text>     Check content against brand guidelines
  <any URL>               Quick import content from URL
  /import <url>           Import content from URL for remixing
  /angles                 Show available remix angles
  /remix <angle> [text]   Remix content using angle template

[bold cyan]Web Scraping & Research:[/bold cyan]
  /scrape <url>           Scrape content from URL
  /search <query>         Search web and scrape results

[bold cyan]Drafts & Publishing:[/bold cyan]
  /drafts                 List all your drafts
  /draft "Title | Body"   Create a draft for approval
  /submit <draft_id>      Submit draft for approval
  /status <draft_id>      Check draft approval status

[bold cyan]Tips:[/bold cyan]
- Just type naturally to create content
- Paste any URL to import and remix content
- Use /brand-check before submitting drafts
- Try: "Write a blog post about stablecoin remittances"
- Try: "Import this tweet and remix it: https://x.com/..."
"""
        console.print(help_text)

    def _show_skills(self, skill_name: str | None = None) -> None:
        """Show skills."""
        if skill_name:
            self._show_skill_detail(skill_name)
            return

        skills = self.skill_registry.get_all_skills()

        if not skills:
            console.print("[yellow]No skills loaded.[/yellow]")
            return

        console.print("\n[bold]Available Skills:[/bold]\n")

        # Group by category
        by_category: dict[str, list] = {}
        for skill in skills:
            by_category.setdefault(skill.category, []).append(skill)

        for category, cat_skills in sorted(by_category.items()):
            console.print(f"[bold blue]{category.upper()}[/bold blue]")
            for skill in cat_skills:
                console.print(f"  • [green]{skill.name}[/green] - {skill.description}")
            console.print()

    def _show_skill_detail(self, skill_name: str) -> None:
        """Show details for a specific skill."""
        skill = self.skill_registry.get_skill(skill_name)

        if not skill:
            # Try fuzzy search
            results = self.skill_registry.search_skills(skill_name)
            if results:
                skill = results[0]
            else:
                console.print(f"[red]Skill not found: {skill_name}[/red]")
                return

        console.print(f"\n[bold]{skill.name}[/bold]")
        console.print(f"[dim]Category:[/dim] {skill.category}")
        console.print(f"[dim]Description:[/dim] {skill.description}")

        try:
            content = self.skill_registry.load_skill_content(skill.id)
            if len(content) > 500:
                content = content[:500] + "..."
            console.print(f"\n[dim]Content preview:[/dim]\n{content}")
        except Exception:
            pass

        console.print()


def run_shell(
    provider_manager: ProviderManager,
    auth_manager: AuthManager,
    skill_registry: SkillRegistry,
) -> bool:
    """Run the shell synchronously."""
    # Build welcome info like Kimi CLI
    welcome_info: list[WelcomeInfoItem] = []

    provider_name = provider_manager.get_current_provider_name()
    if provider_name:
        welcome_info.append(WelcomeInfoItem("Provider", provider_name))
    else:
        welcome_info.append(WelcomeInfoItem("Provider", "not configured", WelcomeLevel.WARN))

    agent_name = auth_manager.get_agent_name()
    if agent_name:
        welcome_info.append(WelcomeInfoItem("Agent", agent_name))
    else:
        welcome_info.append(WelcomeInfoItem("Agent", "Unknown", WelcomeLevel.WARN))

    shell = Shell(provider_manager, auth_manager, skill_registry, welcome_info)
    return asyncio.run(shell.run())
