#!/usr/bin/env python3
"""
CLI interface for AI message classifier
"""

import sys
import os
import logging
import click
from console import fg, fx, bg
import datetime as dt
from prompt_toolkit import PromptSession
from prompt_toolkit.history import FileHistory
from rich.console import Console
from rich.text import Text
from rich.syntax import Syntax
import shutil
from importlib.metadata import version as get_package_version

sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from ai_classifier import classify_message
from calendar_extractor import extract_calendar_details, add2calendar
from task_extractor import extract_task_details, add2task
from memo_extractor import extract_memo_details, add2keep
from provider import (
    chat_with_provider,
    PROVIDER_OPENROUTER,
    PROVIDER_OLLAMA,
    get_default_model,
    validate_provider,
)
from openrouter_client import OPENROUTER_MODEL
from smol_translator import translate_srt, MODELS_FREE as TRANSLATOR_MODELS
from smol_qrpayment import generate_payment_qr, MODELS_FREE as QRPAYMENT_MODELS
from smol_codeagent import run_codeagent, MODELS_FREE as CODEAGENT_MODELS
from comparison_spectrum import (
    SPECTRUM,
    PASS_RESULTS,
    is_valid_result,
    is_passing_result,
    normalize_result,
    TECHNICAL_FAILURE,
)
from model_tracker import track_model_usage, get_successful_models
from config import (
    get_preferred_models,
    set_default_model as set_config_default_model,
    format_system_prompt,
    get_system_prompt,
    set_system_prompt,
    reset_system_prompt,
    DEFAULT_SYSTEM_PROMPT,
)
import yaml

LOG_FILE = os.path.expanduser("~/agentgog.log")


def setup_logger():
    """Configure file logger for agentgog"""
    logger = logging.getLogger("agentgog")
    if logger.handlers:
        return logger
    logger.setLevel(logging.INFO)
    handler = logging.FileHandler(LOG_FILE)
    handler.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(message)s"))
    logger.addHandler(handler)
    return logger


logger = setup_logger()


def colorize(text, color):
    """Add color to text"""
    colors = {
        "green": fg.green,
        "blue": fg.cyan,
        "yellow": fg.yellow,
        "red": fg.red,
        "white": fg.default,
    }
    color_func = colors.get(color, fg.default)
    return color_func + text + fg.default


def strip_yaml_header(text):
    """Strip YAML frontmatter (between --- markers) from text.

    Args:
        text: Input text that may contain YAML frontmatter

    Returns:
        Text with YAML frontmatter removed, or original text if no frontmatter found
    """
    lines = text.split("\n")

    # Check if first line is ---
    if not lines or lines[0].strip() != "---":
        return text

    # Find the second ---
    for i in range(1, len(lines)):
        if lines[i].strip() == "---":
            # Return everything after the second ---
            result = "\n".join(lines[i + 1 :])
            return result.strip()

    # No closing --- found, return original text
    return text


def format_output(text):
    """Format text with basic styling"""
    result = text
    result = result.replace("[bold]", f"{fx.bold}")
    result = result.replace("[/bold]", f"{fx.default}")
    result = result.replace("[dim]", f"{fx.dim}")
    result = result.replace("[/dim]", f"{fx.default}")
    result = result.replace("[bold green]", f"{fx.bold}{fg.green}")
    result = result.replace("[/bold green]", f"{fx.default}{fg.default}")
    result = result.replace("[bold red]", f"{fx.bold}{fg.red}")
    result = result.replace("[/bold red]", f"{fx.default}{fx.default}")
    result = result.replace("[bold cyan]", f"{fx.bold}{fg.cyan}")
    result = result.replace("[/bold cyan]", f"{fx.default}{fg.default}")
    result = result.replace("[bold yellow]", f"{fx.bold}{fg.yellow}")
    result = result.replace("[/bold yellow]", f"{fx.default}{fx.default}")
    result = result.replace("[green]", f"{fg.green}")
    result = result.replace("[/green]", f"{fx.default}")
    result = result.replace("[cyan]", f"{fg.cyan}")
    result = result.replace("[/cyan]", f"{fx.default}")
    result = result.replace("[blue]", f"{fg.cyan}")
    result = result.replace("[/blue]", f"{fx.default}")
    result = result.replace("[yellow]", f"{fg.yellow}")
    result = result.replace("[/yellow]", f"{fx.default}")
    result = result.replace("[red]", f"{fg.red}")
    result = result.replace("[/red]", f"{fg.default}")
    return result


def print_version(ctx, param, value):
    if not value or ctx.resilient_parsing:
        return
    click.echo(get_package_version("agentgog"))
    ctx.exit()


@click.group()
@click.option(
    "--version",
    "-v",
    is_flag=True,
    callback=print_version,
    expose_value=False,
    is_eager=True,
    help="Show version and exit",
)
def cli():
    """AI message classifier and chat assistant"""
    pass


