#!/usr/bin/env python3
"""
API Key Manager TUI - Interactive Terminal Interface
Beautiful and intuitive key management interface
"""

import os
import sys
import json
from pathlib import Path
from typing import Dict, List, Optional, Tuple
from datetime import datetime
import argparse
from collections import defaultdict
import subprocess
import shutil
from getpass import getpass

# Rich TUI components
from rich.console import Console
from rich.table import Table
from rich.panel import Panel
from rich.layout import Layout
from rich.live import Live
from rich.prompt import Prompt, Confirm, IntPrompt
from rich.text import Text
from rich.style import Style
from rich.columns import Columns
from rich.syntax import Syntax
from rich.tree import Tree
from rich import box
from rich.align import Align
from rich.padding import Padding

console = Console()

class KeyManagerTUI:
    """Interactive TUI for API Key Management"""
    
    def __init__(self):
        self.workspace_root = Path(__file__).parent.parent.parent.parent
        self.keys_file = self.workspace_root / "config" / "consolidated_keys.json"
        self.keys_file.parent.mkdir(parents=True, exist_ok=True)
        self.current_view = "main"
        self.selected_category = None
        self.keys_data = self.load_keys()
        
    def load_keys(self) -> Dict:
        """Load consolidated keys from JSON"""
        if self.keys_file.exists():
            with open(self.keys_file, 'r') as f:
                return json.load(f)
        return {"keys": {}, "consolidated_at": None, "total_keys": 0}
    
    def save_keys(self):
        """Save keys back to JSON"""
        self.keys_data["consolidated_at"] = datetime.now().isoformat()
        self.keys_data["total_keys"] = sum(len(cat) for cat in self.keys_data.get("keys", {}).values())
        with open(self.keys_file, 'w') as f:
            json.dump(self.keys_data, f, indent=2)
    
    def clear_screen(self):
        """Clear the terminal screen"""
        os.system('clear' if os.name == 'posix' else 'cls')
    
    def display_header(self):
        """Display the application header"""
        header = Panel(
            Align.center(
                Text("🔐 API Key Manager", style="bold cyan", justify="center")
            ),
            box=box.DOUBLE,
            style="cyan",
            padding=(1, 2)
        )
        console.print(header)
        
        # Display status bar
        if self.keys_data.get("consolidated_at"):
            last_update = self.keys_data["consolidated_at"][:19].replace("T", " ")
            status = f"📊 Total Keys: {self.keys_data.get('total_keys', 0)} | 📅 Last Update: {last_update}"
        else:
            status = "⚠️  No keys consolidated yet"
        
        console.print(Panel(status, style="dim", box=box.MINIMAL))
    
    def display_main_menu(self):
        """Display the main menu"""
        menu_items = [
            ("1", "🔍 View Keys", "Browse keys by category"),
            ("2", "➕ Add Key", "Add a new API key"),
            ("3", "✏️  Edit Key", "Modify an existing key"),
            ("4", "🗑️  Delete Key", "Remove a key"),
            ("5", "🔄 Scan Projects", "Scan all projects for .env files"),
            ("6", "📤 Export Keys", "Export to .env format"),
            ("7", "📊 Statistics", "View key statistics"),
            ("8", "🔍 Search", "Search for a specific key"),
            ("9", "⚙️  Settings", "Configure settings"),
            ("0", "🚪 Exit", "Exit the application")
        ]
        
        table = Table(
            title="Main Menu",
            box=box.ROUNDED,
            show_header=False,
            padding=(0, 2),
            style="cyan"
        )
        table.add_column("Option", style="bold yellow", width=8)
        table.add_column("Action", style="bold white", width=20)
        table.add_column("Description", style="dim")
        
        for option, action, desc in menu_items:
            table.add_row(option, action, desc)
        
        console.print(Padding(table, (1, 0)))
    
    def view_keys(self):
        """View keys organized by category"""
        self.clear_screen()
        self.display_header()
        
        if not self.keys_data.get("keys"):
            console.print(Panel("⚠️  No keys found. Use option 5 to scan projects.", style="yellow"))
            Prompt.ask("\nPress Enter to continue")
            return
        
        # Create a tree view
        tree = Tree("📁 API Keys", style="bold cyan")
        
        categories = self.keys_data.get("keys", {})
        for category, keys in categories.items():
            branch = tree.add(f"📂 {category.upper()} ({len(keys)} keys)", style="yellow")
            
            # Show first 5 keys in each category
            for i, (key_name, value) in enumerate(sorted(keys.items())[:5]):
                masked_value = value[:8] + "..." if value and len(value) > 8 else value or "(empty)"
                branch.add(f"🔑 {key_name}: {masked_value}", style="dim white")
            
            if len(keys) > 5:
                branch.add(f"... and {len(keys) - 5} more", style="dim italic")
        
        console.print(Padding(tree, (1, 2)))
        
        # Category selection
        console.print("\n[bold]Select a category to view all keys:[/bold]")
        cat_list = list(categories.keys())
        for i, cat in enumerate(cat_list, 1):
            console.print(f"  {i}. {cat.upper()} ({len(categories[cat])} keys)")
        
        console.print("  0. Back to main menu")
        
        choice = Prompt.ask("\nYour choice", default="0")
        
        if choice.isdigit() and 0 < int(choice) <= len(cat_list):
            self.view_category_keys(cat_list[int(choice) - 1])
    
    def view_category_keys(self, category: str):
        """View all keys in a specific category"""
        self.clear_screen()
        self.display_header()
        
        keys = self.keys_data.get("keys", {}).get(category, {})
        
        console.print(Panel(f"Category: {category.upper()}", style="bold yellow"))
        
        table = Table(
            title=f"{len(keys)} Keys",
            box=box.SIMPLE,
            show_lines=True,
            style="cyan"
        )
        table.add_column("#", style="dim", width=4)
        table.add_column("Key Name", style="bold white", width=30)
        table.add_column("Value", style="green")
        
        for i, (key_name, value) in enumerate(sorted(keys.items()), 1):
            display_value = value[:50] + "..." if value and len(value) > 50 else value or "(empty)"
            table.add_row(str(i), key_name, display_value)
        
        console.print(table)
        
        Prompt.ask("\nPress Enter to continue")
    
    def add_key(self):
        """Add a new API key"""
        self.clear_screen()
        self.display_header()
        
        console.print(Panel("➕ Add New API Key", style="bold green"))
        
        # Get key details
        key_name = Prompt.ask("\n[bold]Key name[/bold]").strip().upper()
        
        if not key_name:
            console.print("[red]Invalid key name[/red]")
            Prompt.ask("\nPress Enter to continue")
            return
        
        # Check if key exists
        for category, keys in self.keys_data.get("keys", {}).items():
            if key_name in keys:
                console.print(f"[yellow]⚠️  Key '{key_name}' already exists in category '{category}'[/yellow]")
                if not Confirm.ask("Do you want to update it?"):
                    return
        
        # Get value (hidden input for security)
        console.print("[dim]Enter key value (input will be hidden):[/dim]")
        key_value = getpass("Value: ")
        
        # Select category
        categories = ["ai", "services", "database", "settings", "custom"]
        console.print("\n[bold]Select category:[/bold]")
        for i, cat in enumerate(categories, 1):
            console.print(f"  {i}. {cat}")
        
        cat_choice = IntPrompt.ask("Category", default=5)
        category = categories[cat_choice - 1] if 1 <= cat_choice <= len(categories) else "custom"
        
        # Add to data structure
        if "keys" not in self.keys_data:
            self.keys_data["keys"] = {}
        if category not in self.keys_data["keys"]:
            self.keys_data["keys"][category] = {}
        
        self.keys_data["keys"][category][key_name] = key_value
        self.save_keys()
        
        console.print(f"\n[green]✅ Key '{key_name}' added to category '{category}'[/green]")
        Prompt.ask("\nPress Enter to continue")
    
    def edit_key(self):
        """Edit an existing key"""
        self.clear_screen()
        self.display_header()
        
        console.print(Panel("✏️  Edit API Key", style="bold yellow"))
        
        # Search for key
        search = Prompt.ask("\n[bold]Enter key name to edit (partial match OK)[/bold]").strip().upper()
        
        matches = []
        for category, keys in self.keys_data.get("keys", {}).items():
            for key_name in keys:
                if search in key_name:
                    matches.append((category, key_name, keys[key_name]))
        
        if not matches:
            console.print(f"[red]No keys found matching '{search}'[/red]")
            Prompt.ask("\nPress Enter to continue")
            return
        
        if len(matches) == 1:
            category, key_name, old_value = matches[0]
        else:
            # Multiple matches, let user choose
            console.print(f"\n[bold]Found {len(matches)} matches:[/bold]")
            for i, (cat, name, val) in enumerate(matches, 1):
                masked = val[:8] + "..." if val and len(val) > 8 else val or "(empty)"
                console.print(f"  {i}. {name} ({cat}): {masked}")
            
            choice = IntPrompt.ask("Select key to edit", default=1)
            if 1 <= choice <= len(matches):
                category, key_name, old_value = matches[choice - 1]
            else:
                return
        
        # Show current value
        console.print(f"\n[bold]Editing: {key_name}[/bold]")
        console.print(f"Category: {category}")
        console.print(f"Current value: {old_value[:20]}..." if old_value and len(old_value) > 20 else f"Current value: {old_value or '(empty)'}")
        
        # Get new value
        console.print("\n[dim]Enter new value (input will be hidden, leave empty to keep current):[/dim]")
        new_value = getpass("New value: ")
        
        if new_value:
            self.keys_data["keys"][category][key_name] = new_value
            self.save_keys()
            console.print(f"\n[green]✅ Key '{key_name}' updated[/green]")
        else:
            console.print("\n[yellow]No changes made[/yellow]")
        
        Prompt.ask("\nPress Enter to continue")
    
    def delete_key(self):
        """Delete a key"""
        self.clear_screen()
        self.display_header()
        
        console.print(Panel("🗑️  Delete API Key", style="bold red"))
        
        # Search for key
        search = Prompt.ask("\n[bold]Enter key name to delete (partial match OK)[/bold]").strip().upper()
        
        matches = []
        for category, keys in self.keys_data.get("keys", {}).items():
            for key_name in keys:
                if search in key_name:
                    matches.append((category, key_name))
        
        if not matches:
            console.print(f"[red]No keys found matching '{search}'[/red]")
            Prompt.ask("\nPress Enter to continue")
            return
        
        if len(matches) == 1:
            category, key_name = matches[0]
        else:
            # Multiple matches
            console.print(f"\n[bold]Found {len(matches)} matches:[/bold]")
            for i, (cat, name) in enumerate(matches, 1):
                console.print(f"  {i}. {name} ({cat})")
            
            choice = IntPrompt.ask("Select key to delete", default=1)
            if 1 <= choice <= len(matches):
                category, key_name = matches[choice - 1]
            else:
                return
        
        # Confirm deletion
        if Confirm.ask(f"\n[bold red]Delete '{key_name}' from '{category}'?[/bold red]"):
            del self.keys_data["keys"][category][key_name]
            
            # Remove category if empty
            if not self.keys_data["keys"][category]:
                del self.keys_data["keys"][category]
            
            self.save_keys()
            console.print(f"\n[green]✅ Key '{key_name}' deleted[/green]")
        else:
            console.print("\n[yellow]Deletion cancelled[/yellow]")
        
        Prompt.ask("\nPress Enter to continue")
    
    def scan_projects(self):
        """Scan all projects for .env files"""
        self.clear_screen()
        self.display_header()
        
        console.print(Panel("🔄 Scanning Projects for API Keys", style="bold cyan"))
        
        projects = {
            "root": self.workspace_root,
            "trax": self.workspace_root / "apps" / "trax",
            "youtube-summarizer": self.workspace_root / "apps" / "youtube-summarizer",
            "pdf-translator": self.workspace_root / "pdf-translator",
            "directus-mcp": self.workspace_root / "tools" / "directus-mcp-server",
        }
        
        all_keys = defaultdict(dict)
        found_count = 0
        
        with console.status("[bold green]Scanning projects...") as status:
            for project_name, project_path in projects.items():
                env_file = project_path / ".env"
                if env_file.exists():
                    status.update(f"[bold green]Scanning {project_name}...")
                    
                    with open(env_file, 'r') as f:
                        for line in f:
                            line = line.strip()
                            if line and not line.startswith('#'):
                                if '=' in line:
                                    key, value = line.split('=', 1)
                                    key = key.strip()
                                    value = value.strip().strip('"').strip("'")
                                    all_keys[key][project_name] = value
                                    found_count += 1
                    
                    console.print(f"  ✅ {project_name}: Found keys")
                else:
                    console.print(f"  ⚠️  {project_name}: No .env file")
        
        # Consolidate keys
        console.print(f"\n[bold]Found {len(all_keys)} unique keys across {found_count} total entries[/bold]")
        
        if Confirm.ask("\nDo you want to consolidate these keys?"):
            # Organize by category
            categorized = {
                "ai": {},
                "services": {},
                "database": {},
                "settings": {},
                "custom": {}
            }
            
            ai_prefixes = ["ANTHROPIC", "DEEPSEEK", "OPENAI", "PERPLEXITY", "OPENROUTER", 
                           "GOOGLE_API", "XAI", "MISTRAL", "QWEN", "DASHSCOPE", "MODEL_STUDIO"]
            service_prefixes = ["SLACK", "GITHUB", "GITEA", "YOUTUBE", "DIRECTUS", "MICROSOFT"]
            db_prefixes = ["DATABASE", "REDIS", "POSTGRES"]
            
            for key_name, values in all_keys.items():
                # Pick the most common value or root
                if "root" in values:
                    final_value = values["root"]
                else:
                    final_value = max(values.values(), key=lambda x: list(values.values()).count(x))
                
                # Categorize
                if any(key_name.startswith(prefix) for prefix in ai_prefixes):
                    categorized["ai"][key_name] = final_value
                elif any(key_name.startswith(prefix) for prefix in service_prefixes):
                    categorized["services"][key_name] = final_value
                elif any(key_name.startswith(prefix) for prefix in db_prefixes):
                    categorized["database"][key_name] = final_value
                elif any(keyword in key_name for keyword in ["JWT", "SECRET", "TOKEN", "KEY"]):
                    categorized["settings"][key_name] = final_value
                else:
                    categorized["custom"][key_name] = final_value
            
            self.keys_data["keys"] = categorized
            self.save_keys()
            
            console.print("\n[green]✅ Keys consolidated successfully![/green]")
        
        Prompt.ask("\nPress Enter to continue")
    
    def export_keys(self):
        """Export keys to .env format"""
        self.clear_screen()
        self.display_header()
        
        console.print(Panel("📤 Export Keys", style="bold green"))
        
        if not self.keys_data.get("keys"):
            console.print("[red]No keys to export[/red]")
            Prompt.ask("\nPress Enter to continue")
            return
        
        # Choose export options
        console.print("\n[bold]Export Options:[/bold]")
        console.print("  1. All keys")
        console.print("  2. Specific category")
        console.print("  3. Specific project requirements")
        
        choice = IntPrompt.ask("\nYour choice", default=1)
        
        filter_category = None
        if choice == 2:
            categories = list(self.keys_data["keys"].keys())
            console.print("\n[bold]Select category:[/bold]")
            for i, cat in enumerate(categories, 1):
                console.print(f"  {i}. {cat}")
            
            cat_choice = IntPrompt.ask("Category", default=1)
            if 1 <= cat_choice <= len(categories):
                filter_category = categories[cat_choice - 1]
        
        # Get output file
        default_path = "exported_keys.env"
        output_path = Prompt.ask("\n[bold]Output file path[/bold]", default=default_path)
        
        # Export
        with open(output_path, 'w') as f:
            f.write(f"# Exported API Keys\n")
            f.write(f"# Generated: {datetime.now().isoformat()}\n\n")
            
            for category, keys in self.keys_data["keys"].items():
                if filter_category and category != filter_category:
                    continue
                
                f.write(f"# {category.upper()} KEYS\n")
                for key_name, value in sorted(keys.items()):
                    f.write(f"{key_name}={value}\n")
                f.write("\n")
        
        console.print(f"\n[green]✅ Exported to {output_path}[/green]")
        
        # Show preview
        if Confirm.ask("\nDo you want to preview the exported file?"):
            with open(output_path, 'r') as f:
                lines = f.readlines()[:20]
                syntax = Syntax("".join(lines), "bash", theme="monokai", line_numbers=True)
                console.print(syntax)
        
        Prompt.ask("\nPress Enter to continue")
    
    def show_statistics(self):
        """Show key statistics"""
        self.clear_screen()
        self.display_header()
        
        console.print(Panel("📊 Key Statistics", style="bold cyan"))
        
        if not self.keys_data.get("keys"):
            console.print("[red]No keys found[/red]")
            Prompt.ask("\nPress Enter to continue")
            return
        
        # Calculate statistics
        categories = self.keys_data["keys"]
        total_keys = sum(len(keys) for keys in categories.values())
        
        # Category breakdown
        table = Table(
            title="Keys by Category",
            box=box.SIMPLE_HEAD,
            show_lines=True,
            style="cyan"
        )
        table.add_column("Category", style="bold yellow", width=15)
        table.add_column("Count", style="white", justify="right", width=10)
        table.add_column("Percentage", style="green", justify="right", width=12)
        table.add_column("Status", style="white", width=20)
        
        for category, keys in sorted(categories.items()):
            count = len(keys)
            percentage = (count / total_keys * 100) if total_keys > 0 else 0
            
            # Status indicator
            if count == 0:
                status = "❌ Empty"
            elif count < 5:
                status = "⚠️  Few keys"
            else:
                status = "✅ Good"
            
            table.add_row(
                category.upper(),
                str(count),
                f"{percentage:.1f}%",
                status
            )
        
        table.add_section()
        table.add_row(
            "TOTAL",
            str(total_keys),
            "100.0%",
            "📊 All Keys",
            style="bold"
        )
        
        console.print(table)
        
        # Key analysis
        console.print("\n[bold]Key Analysis:[/bold]")
        
        # Find empty keys
        empty_keys = []
        for category, keys in categories.items():
            for key_name, value in keys.items():
                if not value:
                    empty_keys.append(f"{key_name} ({category})")
        
        if empty_keys:
            console.print(f"  ⚠️  Empty keys: {len(empty_keys)}")
            for key in empty_keys[:5]:
                console.print(f"     - {key}", style="yellow")
            if len(empty_keys) > 5:
                console.print(f"     ... and {len(empty_keys) - 5} more", style="dim")
        else:
            console.print("  ✅ No empty keys found", style="green")
        
        # Last update
        if self.keys_data.get("consolidated_at"):
            last_update = self.keys_data["consolidated_at"][:19].replace("T", " ")
            console.print(f"\n  📅 Last consolidation: {last_update}")
        
        Prompt.ask("\nPress Enter to continue")
    
    def search_keys(self):
        """Search for specific keys"""
        self.clear_screen()
        self.display_header()
        
        console.print(Panel("🔍 Search Keys", style="bold cyan"))
        
        search_term = Prompt.ask("\n[bold]Enter search term[/bold]").strip().upper()
        
        if not search_term:
            return
        
        matches = []
        for category, keys in self.keys_data.get("keys", {}).items():
            for key_name, value in keys.items():
                if search_term in key_name:
                    matches.append((category, key_name, value))
        
        if not matches:
            console.print(f"\n[red]No keys found matching '{search_term}'[/red]")
        else:
            console.print(f"\n[bold]Found {len(matches)} matches:[/bold]\n")
            
            table = Table(box=box.SIMPLE, show_lines=True)
            table.add_column("Category", style="yellow", width=12)
            table.add_column("Key Name", style="bold white", width=30)
            table.add_column("Value", style="green")
            
            for category, key_name, value in matches:
                display_value = value[:40] + "..." if value and len(value) > 40 else value or "(empty)"
                table.add_row(category, key_name, display_value)
            
            console.print(table)
        
        Prompt.ask("\nPress Enter to continue")
    
    def run(self):
        """Main TUI loop"""
        while True:
            self.clear_screen()
            self.display_header()
            self.display_main_menu()
            
            choice = Prompt.ask("\n[bold cyan]Select an option[/bold cyan]", default="0")
            
            if choice == "1":
                self.view_keys()
            elif choice == "2":
                self.add_key()
            elif choice == "3":
                self.edit_key()
            elif choice == "4":
                self.delete_key()
            elif choice == "5":
                self.scan_projects()
            elif choice == "6":
                self.export_keys()
            elif choice == "7":
                self.show_statistics()
            elif choice == "8":
                self.search_keys()
            elif choice == "9":
                console.print("\n[yellow]Settings not implemented yet[/yellow]")
                Prompt.ask("\nPress Enter to continue")
            elif choice == "0":
                if Confirm.ask("\n[bold red]Are you sure you want to exit?[/bold red]"):
                    self.clear_screen()
                    console.print("[bold green]Goodbye! 👋[/bold green]")
                    break

def main():
    """Entry point"""
    try:
        tui = KeyManagerTUI()
        tui.run()
    except KeyboardInterrupt:
        console.print("\n\n[yellow]Interrupted by user[/yellow]")
    except Exception as e:
        console.print(f"\n[red]Error: {e}[/red]")

if __name__ == "__main__":
    main()