"""CLI entry point for rcl-rlm."""

import argparse
import json
import os
import sys
from pathlib import Path

from .execution_context import (
    DEFAULT_MAX_CALLS,
    DEFAULT_MAX_DEPTH,
    DEFAULT_MAX_OUTPUT_CHARS,
)
from .fingerprint import build_request_fingerprint, log_fingerprint
from .modelrelay import ModelResolutionPolicy, resolve_root_model, resolve_subcall_model
from rlm_core import PolicyHints
from .rlm import run_rlm

# Data source types
SOURCE_MESSAGES = "messages"
SOURCE_GMAIL = "gmail"


def get_default_db_path() -> str:
    """Get the default shadow.db path."""
    # Check environment variable first
    if env_path := os.environ.get("SHADOW_DB_PATH"):
        return env_path

    # Default location
    return str(Path.home() / "Library/Application Support/RecallRLM/shadow.db")


def get_default_contacts_db_path(shadow_db_path: str) -> str:
    """Get the default contacts.db path."""
    if env_path := os.environ.get("CONTACTS_DB_PATH"):
        return env_path

    shadow_path = Path(shadow_db_path)
    return str(shadow_path.with_name("contacts.db"))


def main():
    parser = argparse.ArgumentParser(
        description="Query your messages using RLM (Recursive Language Model)",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
Examples:
  # Query iMessage (default)
  rcl-rlm "What projects did we discuss last month?"
  rcl-rlm "Find messages about dinner plans" --verbose

  # Query Gmail
  rcl-rlm "What emails did I get from GitHub today?" --source gmail
  rcl-rlm "Summarize my unread emails" --source gmail --verbose

Environment variables:
  MODELRELAY_API_KEY    - Required. Your ModelRelay API key.
  SHADOW_DB_PATH        - Optional. Path to shadow.db (default: ~/Library/Application Support/RecallRLM/shadow.db)
  CONTACTS_DB_PATH      - Optional. Path to contacts.db (default: alongside shadow.db)
  RECALL_CLOUD_URL      - Optional. Override cloud backend URL (default: https://recall-ai.fly.dev)
  RECALL_CLOUD_SESSION  - Optional. Pre-configured cloud session token (for CI/scripts)
  RECALL_RLM_ROOT_MODEL    - Optional. Override root model (gemini-3-flash-preview)
  RECALL_RLM_SUBCALL_MODEL - Optional. Override subcall model (defaults to root)
  RECALL_RLM_MODEL         - Optional. Override default model (gemini-3-flash-preview)

Model resolution precedence (CLI):
  Root: --root-model > RECALL_RLM_ROOT_MODEL > RECALL_RLM_MODEL/MODELRELAY_MODEL > default
  Subcall: --subcall-model > RECALL_RLM_SUBCALL_MODEL > --root-model > RECALL_RLM_ROOT_MODEL > RECALL_RLM_MODEL/MODELRELAY_MODEL > default
  See tools/rcl-rlm/README.md (Model resolution precedence).
""",
    )

    parser.add_argument(
        "question",
        help="The question to ask about your message history",
    )

    parser.add_argument(
        "--source",
        choices=[SOURCE_MESSAGES, SOURCE_GMAIL],
        default=SOURCE_MESSAGES,
        help="Data source: messages (local iMessage) or gmail (cloud)",
    )

    parser.add_argument(
        "--account",
        dest="account_id",
        help="Specific account ID for cloud sources (for multi-account setups)",
    )

    parser.add_argument(
        "--db",
        dest="db_path",
        default=get_default_db_path(),
        help="Path to shadow.db SQLite database",
    )

    parser.add_argument(
        "--contacts-db",
        dest="contacts_db_path",
        help="Path to contacts.db SQLite database (defaults to CONTACTS_DB_PATH or alongside --db)",
    )

    parser.add_argument(
        "--max-depth",
        type=int,
        default=DEFAULT_MAX_DEPTH,
        help=f"Maximum recursion depth for llm_query() sub-calls (default: {DEFAULT_MAX_DEPTH})",
    )

    parser.add_argument(
        "--max-calls",
        type=int,
        default=DEFAULT_MAX_CALLS,
        help=f"Maximum number of llm_query() sub-calls (default: {DEFAULT_MAX_CALLS})",
    )

    parser.add_argument(
        "--max-output-chars",
        type=int,
        default=DEFAULT_MAX_OUTPUT_CHARS,
        help=f"Maximum sandbox stdout output size in characters (default: {DEFAULT_MAX_OUTPUT_CHARS})",
    )

    parser.add_argument(
        "--context",
        dest="conversation_context",
        help="Optional conversation context to provide for follow-up questions",
    )

    parser.add_argument(
        "--root-model",
        dest="root_model",
        help="Model to use for the root RLM call (defaults to RECALL_RLM_ROOT_MODEL or RECALL_RLM_MODEL)",
    )

    parser.add_argument(
        "--subcall-model",
        dest="subcall_model",
        help="Model to use for RLM sub-calls (defaults to root model)",
    )

    parser.add_argument(
        "-v", "--verbose",
        action="store_true",
        help="Show detailed execution trace",
    )

    parser.add_argument(
        "--show-log",
        action="store_true",
        help="Show the full execution log after the answer",
    )

    parser.add_argument(
        "--benchmark",
        action="store_true",
        help="Output JSON with retrieved GUIDs for benchmark comparison",
    )

    parser.add_argument(
        "--tips-profile",
        dest="tips_profile",
        choices=["full", "minimal", "bench", "none"],
        default=None,
        help="Tips profile for system prompt (default: RECALL_RLM_TIPS_PROFILE or 'full')",
    )
    parser.add_argument(
        "--policy-hints",
        dest="policy_hints",
        help="Comma-separated policy hints (semantic,count,numeric). "
             "If unset, no contract enforcement hints are applied.",
    )

    args = parser.parse_args()

    # Check for API key (required for all sources)
    if not os.environ.get("MODELRELAY_API_KEY"):
        print("Error: MODELRELAY_API_KEY environment variable not set", file=sys.stderr)
        sys.exit(1)

    # Route based on data source
    if args.source == SOURCE_GMAIL:
        _run_gmail_query(args)
    else:
        _run_messages_query(args)


def _run_gmail_query(args: argparse.Namespace) -> None:
    """Run a Gmail RLM query."""
    from .cloud_auth import CloudAuthError
    from .gmail_rlm import query_gmail
    from .modelrelay import ModelRelayError

    policy = ModelResolutionPolicy(allow_env=True)
    resolved_model = resolve_root_model(args.root_model, policy=policy)

    if not args.benchmark:
        print(f"Searching Gmail for: {args.question}\n", file=sys.stderr)

    try:
        result = query_gmail(
            question=args.question,
            model=resolved_model,
            account_id=args.account_id,
            verbose=args.verbose and not args.benchmark,
        )
    except CloudAuthError as exc:
        print(f"Error: {exc}", file=sys.stderr)
        sys.exit(1)
    except ModelRelayError as exc:
        print(f"Error: ModelRelay API error: {exc}", file=sys.stderr)
        sys.exit(1)

    # Benchmark mode: output JSON
    if args.benchmark:
        output = {
            "answer": result.answer,
            "citations": result.citations,
            "iterations": result.iterations,
            "total_tokens": result.total_tokens,
            "error": result.error,
        }
        print(json.dumps(output))
        return

    # Check for errors
    if result.error:
        print(f"Error: {result.error}", file=sys.stderr)
        sys.exit(1)

    # Print the answer
    print(result.answer)

    # Print citations if verbose
    if args.verbose and result.citations:
        print(f"\n{'='*60}", file=sys.stderr)
        print(f"Citations ({len(result.citations)} emails referenced)", file=sys.stderr)
        print(f"{'='*60}", file=sys.stderr)
        for citation in result.citations[:5]:  # Show first 5
            subject = citation.get("subject", "No subject")
            from_addr = citation.get("from", "Unknown")
            date = citation.get("date", "Unknown date")
            print(f"  - {subject} (from: {from_addr}, {date})", file=sys.stderr)
        if len(result.citations) > 5:
            print(f"  ... and {len(result.citations) - 5} more", file=sys.stderr)


def _run_messages_query(args: argparse.Namespace) -> None:
    """Run a local iMessage RLM query."""
    if not args.contacts_db_path:
        args.contacts_db_path = get_default_contacts_db_path(args.db_path)

    # Validate database exists (only for messages source)
    if not os.path.exists(args.db_path):
        print(f"Error: Database not found at {args.db_path}", file=sys.stderr)
        print(f"Set SHADOW_DB_PATH or use --db to specify the path.", file=sys.stderr)
        sys.exit(1)

    policy_hints_value = args.policy_hints or os.environ.get("RECALL_RLM_POLICY_HINTS")
    policy_hints = None
    if policy_hints_value:
        tokens = [token.strip() for token in policy_hints_value.split(",") if token.strip()]
        policy_hints = PolicyHints.from_tokens(tokens)

    policy = ModelResolutionPolicy(allow_env=True)
    resolved_root_model = resolve_root_model(args.root_model, policy=policy)
    resolved_subcall_model = resolve_subcall_model(
        args.subcall_model,
        root_fallback=args.root_model,
        policy=policy,
    )
    fingerprint = build_request_fingerprint(
        question=args.question,
        db_path=args.db_path,
        contacts_db_path=args.contacts_db_path,
        conversation_context=args.conversation_context,
        max_depth=args.max_depth,
        max_calls=args.max_calls,
        max_output_chars=max(1, args.max_output_chars),
        root_model=resolved_root_model,
        subcall_model=resolved_subcall_model,
    )
    log_fingerprint(fingerprint)

    # Run true RLM - single generation with recursive sub-calls
    if not args.benchmark:
        print(f"Searching messages for: {args.question}\n", file=sys.stderr)

    result = run_rlm(
        question=args.question,
        db_path=args.db_path,
        contacts_db_path=args.contacts_db_path,
        conversation_context=args.conversation_context,
        max_depth=args.max_depth,
        max_calls=args.max_calls,
        max_output_chars=max(1, args.max_output_chars),
        verbose=args.verbose and not args.benchmark,
        root_model=resolved_root_model,
        subcall_model=resolved_subcall_model,
        tips_profile=args.tips_profile,
        policy_hints=policy_hints,
    )

    # Benchmark mode: output JSON with retrieved GUIDs
    if args.benchmark:
        output = {
            "answer": result.answer,
            "retrieved_guids": result.retrieved_guids,
            "iterations": result.iterations,
            "sub_calls_made": result.sub_calls_made,
            "total_tokens": result.total_tokens,
        }
        print(json.dumps(output))
        return

    # Print the answer
    print(result.answer)

    # Print execution log if requested
    if args.show_log:
        print(f"\n{'='*60}", file=sys.stderr)
        print(f"Execution Log ({result.iterations} iterations, {result.sub_calls_made} sub-calls)", file=sys.stderr)
        print(f"{'='*60}", file=sys.stderr)
        for index, entry in enumerate(result.execution_log, start=1):
            print(f"\n--- Iteration {index} ---", file=sys.stderr)
            print(f"Code:\n{entry.get('code', '')}", file=sys.stderr)
            print(f"\nOutput:\n{entry.get('output', '')[:500]}", file=sys.stderr)


if __name__ == "__main__":
    main()
