"""LLM Analyser - CSV data processing with LLM-powered structured output generation."""

import argparse
import logging
import os
import sys
from pathlib import Path

from pplyz.config import (
    API_KEY_ENV_VARS,
    DATA_DIR,
    DEFAULT_INPUT_COLUMNS_ENV_VAR,
    DEFAULT_OUTPUT_FIELDS_ENV_VAR,
    DEFAULT_PREVIEW_ROWS,
    PREVIEW_ROWS_ENV_VAR,
    SUPPORTED_MODELS,
    get_default_model,
)
from pplyz.llm_client import LLMClient
from pplyz.processor import CSVProcessor
from pplyz.schemas import create_output_model_from_string
from pplyz.settings import load_runtime_configuration

DOCS_URL = "https://github.com/masaki39/pplyz#readme"

try:
    from prompt_toolkit import PromptSession
    from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
    from prompt_toolkit.history import FileHistory, InMemoryHistory
except ImportError:  # pragma: no cover - optional dependency
    PromptSession = None
    AutoSuggestFromHistory = None
    FileHistory = None
    InMemoryHistory = None

# Configure logging
logging.basicConfig(
    level=logging.INFO,
    format="%(message)s",  # Simple format for user-facing output
    handlers=[logging.StreamHandler(sys.stdout)],
)

logger = logging.getLogger(__name__)


class CompactHelpFormatter(argparse.RawDescriptionHelpFormatter):
    """Help formatter without metavar duplication in option listing."""

    def _format_action_invocation(self, action):
        if not action.option_strings:
            return super()._format_action_invocation(action)
        # Display short flags first for consistent column width
        option_strings = sorted(action.option_strings, key=len)
        return ", ".join(option_strings)


def parse_arguments() -> argparse.Namespace:
    """Parse command-line arguments.

    Returns:
        Parsed arguments namespace.
    """
    parser = argparse.ArgumentParser(
        usage="pplyz [INPUT] [options]",
        formatter_class=CompactHelpFormatter,
        epilog=f"Docs & issues: {DOCS_URL}",
    )

    parser.add_argument(
        "input_path",
        nargs="?",
        metavar="INPUT",
        help="Positional input CSV path",
    )

    parser.add_argument(
        "--input",
        "-i",
        dest="input_columns",
        type=str,
        help="Comma-separated list of columns to use as LLM input (e.g., 'title,abstract,keywords')",
    )

    output_help = (
        'Output fields definition (e.g., "is_relevant:bool,summary:str,keywords:list[str]"). '
        "Supported types: bool, int, float, str, list[str], list[int], list[float], "
        "list[bool], dict."
    )

    parser.add_argument(
        "--output",
        "-o",
        type=str,
        dest="output_fields",
        help=output_help,
    )

    parser.add_argument(
        "--preview",
        "-p",
        action="store_true",
        help="Preview results on sample rows without saving",
    )

    default_model = get_default_model()

    parser.add_argument(
        "--model",
        "-m",
        type=str,
        default=default_model,
        help=f"LLM model name (default: {default_model})",
    )

    parser.add_argument(
        "--force",
        "-f",
        dest="force",
        action="store_true",
        help="Force reprocessing of all rows and overwrite previous output",
    )
    parser.add_argument(
        "--no-resume",
        dest="force",
        action="store_true",
        help=argparse.SUPPRESS,
    )

    parser.add_argument(
        "--list",
        "-l",
        action="store_true",
        dest="list_models",
        help="List supported models and exit",
    )

    return parser.parse_args()


def resolve_preview_rows() -> int:
    """Resolve preview row count from environment/config."""
    value = os.environ.get(PREVIEW_ROWS_ENV_VAR)
    if value is None:
        return DEFAULT_PREVIEW_ROWS

    try:
        rows = int(value)
        if rows <= 0:
            raise ValueError
        return rows
    except ValueError:
        logger.warning(
            "Invalid preview row count '%s'. Falling back to %d rows.",
            value,
            DEFAULT_PREVIEW_ROWS,
        )
        return DEFAULT_PREVIEW_ROWS


def get_user_prompt() -> str:
    """Get the analysis prompt from user interactively.

    Returns:
        The user-provided prompt.
    """
    print("\n" + "=" * 60)
    print("LLM ANALYSER - Interactive Prompt Input")
    print("=" * 60)
    print(
        "\nPlease describe the task you want the LLM to perform on each row.\n"
        "The LLM will receive the selected columns and generate structured output.\n"
    )
    print("Examples:")
    print('  - "Classify the sentiment as positive, negative, or neutral"')
    print('  - "Extract key findings and methodology from the abstract"')
    print('  - "Summarize the main topic in 1-2 sentences"\n')

    prompt_session = _build_prompt_session()

    try:
        if prompt_session is not None:
            prompt = prompt_session.prompt("Enter your task description: ").strip()
        else:
            prompt = input("Enter your task description: ").strip()
    except (KeyboardInterrupt, EOFError):
        print("\nPrompt entry cancelled. Exiting.")
        sys.exit(1)

    if not prompt:
        print("Error: Prompt cannot be empty")
        sys.exit(1)

    return prompt


