"""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 rich.text import Text

from ..core.brand_checker import BrandCheckResult, BrandChecker
from ..core.content_remixer import ContentRemixer
from ..core.draft_manager import DraftContent, DraftManager, DraftResult
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.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."""

    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."""
        # 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}]")

        # 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 == "/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

    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:
                    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
  /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())
