#!/usr/bin/env python3
"""
Install all SmartTools example tools.

Usage:
    python3 install.py              # Install all tools
    python3 install.py --list       # List available tools
    python3 install.py summarize    # Install specific tool
    python3 install.py --category dev  # Install category

Run from anywhere:
    curl -sSL https://gitea.brrd.tech/rob/smarttools/raw/branch/main/examples/install.py | python3
"""

import argparse
import sys
from pathlib import Path

try:
    import yaml
except ImportError:
    print("PyYAML required. Install with: pip install pyyaml")
    sys.exit(1)

TOOLS_DIR = Path.home() / ".smarttools"

# All example tool configurations
TOOLS = {
    # TEXT PROCESSING
    "summarize": {
        "category": "text",
        "description": "Condense long documents to key points",
        "arguments": [{"flag": "--length", "variable": "length", "default": "3-5 bullet points"}],
        "steps": [{"type": "prompt", "prompt": "Summarize the following text into {length}. Be concise and capture the key points:\n\n{input}", "provider": "opencode-pickle", "output_var": "response"}],
        "output": "{response}"
    },
    "translate": {
        "category": "text",
        "description": "Translate text to any language",
        "arguments": [{"flag": "--lang", "variable": "lang", "default": "Spanish"}],
        "steps": [{"type": "prompt", "prompt": "Translate the following text to {lang}. Only output the translation, nothing else:\n\n{input}", "provider": "claude-haiku", "output_var": "response"}],
        "output": "{response}"
    },
    "fix-grammar": {
        "category": "text",
        "description": "Fix grammar and spelling errors",
        "arguments": [],
        "steps": [{"type": "prompt", "prompt": "Fix all grammar, spelling, and punctuation errors in the following text. Only output the corrected text, no explanations:\n\n{input}", "provider": "opencode-deepseek", "output_var": "response"}],
        "output": "{response}"
    },
    "simplify": {
        "category": "text",
        "description": "Rewrite text for easier understanding",
        "arguments": [{"flag": "--level", "variable": "level", "default": "5th grade reading level"}],
        "steps": [{"type": "prompt", "prompt": "Rewrite the following text for a {level}. Keep the meaning but use simpler words and shorter sentences:\n\n{input}", "provider": "opencode-pickle", "output_var": "response"}],
        "output": "{response}"
    },
    "tone-shift": {
        "category": "text",
        "description": "Change the tone of text",
        "arguments": [{"flag": "--tone", "variable": "tone", "default": "professional"}],
        "steps": [{"type": "prompt", "prompt": "Rewrite the following text in a {tone} tone. Keep the core message but adjust the style:\n\n{input}", "provider": "opencode-deepseek", "output_var": "response"}],
        "output": "{response}"
    },
    "eli5": {
        "category": "text",
        "description": "Explain like I'm 5",
        "arguments": [],
        "steps": [{"type": "prompt", "prompt": "Explain this like I'm 5 years old. Use simple words and fun analogies:\n\n{input}", "provider": "opencode-pickle", "output_var": "response"}],
        "output": "{response}"
    },
    "tldr": {
        "category": "text",
        "description": "One-line summary",
        "arguments": [],
        "steps": [{"type": "prompt", "prompt": "Give a one-line TL;DR summary of this text:\n\n{input}", "provider": "opencode-grok", "output_var": "response"}],
        "output": "{response}"
    },
    "expand": {
        "category": "text",
        "description": "Expand bullet points to paragraphs",
        "arguments": [],
        "steps": [{"type": "prompt", "prompt": "Expand these bullet points into well-written paragraphs:\n\n{input}", "provider": "opencode-pickle", "output_var": "response"}],
        "output": "{response}"
    },

    # DEVELOPER TOOLS
    "explain-error": {
        "category": "dev",
        "description": "Explain error messages and stack traces",
        "arguments": [],
        "steps": [{"type": "prompt", "prompt": "Explain this error/stack trace in plain English. What went wrong and how to fix it:\n\n{input}", "provider": "claude-haiku", "output_var": "response"}],
        "output": "{response}"
    },
    "explain-code": {
        "category": "dev",
        "description": "Explain what code does",
        "arguments": [{"flag": "--detail", "variable": "detail", "default": "moderate"}],
        "steps": [{"type": "prompt", "prompt": "Explain what this code does at a {detail} level of detail:\n\n```\n{input}\n```", "provider": "opencode-pickle", "output_var": "response"}],
        "output": "{response}"
    },
    "review-code": {
        "category": "dev",
        "description": "Quick code review with suggestions",
        "arguments": [{"flag": "--focus", "variable": "focus", "default": "bugs, security, and improvements"}],
        "steps": [{"type": "prompt", "prompt": "Review this code focusing on {focus}. Be concise and actionable:\n\n```\n{input}\n```", "provider": "claude-sonnet", "output_var": "response"}],
        "output": "{response}"
    },
    "gen-tests": {
        "category": "dev",
        "description": "Generate unit tests for code",
        "arguments": [{"flag": "--framework", "variable": "framework", "default": "pytest"}],
        "steps": [{"type": "prompt", "prompt": "Generate comprehensive unit tests for this code using {framework}. Include edge cases:\n\n```\n{input}\n```", "provider": "claude-haiku", "output_var": "response"}],
        "output": "{response}"
    },
    "docstring": {
        "category": "dev",
        "description": "Add docstrings to functions/classes",
        "arguments": [{"flag": "--style", "variable": "style", "default": "Google style"}],
        "steps": [{"type": "prompt", "prompt": "Add {style} docstrings to all functions and classes in this code. Output the complete code with docstrings:\n\n```\n{input}\n```", "provider": "opencode-deepseek", "output_var": "response"}],
        "output": "{response}"
    },
    "commit-msg": {
        "category": "dev",
        "description": "Generate commit message from diff",
        "arguments": [{"flag": "--style", "variable": "style", "default": "conventional commits"}],
        "steps": [{"type": "prompt", "prompt": "Generate a concise {style} commit message for this diff. Just the message, no explanation:\n\n{input}", "provider": "opencode-pickle", "output_var": "response"}],
        "output": "{response}"
    },

    # DATA TOOLS
    "json-extract": {
        "category": "data",
        "description": "Extract structured data as validated JSON",
        "arguments": [{"flag": "--fields", "variable": "fields", "default": "any relevant fields"}],
        "steps": [
            {"type": "prompt", "prompt": "Extract {fields} from this text as a JSON object. Output ONLY valid JSON, no markdown, no explanation:\n\n{input}", "provider": "opencode-deepseek", "output_var": "raw_json"},
            {"type": "code", "code": "import json\nimport re\ntext = raw_json.strip()\ntext = re.sub(r'^```json?\\s*', '', text)\ntext = re.sub(r'\\s*```$', '', text)\ntry:\n    parsed = json.loads(text)\n    validated = json.dumps(parsed, indent=2)\nexcept json.JSONDecodeError as e:\n    validated = f\"ERROR: Invalid JSON - {e}\\nRaw output: {text[:500]}\"", "output_var": "validated"}
        ],
        "output": "{validated}"
    },
    "json2csv": {
        "category": "data",
        "description": "Convert JSON to CSV format",
        "arguments": [],
        "steps": [{"type": "prompt", "prompt": "Convert this JSON to CSV format. Output only the CSV, no explanation:\n\n{input}", "provider": "opencode-deepseek", "output_var": "response"}],
        "output": "{response}"
    },
    "extract-emails": {
        "category": "data",
        "description": "Extract email addresses from text",
        "arguments": [],
        "steps": [{"type": "prompt", "prompt": "Extract all email addresses from this text. Output one email per line, nothing else:\n\n{input}", "provider": "opencode-pickle", "output_var": "response"}],
        "output": "{response}"
    },
    "extract-contacts": {
        "category": "data",
        "description": "Extract contact info as structured CSV",
        "arguments": [],
        "steps": [
            {"type": "prompt", "prompt": "Extract all contact information (name, email, phone, company) from this text. Output as JSON array with objects having keys: name, email, phone, company. Use null for missing fields. Output ONLY the JSON array:\n\n{input}", "provider": "opencode-pickle", "output_var": "contacts_json"},
            {"type": "code", "code": "import json\nimport re\ntext = contacts_json.strip()\ntext = re.sub(r'^```json?\\s*', '', text)\ntext = re.sub(r'\\s*```$', '', text)\ntry:\n    contacts = json.loads(text)\n    lines = ['name,email,phone,company']\n    for c in contacts:\n        row = [str(c.get('name') or ''), str(c.get('email') or ''), str(c.get('phone') or ''), str(c.get('company') or '')]\n        lines.append(','.join(f'\"{f}\"' for f in row))\n    csv_output = '\\n'.join(lines)\nexcept:\n    csv_output = f\"Error parsing contacts: {text[:200]}\"", "output_var": "csv_output"}
        ],
        "output": "{csv_output}"
    },
    "sql-from-text": {
        "category": "data",
        "description": "Generate SQL from natural language",
        "arguments": [{"flag": "--dialect", "variable": "dialect", "default": "PostgreSQL"}],
        "steps": [{"type": "prompt", "prompt": "Generate a {dialect} SQL query for this request. Output only the SQL, no explanation:\n\n{input}", "provider": "claude-haiku", "output_var": "response"}],
        "output": "{response}"
    },
    "safe-sql": {
        "category": "data",
        "description": "Generate SQL with safety checks",
        "arguments": [{"flag": "--dialect", "variable": "dialect", "default": "PostgreSQL"}],
        "steps": [
            {"type": "prompt", "prompt": "Generate a {dialect} SELECT query for: {input}\n\nOutput ONLY the SQL query, no explanation.", "provider": "claude-haiku", "output_var": "raw_sql"},
            {"type": "code", "code": "import re\nsql = raw_sql.strip()\nsql = re.sub(r'^```sql?\\s*', '', sql)\nsql = re.sub(r'\\s*```$', '', sql)\ndangerous = ['DROP', 'DELETE', 'TRUNCATE', 'UPDATE', 'INSERT', 'ALTER', 'CREATE', 'GRANT']\nwarnings = []\nfor keyword in dangerous:\n    if re.search(r'\\b' + keyword + r'\\b', sql, re.I):\n        warnings.append(f\"WARNING: Contains {keyword}\")\nif warnings:\n    validated = '\\n'.join(warnings) + '\\n\\n' + sql\nelse:\n    validated = sql", "output_var": "validated"}
        ],
        "output": "{validated}"
    },
    "parse-log": {
        "category": "data",
        "description": "Summarize and analyze log files",
        "arguments": [{"flag": "--focus", "variable": "focus", "default": "errors and warnings"}],
        "steps": [{"type": "prompt", "prompt": "Analyze these logs focusing on {focus}. Summarize key issues and patterns:\n\n{input}", "provider": "claude-haiku", "output_var": "response"}],
        "output": "{response}"
    },
    "csv-insights": {
        "category": "data",
        "description": "Analyze CSV with sampled data for large files",
        "arguments": [{"flag": "--question", "variable": "question", "default": "What patterns and insights can you find?"}],
        "steps": [
            {"type": "code", "code": "import random\nlines = input.strip().split('\\n')\nheader = lines[0] if lines else ''\ndata_lines = lines[1:] if len(lines) > 1 else []\nif len(data_lines) > 50:\n    sampled = random.sample(data_lines, 50)\n    sample_note = f\"(Sampled 50 of {len(data_lines)} rows)\"\nelse:\n    sampled = data_lines\n    sample_note = f\"({len(data_lines)} rows)\"\nsampled_csv = header + '\\n' + '\\n'.join(sampled)\nstats = f\"Columns: {len(header.split(','))}, Rows: {len(data_lines)} {sample_note}\"", "output_var": "sampled_csv,stats"},
            {"type": "prompt", "prompt": "Analyze this CSV data. {stats}\n\nQuestion: {question}\n\nData:\n{sampled_csv}", "provider": "claude-haiku", "output_var": "response"}
        ],
        "output": "{response}"
    },

    # ADVANCED TOOLS
    "log-errors": {
        "category": "advanced",
        "description": "Extract and explain errors from large log files",
        "arguments": [],
        "steps": [
            {"type": "code", "code": "import re\nlines = input.split('\\n')\nerrors = [l for l in lines if re.search(r'\\b(ERROR|CRITICAL|FATAL|Exception|Traceback)\\b', l, re.I)]\nresult = []\nfor i, line in enumerate(lines):\n    if re.search(r'\\b(ERROR|CRITICAL|FATAL|Exception|Traceback)\\b', line, re.I):\n        result.extend(lines[i:i+5])\nextracted = '\\n'.join(result[:200])", "output_var": "extracted"},
            {"type": "prompt", "prompt": "Analyze these error log entries. Group by error type, explain likely causes, and suggest fixes:\n\n{extracted}", "provider": "claude-haiku", "output_var": "response"}
        ],
        "output": "{response}"
    },
    "diff-focus": {
        "category": "advanced",
        "description": "Review only the added/changed code in a diff",
        "arguments": [],
        "steps": [
            {"type": "code", "code": "lines = input.split('\\n')\nresult = []\nfor i, line in enumerate(lines):\n    if line.startswith('@@') or line.startswith('+++') or line.startswith('---'):\n        result.append(line)\n    elif line.startswith('+') and not line.startswith('+++'):\n        result.append(line)\nextracted = '\\n'.join(result)", "output_var": "extracted"},
            {"type": "prompt", "prompt": "Review these added lines of code. Focus on bugs, security issues, and improvements:\n\n{extracted}", "provider": "claude-haiku", "output_var": "response"}
        ],
        "output": "{response}"
    },
    "changelog": {
        "category": "advanced",
        "description": "Generate changelog from git commits",
        "arguments": [{"flag": "--since", "variable": "since", "default": "last week"}],
        "steps": [
            {"type": "code", "code": "lines = input.strip().split('\\n')\ncommits = []\nfor line in lines:\n    if line.strip() and not line.startswith('commit '):\n        commits.append(line.strip())\ngit_log = '\\n'.join(commits[:100])", "output_var": "git_log"},
            {"type": "prompt", "prompt": "Generate a user-friendly changelog from these git commits. Group by category (Features, Fixes, Improvements, etc). Use markdown with bullet points:\n\n{git_log}", "provider": "opencode-pickle", "output_var": "changelog_raw"},
            {"type": "code", "code": "from datetime import datetime\nheader = f\"\"\"# Changelog\\n\\nGenerated: {datetime.now().strftime('%Y-%m-%d')}\\n\\n\"\"\"\nformatted = header + changelog_raw", "output_var": "formatted"}
        ],
        "output": "{formatted}"
    },
    "code-validate": {
        "category": "advanced",
        "description": "Generate and validate Python code",
        "arguments": [{"flag": "--task", "variable": "task", "default": ""}],
        "steps": [
            {"type": "prompt", "prompt": "Write Python code to: {task}\n\nContext/input data:\n{input}\n\nOutput ONLY the Python code, no markdown, no explanation.", "provider": "claude-haiku", "output_var": "raw_code"},
            {"type": "code", "code": "import ast\nimport re\ncode = raw_code.strip()\ncode = re.sub(r'^```python?\\s*', '', code)\ncode = re.sub(r'\\s*```$', '', code)\ntry:\n    ast.parse(code)\n    validated = code\nexcept SyntaxError as e:\n    validated = f\"# SYNTAX ERROR at line {e.lineno}: {e.msg}\\n# Fix needed:\\n\\n{code}\"", "output_var": "validated"}
        ],
        "output": "{validated}"
    },
    "reply-email": {
        "category": "text",
        "description": "Draft email replies",
        "arguments": [{"flag": "--tone", "variable": "tone", "default": "professional and friendly"}],
        "steps": [{"type": "prompt", "prompt": "Draft a {tone} reply to this email:\n\n{input}", "provider": "opencode-deepseek", "output_var": "response"}],
        "output": "{response}"
    },
}