def _build_prompt_session():
    """Create a prompt_toolkit session when available for better line editing."""
    if PromptSession is None:
        return None

    auto_suggest = AutoSuggestFromHistory()
    history = InMemoryHistory()

    try:
        history_path = Path.home() / ".pplyz_prompt_history"
        history_path.parent.mkdir(parents=True, exist_ok=True)
        history = FileHistory(str(history_path))
    except OSError:
        history = InMemoryHistory()

    return PromptSession(history=history, auto_suggest=auto_suggest)


def list_supported_models() -> None:
    """Print list of supported models."""
    print("\n" + "=" * 70)
    print("SUPPORTED MODELS")
    print("=" * 70)
    print("\nNote: LiteLLM supports many more models. These are common examples.\n")

    for model_name, description in SUPPORTED_MODELS.items():
        print(f"  {model_name}")
        print(f"    {description}\n")

    print("=" * 70)
    print("\nFor the full list, visit: https://docs.litellm.ai/docs/providers")
    print()


def main() -> None:
    """Main entry point for the LLM Analyser CLI."""
    # Load environment/configuration files
    load_runtime_configuration()

    # Parse arguments
    args = parse_arguments()

    positional_input = getattr(args, "input_path", None)

    default_columns = os.environ.get(DEFAULT_INPUT_COLUMNS_ENV_VAR)
    default_fields = os.environ.get(DEFAULT_OUTPUT_FIELDS_ENV_VAR)

    resolved_input = positional_input
    resolved_columns = args.input_columns or default_columns
    resolved_fields = args.output_fields or default_fields

    # Handle --list
    if args.list_models:
        list_supported_models()
        sys.exit(0)

    # Validate required arguments (only if not using --list)
    missing_args = []
    if not resolved_input:
        missing_args.append("INPUT (positional)")
    if not resolved_columns:
        missing_args.append("--input/-i")
    if not resolved_fields:
        missing_args.append("--output/-o")

    if missing_args:
        print("Error: the following arguments are required: " + ", ".join(missing_args))
        print("Use --help for more information")
        sys.exit(1)

    # Parse columns
    columns = [col.strip() for col in resolved_columns.split(",")]

    if not columns:
        print("Error: At least one column must be specified")
        sys.exit(1)

    # Resolve input path
    input_path = Path(resolved_input)
    if not input_path.is_absolute():
        # Try relative to DATA_DIR first
        data_path = DATA_DIR / input_path
        if data_path.exists():
            input_path = data_path
        else:
            # Use as relative to current directory
            input_path = input_path.resolve()

    if not input_path.exists():
        print(f"Error: Input file not found: {input_path}")
        sys.exit(1)

    if input_path.is_dir():
        print(f"Error: Input path must be a CSV file, not a directory: {input_path}")
        sys.exit(1)

    if input_path.suffix.lower() != ".csv":
        print(f"Error: Input file must have a .csv extension: {input_path}")
        sys.exit(1)

    # Get user prompt interactively
    prompt = get_user_prompt()

    # Create response model from the requested output schema when provided
    response_model = None
    if resolved_fields:
        try:
            response_model = create_output_model_from_string(resolved_fields)
            print(f"\n✓ Using output schema: {resolved_fields}")
        except Exception as e:
            print(f"Error parsing fields: {e}")
            sys.exit(1)

    # Initialize LLM client
    logger.info("Initializing LLM client (model: %s)...", args.model)
    try:
        llm_client = LLMClient(model_name=args.model)
        logger.info("✓ LLM client initialized (provider: %s)", llm_client.provider)
    except ValueError as e:
        sample_keys = [envs[0] for envs in API_KEY_ENV_VARS.values() if envs]
        sample_text = ", ".join(sample_keys[:5])
        logger.error("LLM client initialization failed: %s", e)
        logger.error(
            "Set the API key env var for your provider (e.g., %s) or add it to the TOML config.",
            sample_text,
        )
        logger.error("See the README for the full list of supported provider keys.")
        sys.exit(1)

    # Initialize processor
    processor = CSVProcessor(llm_client)

    try:
        if args.preview:
            preview_rows = resolve_preview_rows()
            # Preview mode
            processor.preview_sample(
                input_path=input_path,
                columns=columns,
                prompt=prompt,
                num_rows=preview_rows,
                response_model=response_model,
            )
        else:
            # Full processing mode (always overwrite input file unless backup is handled externally)
            processor.process_csv(
                input_path=input_path,
                output_path=input_path,
                columns=columns,
                prompt=prompt,
                response_model=response_model,
                resume=not args.force,
            )

    except FileNotFoundError as e:
        print(f"Error: {e}")
        sys.exit(1)
    except ValueError as e:
        print(f"Error: {e}")
        sys.exit(1)
    except KeyboardInterrupt:
        print("\n\nProcessing interrupted by user")
        sys.exit(1)
    except Exception as e:
        print(f"Unexpected error: {e}")
        import traceback

        traceback.print_exc()
        sys.exit(1)


if __name__ == "__main__":
    main()