@cli.command()
@click.argument("message", nargs=-1, type=click.STRING)
@click.option(
    "--interactive", "-i", is_flag=True, help="Enter interactive classification mode"
)
@click.option(
    "--provider",
    "-p",
    default=PROVIDER_OPENROUTER,
    help="Provider to use (openrouter or ollama)",
)
def classify(message, interactive, provider):
    """
    Classify messages into categories: CALENDAR, TASK, MEMO

    For CALENDAR events, recursively extracts details and calls add2calendar() tool

    Examples:
        agentgog classify "Meeting tomorrow at 10am"
        agentgog classify "Buy groceries"
        agentgog classify -i
        agentgog classify "Meeting tomorrow" -p ollama
    """
    logger.info(f"Agentgog classification started with provider: {provider}")

    # Validate provider
    is_valid, error_msg = validate_provider(provider)
    if not is_valid:
        output = f"{fx.bold}{fg.red}Error:{fx.default} {error_msg}"
        print(format_output(output))
        sys.exit(1)

    try:
        if interactive:
            try:
                print(
                    format_output(
                        f"{fx.bold}AI Message Classifier - Interactive Mode (Provider: {provider}){fx.default}"
                    )
                )
                message_text = input(
                    format_output(f"{fx.bold}Enter message:{fx.default} ")
                ).strip()
                if not message_text:
                    return
            except KeyboardInterrupt:
                print(format_output(f"\n{fx.dim}Goodbye!{fx.default}"))
                return
        elif not message:
            click.echo("Error: Please provide a message to classify", err=True)
            click.echo("Use 'agentgog classify --help' for usage information", err=True)
            sys.exit(1)
        else:
            message_text = " ".join(message)
        success, classification, raw_response = classify_message(
            message_text, provider=provider
        )

        if success:
            colors = {"CALENDAR": "green", "TASK": "blue", "MEMO": "cyan"}
            color = colors.get(classification or "MEMO", "white")
            output = f"{fx.bold}Classification:{fx.default} {colorize(classification, color)}"
            print(format_output(output))

            if classification == "CALENDAR":
                logger.info("Calendar")
                print(
                    format_output(
                        f"{fx.bold}[cyan]Extracting calendar details...[/cyan]{fx.default}"
                    )
                )
                extract_success, calendar_details, extract_response = (
                    extract_calendar_details(message_text, provider=provider)
                )
                # logger.info(f"Response: {extract_response}")

                if extract_success and calendar_details:
                    print(
                        format_output(
                            f"{fx.bold}[cyan]Calling add2calendar() tool...[/cyan]{fx.default}"
                        )
                    )
                    result = add2calendar(
                        date=calendar_details.get("date"),
                        time=calendar_details.get("time"),
                        event_name=calendar_details.get("event_name"),
                        details=calendar_details.get("details"),
                        timezone="Europe/Prague",
                    )
                else:
                    error_msg = (
                        extract_response.get("error")
                        if extract_response
                        else "Failed to extract calendar details"
                    )
                    output = f"{fx.bold}{fg.red}Error extracting calendar details:{fx.default} {error_msg}"
                    print(format_output(output))
                    sys.exit(1)

            elif classification == "TASK":
                logger.info("Task")
                print(
                    format_output(
                        f"{fx.bold}[cyan]Extracting task details...[/cyan]{fx.default}"
                    )
                )
                extract_success, task_details, extract_response = extract_task_details(
                    message_text, provider=provider
                )
                # logger.info(f"Response: {extract_response}")

                if extract_success and task_details:
                    print(
                        format_output(
                            f"{fx.bold}[cyan]Calling add2task() tool...[/cyan]{fx.default}"
                        )
                    )
                    result = add2task(
                        task_name=task_details.get("task_name"),
                        due_date=task_details.get("due_date"),
                        priority=task_details.get("priority"),
                        details=task_details.get("details"),
                    )
                else:
                    error_msg = (
                        extract_response.get("error")
                        if extract_response
                        else "Failed to extract task details"
                    )
                    output = f"{fx.bold}{fg.red}Error extracting task details:{fx.default} {error_msg}"
                    print(format_output(output))
                    sys.exit(1)

            elif classification == "MEMO":
                logger.info("Memo")
                print(
                    format_output(
                        f"{fx.bold}[cyan]Extracting memo details...[/cyan]{fx.default}"
                    )
                )
                extract_success, memo_details, extract_response = extract_memo_details(
                    message_text, provider=provider
                )
                # logger.info(f"Response: {extract_response}")

                if extract_success and memo_details:
                    print(
                        format_output(
                            f"{fx.bold}[cyan]Calling add2keep() tool...[/cyan]{fx.default}"
                        )
                    )
                    result = add2keep(
                        title=memo_details.get("title"),
                        content=memo_details.get("content"),
                        labels=memo_details.get("labels"),
                    )
                else:
                    logger.error(extract_response)
                    error_msg = (
                        extract_response.get("error")
                        if extract_response
                        else "Failed to extract memo details"
                    )
                    output = f"{fx.bold}{fg.red}Error extracting memo details:{fx.default} {error_msg}"
                    print(format_output(output))
                    sys.exit(1)
        else:
            error_msg = raw_response.get("error") if raw_response else "Unknown error"
            output = f"{fx.bold}{fg.red}Error:{fx.default} {error_msg}"
            print(format_output(output))
            sys.exit(1)
    finally:
        logger.info("Agentgog classification ended")