CATEGORIES = {
    "text": "Text Processing",
    "dev": "Developer Tools",
    "data": "Data Tools",
    "advanced": "Advanced Multi-Step Tools",
}


def install_tool(name: str, config: dict, force: bool = False) -> bool:
    """Install a single tool."""
    tool_dir = TOOLS_DIR / name
    config_file = tool_dir / "config.yaml"

    if config_file.exists() and not force:
        print(f"  {name}: already exists (use --force to overwrite)")
        return False

    tool_dir.mkdir(parents=True, exist_ok=True)

    # Build config without category
    tool_config = {
        "name": name,
        "description": config["description"],
        "arguments": config.get("arguments", []),
        "steps": config["steps"],
        "output": config["output"]
    }

    with open(config_file, 'w') as f:
        yaml.dump(tool_config, f, default_flow_style=False, sort_keys=False)

    print(f"  {name}: installed")
    return True


def list_tools():
    """List all available tools."""
    print("Available example tools:\n")

    for cat_id, cat_name in CATEGORIES.items():
        tools_in_cat = [(n, c) for n, c in TOOLS.items() if c.get("category") == cat_id]
        if tools_in_cat:
            print(f"{cat_name}:")
            for name, config in tools_in_cat:
                print(f"  {name:<20} {config['description']}")
            print()