@cli.command()
@click.argument("message", nargs=-1, type=click.STRING, required=False)
@click.option("--model", "-m", default=None, help="Model to use for chat")
@click.option(
    "--provider",
    "-p",
    default=PROVIDER_OPENROUTER,
    help="Provider to use (openrouter or ollama)",
)
@click.option(
    "--timeout",
    "-t",
    type=int,
    default=None,
    help="Timeout in seconds (provider defaults: ollama=90, openrouter=10)",
)
@click.option(
    "--input-file",
    "-f",
    type=click.Path(exists=True),
    help="Read prompt from file (use - for stdin)",
)
@click.option(
    "--output-file", "-o", type=click.Path(), help="Save final output to file"
)
@click.option(
    "--system-prompt",
    "-s",
    default=None,
    help="Custom system prompt (overrides config default)",
)
def chat(message, model, provider, timeout, input_file, output_file, system_prompt):
    """
    Chat with AI assistant (interactive by default)

    Examples:
        agentgog chat                          # Interactive mode (default)
        agentgog chat "What's the capital of France?"
        agentgog chat "Explain quantum physics" --model x-ai/grok-4.1-fast
        agentgog chat "Hello" -p ollama
        agentgog chat "Hi" -p ollama -m llama2
        agentgog chat "Complex question" -p ollama --timeout 120
        agentgog chat -f prompt.txt -o response.txt
        cat prompt.txt | agentgog chat -f -
        agentgog chat "Hello" -s "You are a coding expert"  # Custom system prompt

    The system prompt can be customized with the -s/--system-prompt option.
    Use {timestamp} placeholder to inject current time (automatically replaced).
    Default prompt is stored in ~/.config/agata/config.yaml
    """
    logger.info(f"Agentgog chat started with provider: {provider}")

    # Validate provider
    is_valid, error_msg = validate_provider(provider)
    if not is_valid:
        output = f"{fx.bold}{fg.red}Error:{fx.default} {error_msg}"
        print(format_output(output))
        sys.exit(1)

    # Use provider-specific default model if not specified
    if model is None:
        model = get_default_model(provider)

    # Use CLI-provided system prompt or get from config (with timestamp injection)
    system_prompt = format_system_prompt(system_prompt)

    try:
        # Determine if we should run in interactive mode (default) or non-interactive
        has_input = bool(input_file) or bool(message)

        if not has_input:
            # Default to interactive mode when no input provided
            interactive_chat_mode(system_prompt, model, provider, timeout)
            return

        # Determine input source for non-interactive mode
        if input_file:
            if input_file == "-":
                # Read from stdin
                message_text = sys.stdin.read().strip()
            else:
                # Read from file
                with open(input_file, "r") as f:
                    message_text = f.read().strip()
            # Strip YAML frontmatter if present
            message_text = strip_yaml_header(message_text)
        else:
            message_text = " ".join(message)

        if not message_text:
            click.echo("Error: Input is empty", err=True)
            sys.exit(1)

        success, response, raw_response = chat_with_provider(
            provider=provider,
            message=message_text,
            system_prompt=system_prompt,
            model=model,
            timeout=timeout,
        )

        # Track model usage (logs all failures)
        error_msg = raw_response.get("error") if raw_response else None
        track_model_usage(model, success, error_msg)

        if success:
            if output_file:
                # Save only the final output to file
                with open(output_file, "w") as f:
                    f.write(response)
                logger.info(f"Response saved to: {output_file}")
            else:
                print(
                    format_output(
                        f"{fx.bold}{fg.green}{provider} {model}:{fx.default} {response}"
                    )
                )
                # print(format_output(f"{response}"))
        else:
            error_msg = raw_response.get("error") if raw_response else "Unknown error"
            output = f"{fx.bold}{fg.red}Error:{fx.default} {error_msg}"
            if output_file:
                with open(output_file, "w") as f:
                    f.write(f"Error: {error_msg}")
            print(format_output(output))
            sys.exit(1)
    finally:
        logger.info("Agentgog chat ended")


def visible_length(text):
    """Get the visible/display length of text, stripping ANSI codes and handling wide chars.

    Args:
        text: Text that may contain ANSI escape codes

    Returns:
        Visible character count (accounting for display width)
    """
    import re

    # Strip ANSI escape sequences
    ansi_escape = re.compile(r"\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])")
    clean = ansi_escape.sub("", text)
    # Count visible chars (most chars are width 1, emoji/CJK can be width 2)
    # For simplicity, we'll strip non-ASCII which handles most wide chars poorly
    # A proper implementation would use wcwidth library
    length = 0
    for char in clean:
        if ord(char) < 128:
            length += 1
        else:
            # Assume wide characters take 2 columns
            length += 2
    return length


def print_with_full_bg(text, terminal_width, bg_color, fg_color=None):
    """Print text with full-width background, handling long lines.

    Args:
        text: Text to print
        terminal_width: Terminal width in columns
        bg_color: Background color escape sequence
        fg_color: Foreground color escape sequence (optional)
    """
    visible_len = visible_length(text)
    # Calculate how many full terminal widths we need
    bg_repeats = max(1, (visible_len + terminal_width - 1) // terminal_width)

    # Print background lines
    for _ in range(bg_repeats):
        print(f"{bg_color}{' ' * terminal_width}{fx.default}")

    # Move cursor up and print the text
    print(f"\033[{bg_repeats}A", end="")  # Move cursor up
    if fg_color:
        print(f"{bg_color}{fg_color}{text}{fx.default}")
    else:
        print(f"{bg_color}{text}{fx.default}")

    # Move cursor down if we printed extra lines
    if bg_repeats > 1:
        print(f"\033[{bg_repeats - 1}B", end="")


def parse_code_blocks(text):
    """Parse text and detect code blocks (```...```).

    Returns a list of tuples: (text, is_code, needs_padding, language)
    where is_code indicates if this segment is inside a code block,
    needs_padding indicates if this is a full line that needs terminal width padding,
    and language is the detected language (e.g., 'python', 'javascript', etc.) or None.
    """
    lines = text.split("\n")
    segments = []
    in_code_block = False
    code_buffer = []
    current_language = None

    i = 0
    while i < len(lines):
        line = lines[i]
        stripped = line.strip()

        # Check for triple backticks
        if (
            stripped.startswith("```")
            and stripped.endswith("```")
            and len(stripped) >= 6
        ):
            # Single line code block
            if in_code_block:
                # Closing current block
                code_buffer.append(line)
                segments.append(("\n".join(code_buffer), True, False, current_language))
                code_buffer = []
                in_code_block = False
                current_language = None
            else:
                # Opening new block
                if code_buffer:
                    segments.append(("\n".join(code_buffer), False, True, None))
                    code_buffer = []
                code_buffer.append(line)
                in_code_block = True
                # Extract language from opening tag (e.g., ```python)
                lang = stripped[3:-3].strip()
                current_language = lang if lang else None
            i += 1
        elif stripped == "```" or stripped.startswith("```"):
            # Code block delimiter
            if in_code_block:
                # Closing code block
                code_buffer.append(line)
                segments.append(("\n".join(code_buffer), True, False, current_language))
                code_buffer = []
                in_code_block = False
                current_language = None
            else:
                # Opening code block
                if code_buffer:
                    segments.append(("\n".join(code_buffer), False, True, None))
                    code_buffer = []
                code_buffer.append(line)
                in_code_block = True
                # Extract language from opening tag
                lang = stripped[3:].strip()
                current_language = lang if lang else None
            i += 1
        else:
            code_buffer.append(line)
            i += 1

    # Add remaining content
    if code_buffer:
        # If we ended while still in a code block without closing, treat as normal text
        segments.append(
            (
                "\n".join(code_buffer),
                in_code_block,
                not in_code_block,
                None if not in_code_block else current_language,
            )
        )

    return segments


def interactive_chat_mode(
    system_prompt, initial_model, provider=PROVIDER_OPENROUTER, timeout=None
):
    """Interactive mode for chatting with AI"""
    messages = [{"role": "system", "content": system_prompt}]
    history_file = os.path.expanduser("~/.agentgog_history")
    rich_console = Console()
    model = initial_model

    session = PromptSession(history=FileHistory(history_file))

    print(
        format_output(
            f"{fx.bold}AI Chat Assistant - Interactive Mode (Provider: {provider}){fx.default}"
        )
    )
    print(format_output(f"{fx.bold}     Model: {model}{fx.default}"))
    print(
        format_output(
            f"Commands: {fg.cyan}/q /quit /exit{fx.default} - quit, {fg.cyan}/c /clear /reset /r{fx.default} - clear history, {fg.cyan}/m /model <name>{fx.default} - switch model, {fg.cyan}/mf /modelsfree{fx.default} - list free models, {fg.cyan}/mp /modelspaid{fx.default} - list paid models, {fg.cyan}/mx /modelsx <model>{fx.default} - list preferred models or set default"
        )
    )
    print(
        format_output(
            f"{fx.dim}Use arrow keys to navigate history, Ctrl+C to quit\n{fx.default}"
        )
    )

    while True:
        try:
            user_message = session.prompt("You: ").strip()

            if user_message.lower() in ("/q", "/quit", "quit", "exit", "/exit"):
                print(format_output(f"{fx.dim}Goodbye!{fx.default}"))
                break

            if user_message.lower() in ("/c", "/clear", "/reset", "/r"):
                messages = [{"role": "system", "content": system_prompt}]
                print(
                    format_output(
                        f"{fx.dim}Conversation history cleared.\n{fx.default}"
                    )
                )
                continue

            if user_message.lower().startswith(
                "/m "
            ) or user_message.lower().startswith("/model "):
                parts = user_message.split(None, 1)
                if len(parts) < 2 or not parts[1].strip():
                    print(
                        format_output(
                            f"{fx.bold}{fg.red}Error:{fx.default} Please provide a model name (e.g., /model openai/gpt-4)"
                        )
                    )
                    continue
                model = parts[1].strip()
                print(
                    format_output(f"{fg.green}Model switched to:{fx.default} {model}\n")
                )
                continue

            if user_message.lower() in ("/m", "/model"):
                print(format_output(f"{fx.dim}Current model:{fx.default} {model}"))
                continue

            if user_message.lower() in ("/mf", "/modelsfree"):
                print(
                    format_output(
                        f"{fx.dim}Fetching free models from OpenRouter...{fx.default}"
                    )
                )
                try:
                    from check_openrouter_prices import (
                        fetch_models,
                        filter_free_models,
                        print_free_models,
                    )

                    success, models, error = fetch_models()
                    if success:
                        free_models = filter_free_models(models)
                        if free_models:
                            print()
                            print_free_models(free_models)
                            print()
                        else:
                            print(
                                format_output(
                                    f"{fx.dim}No free models found{fx.default}"
                                )
                            )
                    else:
                        print(format_output(f"{fg.red}Error:{fx.default} {error}"))
                except Exception as e:
                    print(
                        format_output(f"{fg.red}Error fetching models:{fx.default} {e}")
                    )
                print()
                continue

            if user_message.lower() in ("/mp", "/modelspaid"):
                print(
                    format_output(
                        f"{fx.dim}Fetching paid models from OpenRouter...{fx.default}"
                    )
                )
                try:
                    from check_openrouter_prices import (
                        fetch_models,
                        filter_paid_models,
                        print_paid_models,
                    )

                    success, models, error = fetch_models()
                    if success:
                        paid_models = filter_paid_models(models)
                        if paid_models:
                            print()
                            print_paid_models(paid_models)
                            print()
                        else:
                            print(
                                format_output(
                                    f"{fx.dim}No paid models found{fx.default}"
                                )
                            )
                    else:
                        print(format_output(f"{fg.red}Error:{fx.default} {error}"))
                except Exception as e:
                    print(
                        format_output(f"{fg.red}Error fetching models:{fx.default} {e}")
                    )
                print()
                continue

            if user_message.lower().startswith(
                "/mx"
            ) or user_message.lower().startswith("/modelsx"):
                parts = user_message.split(None, 1)

                # If a model name is provided, try to set it as default
                if len(parts) > 1 and parts[1].strip():
                    requested_model = parts[1].strip()
                    successful_models = get_successful_models()

                    if requested_model in successful_models:
                        set_config_default_model(provider, requested_model)
                        model = requested_model
                        print(
                            format_output(
                                f"{fg.green}Model switched to:{fx.default} {model} (set as default)"
                            )
                        )
                    else:
                        print(
                            format_output(
                                f"{fx.bold}{fg.red}Error:{fx.default} Model '{requested_model}' must be in working models group first"
                            )
                        )
                        print(
                            format_output(
                                f"{fx.dim}Use the model first to verify it works, then you can set it as default{fx.default}"
                            )
                        )
                    print()
                    continue

                # List preferred models
                preferred_models = get_preferred_models()
                current_default = get_default_model(provider)

                print(format_output(f"{fx.dim}Preferred models:{fx.default}"))
                print()
                for pref_model in preferred_models:
                    if pref_model == current_default:
                        print(
                            format_output(
                                f"  {fg.green}*{fg.cyan} {pref_model}{fx.default} {fg.green}(default){fx.default}"
                            )
                        )
                    else:
                        print(format_output(f"  {fg.cyan}{pref_model}{fx.default}"))
                print()
                print(
                    format_output(
                        f"{fx.dim}Use '/mx <model>' to set a working model as default{fx.default}"
                    )
                )
                print()
                continue

            if not user_message:
                continue

            messages.append({"role": "user", "content": user_message})

            success, response, raw_response = chat_with_provider(
                provider=provider, messages=messages, model=model, timeout=timeout
            )

            # Track model usage (logs all failures)
            error_msg = raw_response.get("error") if raw_response else None
            track_model_usage(model, success, error_msg)

            if success:
                messages.append({"role": "assistant", "content": response})
                # Use rich for full-width background including model name
                terminal_width = shutil.get_terminal_size().columns
                header = f"{provider} {model}:"

                # Parse response for code blocks
                segments = parse_code_blocks(response)

                # Print header with full-width background
                print_with_full_bg(header, terminal_width, bg.grey11, fx.bold)

                # Process each segment in order
                for segment_text, is_code, needs_padding, language in segments:
                    if not segment_text:
                        continue

                    if is_code:
                        # Code block with syntax highlighting if language detected
                        if language:
                            # Extract just the code content (remove the ``` lines)
                            code_lines = segment_text.split("\n")
                            if code_lines[0].strip().startswith("```"):
                                code_lines = code_lines[1:]
                            if code_lines and code_lines[-1].strip() == "```":
                                code_lines = code_lines[:-1]
                            code_content = "\n".join(code_lines)

                            # Add empty line with dark blue background before code
                            rich_console.print(
                                Text(" " * terminal_width, style="on dark_blue")
                            )

                            # Create syntax highlighted code with dark background
                            syntax = Syntax(
                                code_content,
                                language,
                                theme="monokai",
                                background_color="grey11",
                                line_numbers=False,
                                word_wrap=True,
                                padding=0,
                            )
                            rich_console.print(syntax)

                            # Add empty line with dark blue background after code
                            rich_console.print(
                                Text(" " * terminal_width, style="on dark_blue")
                            )
                        else:
                            # No language specified - use yellow on dark background
                            lines = segment_text.split("\n")
                            for line in lines:
                                print_with_full_bg(
                                    line, terminal_width, bg.grey11, fg.yellow
                                )
                    else:
                        # Regular text: dark background with padding for full width
                        lines = segment_text.split("\n")
                        for line in lines:
                            if needs_padding:
                                print_with_full_bg(line, terminal_width, bg.grey11)
                            else:
                                rich_console.print(Text(line, style="on grey11"))

                print()
            else:
                error_msg = (
                    raw_response.get("error") if raw_response else "Unknown error"
                )
                output = f"  → {fx.bold}{fg.red}Error:{fx.default} {error_msg}\n"
                print(format_output(output))

        except KeyboardInterrupt:
            print(format_output(f"\n{fx.dim}Goodbye!{fx.default}"))
            break


@cli.command("translator")
@click.option(
    "--srt-file",
    "-s",
    "inputfile",
    required=True,
    type=click.Path(exists=True),
    help="Input SRT file to translate",
)
@click.option("--model", "-m", required=True, help="Model to use for translation")
@click.option(
    "--provider",
    "-p",
    default=PROVIDER_OPENROUTER,
    help="Provider (currently only openrouter supported)",
)
def translator(inputfile, model, provider):
    """
    Translate SRT subtitles from English to Czech using smolagents.

    Examples:
        agentgog translator -s subtitles.srt --model google/gemma-3-27b-it:free
        agentgog translator -s subtitles.srt --model google/gemma-3-27b-it:free -p openrouter
    """
    logger.info(
        f"Translator command started with file: {inputfile}, model: {model}, provider: {provider}"
    )

    # Validate provider
    is_valid, error_msg = validate_provider(provider)
    if not is_valid:
        output = f"{fx.bold}{fg.red}Error:{fx.default} {error_msg}"
        print(format_output(output))
        sys.exit(1)

    # Note: translator currently only supports OpenRouter
    if provider != PROVIDER_OPENROUTER:
        output = f"{fx.bold}{fg.yellow}Warning:{fx.default} Translator currently only supports OpenRouter provider"
        print(format_output(output))

    try:
        OUTPUTFILE_a, OUTPUTFILE_ext = os.path.splitext(inputfile)

        with open(inputfile) as f:
            srt_content = f.read()

        print(
            format_output(
                f"{fx.bold}[cyan]Using model:{fx.default}{fx.default} {model}"
            )
        )

        translated = translate_srt(model, srt_content)

        output_path = f"{OUTPUTFILE_a}_cs{OUTPUTFILE_ext}"
        with open(output_path, "w") as f:
            f.write(f"{translated}")

        logger.info(f"Translation completed. Output saved to: {output_path}")

        print(
            format_output(
                f"{fx.bold}[green]Translation complete!{fx.default}{fx.default}"
            )
        )
        print(format_output(f"{fx.dim}Output saved to:{fx.default} {output_path}"))
    except Exception as e:
        logger.error(f"Translator command failed: {e}")
        print(format_output(f"{fx.bold}[red]Error:{fx.default}{fx.default} {e}"))
        sys.exit(1)


@cli.command("qrpayment")
@click.argument("prompt")
@click.option(
    "--model", "-m", required=True, help="Model to use for QR payment generation"
)
@click.option(
    "--provider",
    "-p",
    default=PROVIDER_OPENROUTER,
    help="Provider (currently only openrouter supported)",
)
def qrpayment(prompt, model, provider):
    """
    Generate QR code for payment based on the given prompt using smolagents.

    Examples:
        agentgog qrpayment "Transfer 500 CZK to account 1234567890/0300" --model google/gemma-3-27b-it:free
        agentgog qrpayment "Pay 100 to 0987654321/0800 with VS 123456" -p openrouter
    """
    logger.info(
        f"QR payment command started with prompt: {prompt}, model: {model}, provider: {provider}"
    )

    # Validate provider
    is_valid, error_msg = validate_provider(provider)
    if not is_valid:
        output = f"{fx.bold}{fg.red}Error:{fx.default} {error_msg}"
        print(format_output(output))
        sys.exit(1)

    # Note: qrpayment currently only supports OpenRouter
    if provider != PROVIDER_OPENROUTER:
        output = f"{fx.bold}{fg.yellow}Warning:{fx.default} QR payment generation currently only supports OpenRouter provider"
        print(format_output(output))

    try:
        print(
            format_output(
                f"{fx.bold}[cyan]Using model:{fx.default}{fx.default} {model}"
            )
        )
        print(format_output(f"{fx.dim}Analyzing payment details...{fx.default}"))

        output_path = generate_payment_qr(model, prompt)

        logger.info(f"QR code generated and saved to: {output_path}")

        print(
            format_output(f"{fx.bold}[green]QR code generated!{fx.default}{fx.default}")
        )
        print(format_output(f"{fx.dim}QR code saved to:{fx.default} {output_path}"))
    except Exception as e:
        logger.error(f"QR payment command failed: {e}")
        print(format_output(f"{fx.bold}[red]Error:{fx.default}{fx.default} {e}"))
        sys.exit(1)


@cli.command("codeagent")
@click.argument("prompt", required=False)
@click.option(
    "--model",
    "-m",
    default=None,
    help="Model to use for codeagent (uses provider default if not specified)",
)
@click.option(
    "--max-steps", "-x", default=10, help="Maximum number of steps for CodeAgent"
)
@click.option(
    "--provider",
    "-p",
    default=PROVIDER_OPENROUTER,
    help="Provider to use (openrouter or ollama)",
)
@click.option(
    "--input-file",
    "-f",
    type=click.Path(exists=True),
    help="Read prompt from file (use - for stdin)",
)
@click.option(
    "--output-file", "-o", type=click.Path(), help="Save final output to file"
)
def codeagent(prompt, model, max_steps, provider, input_file, output_file):
    """
    Run vanilla CodeAgent with built-in prompt for general AI tasks.

    Examples:
        agentgog codeagent "Write a Python function to calculate fibonacci" --model google/gemma-3-27b-it:free
        agentgog codeagent "Explain how neural networks work" --model google/gemma-3-27b-it:free
        agentgog codeagent "Create a simple web scraper" --max-steps 20 -p openrouter
        agentgog codeagent "Calculate 5 factorial" -p ollama --model llama3.2
        agentgog codeagent "What is Python?" (uses default model)
        agentgog codeagent -f prompt.txt -o result.txt
        cat prompt.txt | agentgog codeagent -f -
    """
    logger.info(
        f"Codeagent command started with prompt: {prompt}, model: {model}, max_steps: {max_steps}, provider: {provider}"
    )

    # Validate provider
    is_valid, error_msg = validate_provider(provider)
    if not is_valid:
        output = f"{fx.bold}{fg.red}Error:{fx.default} {error_msg}"
        print(format_output(output))
        sys.exit(1)

    # Use provider-specific default model if not specified
    if model is None:
        model = get_default_model(provider)

    # Determine input source
    if input_file:
        if input_file == "-":
            # Read from stdin
            prompt_text = sys.stdin.read().strip()
        else:
            # Read from file
            with open(input_file, "r") as f:
                prompt_text = f.read().strip()
        # Strip YAML frontmatter if present
        prompt_text = strip_yaml_header(prompt_text)
    elif prompt:
        prompt_text = prompt
    else:
        click.echo("Error: Please provide a prompt or an input file", err=True)
        click.echo("Use 'agentgog codeagent --help' for usage information", err=True)
        sys.exit(1)

    if not prompt_text:
        click.echo("Error: Input is empty", err=True)
        sys.exit(1)

    try:
        if not output_file:
            # Only print progress info if not in batch mode (no output file)
            print(
                format_output(
                    f"{fx.bold}[cyan]Using model:{fx.default}{fx.default} {model}"
                )
            )
            print(
                format_output(
                    f"{fx.bold}[cyan]Provider:{fx.default}{fx.default} {provider}"
                )
            )
            print(format_output(f"{fx.dim}Running CodeAgent...{fx.default}"))

        result = run_codeagent(model, prompt_text, max_steps, provider=provider)

        logger.info(f"Codeagent completed successfully")

        if output_file:
            # Save only the final output to file
            with open(output_file, "w") as f:
                f.write(result)
            logger.info(f"Result saved to: {output_file}")
        else:
            print(
                format_output(
                    f"{fx.bold}[green]CodeAgent result:{fx.default}{fx.default}"
                )
            )
            print(format_output(f"{result}"))
    except Exception as e:
        logger.error(f"Codeagent command failed: {e}")
        print(format_output(f"{fx.bold}[red]Error:{fx.default}{fx.default} {e}"))
        sys.exit(1)


def extract_expected_from_yaml(yaml_file_path):
    """Extract the 'expected:' value from a YAML file.

    Args:
        yaml_file_path: Path to the YAML file

    Returns:
        The expected string value or None if not found/error
    """
    try:
        with open(yaml_file_path, "r") as f:
            content = f.read()

        # Try to parse as YAML frontmatter first (between --- markers)
        lines = content.split("\n")
        if lines and lines[0].strip() == "---":
            # Find the second ---
            for i in range(1, len(lines)):
                if lines[i].strip() == "---":
                    yaml_content = "\n".join(lines[1:i])
                    data = yaml.safe_load(yaml_content)
                    if data and "expected" in data:
                        return data["expected"]
                    break

        # Try to parse entire content as YAML
        data = yaml.safe_load(content)
        if data and isinstance(data, dict) and "expected" in data:
            return data["expected"]

        return None
    except Exception as e:
        logger.error(f"Error parsing YAML file {yaml_file_path}: {e}")
        return None


@cli.command()
@click.option(
    "--input-file",
    "-f",
    required=True,
    type=click.Path(exists=True),
    help="Path to file containing the result to compare",
)
@click.option(
    "--expected-result-yaml",
    "-e",
    required=True,
    type=click.Path(exists=True),
    help='Path to YAML file with expected result (contains "expected:" header)',
)
@click.option("--output-file", "-o", type=click.Path(), help="Save output to file")
@click.option("--model", "-m", default=None, help="Model to use for comparison")
@click.option(
    "--provider",
    "-p",
    default=PROVIDER_OPENROUTER,
    help="Provider to use (openrouter or ollama)",
)
def compare(input_file, expected_result_yaml, output_file, model, provider):
    """
    Semantically compare a result file with expected result from YAML.

    The AI model compares the content of the input file with the 'expected:'
    value from the YAML file and returns: same | similar | differ

    Examples:
        agentgog compare -f result.txt -e expected.yaml
        agentgog compare -f output.txt -e test.yaml -o comparison.txt
        agentgog compare -f result.txt -e expected.yaml -p ollama
    """
    logger.info(
        f"Compare command started with input: {input_file}, expected: {expected_result_yaml}"
    )

    # Validate provider
    is_valid, error_msg = validate_provider(provider)
    if not is_valid:
        output = f"{fx.bold}{fg.red}Error:{fx.default} {error_msg}"
        print(format_output(output))
        sys.exit(1)

    # Use provider-specific default model if not specified
    if model is None:
        model = get_default_model(provider)

    try:
        # Read input file content
        with open(input_file, "r") as f:
            input_content = f.read().strip()

        if not input_content:
            click.echo("Error: Input file is empty", err=True)
            sys.exit(1)

        # Extract expected value from YAML
        expected_content = extract_expected_from_yaml(expected_result_yaml)
        if expected_content is None:
            click.echo(
                "Error: Could not find 'expected:' header in YAML file", err=True
            )
            sys.exit(1)

        # Build comparison prompt
        spectrum_str = " | ".join(SPECTRUM)
        system_prompt = f"""You are a semantic comparison engine. Compare the ACTUAL result with the EXPECTED result.

Respond with exactly one word and nothing else:
- "same" - if the actual result matches the expected result semantically (identical or equivalent meaning)
- "similar" - if the actual result is close to expected but has minor differences
- "differ" - if the actual result is significantly different from expected

Be strict but fair. Only respond with: {spectrum_str}"""

        comparison_message = f"""EXPECTED:
{expected_content}

ACTUAL:
{input_content}"""

        # Call AI model for comparison
        success, response, raw_response = chat_with_provider(
            provider=provider,
            message=comparison_message,
            system_prompt=system_prompt,
            model=model,
        )

        if success:
            # Normalize response using spectrum utilities
            result = normalize_result(response)

            # Colorize output
            color_map = {"same": "green", "similar": "yellow", "differ": "red"}
            color = color_map.get(result, "white")
            formatted_output = f"{fx.bold}Comparison result:{fx.default} {colorize(result.upper(), color)}"

            if output_file:
                with open(output_file, "w") as f:
                    f.write(result)
                logger.info(f"Comparison result saved to: {output_file}")
                print(format_output(formatted_output))
                print(
                    format_output(f"{fx.dim}Result saved to:{fx.default} {output_file}")
                )
            else:
                print(format_output(formatted_output))

            logger.info(f"Compare command completed with result: {result}")
        else:
            error_msg = raw_response.get("error") if raw_response else "Unknown error"
            output = f"{fx.bold}{fg.red}Error:{fx.default} {error_msg}"
            if output_file:
                with open(output_file, "w") as f:
                    f.write(f"Error: {error_msg}")
            print(format_output(output))
            sys.exit(1)
    except Exception as e:
        logger.error(f"Compare command failed: {e}")
        print(format_output(f"{fx.bold}{fg.red}Error:{fx.default} {e}"))
        sys.exit(1)


@cli.command()
@click.option("--get", "-g", is_flag=True, help="Show current system prompt")
@click.option("--set", "-s", "set_prompt", help="Set custom system prompt")
@click.option("--reset", "-r", is_flag=True, help="Reset to default prompt")
def system_prompt_cmd(get, set_prompt, reset):
    """
    Manage the default system prompt for chat command.

    Examples:
        agentgog system-prompt --get                    # Show current prompt
        agentgog system-prompt --set "Your prompt"      # Set custom prompt
        agentgog system-prompt --reset                  # Reset to default

    Use {timestamp} in your prompt to inject the current datetime.
    """
    if reset:
        reset_system_prompt()
        print(
            format_output(
                f"{fx.bold}{fg.green}System prompt reset to default{fx.default}"
            )
        )
        print(format_output(f"{fx.dim}Default:{fx.default}\n{DEFAULT_SYSTEM_PROMPT}"))
        return

    if set_prompt:
        set_system_prompt(set_prompt)
        print(format_output(f"{fx.bold}{fg.green}System prompt updated{fx.default}"))
        print(format_output(f"{fx.dim}New prompt:{fx.default}\n{set_prompt}"))
        if "{timestamp}" not in set_prompt:
            print(
                format_output(
                    f"{fx.bold}{fg.yellow}Warning:{fx.default} No {{timestamp}} placeholder found"
                )
            )
        return

    # Default: show current prompt
    current = get_system_prompt()
    print(format_output(f"{fx.bold}Current system prompt:{fx.default}"))
    print(format_output(current))
    print(format_output(f"\n{fx.dim}Formatted example:{fx.default}"))
    print(format_output(format_system_prompt()))


def main():
    """Main entry point for backward compatibility"""
    cli(prog_name="agentgog")


if __name__ == "__main__":
    main()