def main():
    parser = argparse.ArgumentParser(description="Install SmartTools example tools")
    parser.add_argument("tools", nargs="*", help="Specific tools to install (default: all)")
    parser.add_argument("--list", action="store_true", help="List available tools")
    parser.add_argument("--category", "-c", choices=list(CATEGORIES.keys()), help="Install tools from category")
    parser.add_argument("--force", "-f", action="store_true", help="Overwrite existing tools")

    args = parser.parse_args()

    if args.list:
        list_tools()
        return

    # Determine which tools to install
    if args.tools:
        to_install = {n: c for n, c in TOOLS.items() if n in args.tools}
        unknown = set(args.tools) - set(TOOLS.keys())
        if unknown:
            print(f"Unknown tools: {', '.join(unknown)}")
            print("Use --list to see available tools")
            return
    elif args.category:
        to_install = {n: c for n, c in TOOLS.items() if c.get("category") == args.category}
    else:
        to_install = TOOLS

    if not to_install:
        print("No tools to install")
        return

    print(f"Installing {len(to_install)} tools to {TOOLS_DIR}...\n")

    installed = 0
    for name, config in to_install.items():
        if install_tool(name, config, args.force):
            installed += 1

    print(f"\nInstalled {installed} tools.")
    print("\nRun 'smarttools refresh' to create executable wrappers.")


if __name__ == "__main__":
    main()
