"""Configuration validation and diagnostics for Proxym.

Validates .env configuration, API keys, and LLM backend connectivity.
Provides interactive prompts for fixing configuration issues.
"""
from __future__ import annotations

import os
import sys
import re
from pathlib import Path
from typing import NamedTuple
from urllib.parse import urlparse

import structlog

logger = structlog.get_logger()


class ConfigIssue(NamedTuple):
    """A configuration issue with severity and fix instructions."""
    severity: str  # error, warning, info
    key: str
    message: str
    fix: str
    auto_fixable: bool = False


class ConfigValidator:
    """Validates Proxym configuration from .env file."""
    
    REQUIRED_KEYS = [
        "AI_PROXY_MASTER_KEY",
    ]
    
    LLM_PROVIDER_KEYS = [
        "OPENROUTER_API_KEY",
        "ANTHROPIC_API_KEY", 
        "OPENAI_API_KEY",
        "GOOGLE_AI_API_KEY",
        "MISTRAL_API_KEY",
    ]
    
    def __init__(self, env_path: Path | None = None):
        self.env_path = env_path or Path(".env")
        self.issues: list[ConfigIssue] = []
        self.env_vars: dict[str, str] = {}
        
    def load_env(self) -> bool:
        """Load .env file if exists."""
        if not self.env_path.exists():
            self.issues.append(ConfigIssue(
                severity="error",
                key=".env",
                message=".env file not found",
                fix=f"Create {self.env_path.absolute()} with required API keys",
                auto_fixable=False
            ))
            return False
            
        content = self.env_path.read_text()
        for line in content.split('\n'):
            line = line.strip()
            if line and not line.startswith('#') and '=' in line:
                key, _, value = line.partition('=')
                self.env_vars[key] = value.strip().strip('"\'')
        return True
    
    def validate(self) -> list[ConfigIssue]:
        """Run all validation checks."""
        self.issues = []
        
        if not self.load_env():
            return self.issues
            
        self._check_required_keys()
        self._check_llm_providers()
        self._check_proxy_url()
        self._check_ollama()
        self._check_master_key_format()
        
        return self.issues
    
    def _check_required_keys(self):
        """Check required keys exist."""
        for key in self.REQUIRED_KEYS:
            if key not in self.env_vars or not self.env_vars[key]:
                self.issues.append(ConfigIssue(
                    severity="error",
                    key=key,
                    message=f"{key} is required but missing or empty",
                    fix=f"Add {key}=sk-proxy-... to {self.env_path}",
                    auto_fixable=True
                ))
    
    def _check_llm_providers(self):
        """Check at least one LLM provider key exists."""
        has_provider = any(
            self.env_vars.get(key) for key in self.LLM_PROVIDER_KEYS
        )
        if not has_provider:
            self.issues.append(ConfigIssue(
                severity="error",
                key="LLM_PROVIDER",
                message="No LLM provider API key configured",
                fix="Add at least one: OPENROUTER_API_KEY, ANTHROPIC_API_KEY, OPENAI_API_KEY, GOOGLE_AI_API_KEY, or MISTRAL_API_KEY",
                auto_fixable=False
            ))
    
    def _check_proxy_url(self):
        """Check AI_PROXY_URL format if set."""
        url = self.env_vars.get("AI_PROXY_URL", "")
        if url:
            try:
                parsed = urlparse(url)
                if not parsed.scheme or not parsed.netloc:
                    self.issues.append(ConfigIssue(
                        severity="warning",
                        key="AI_PROXY_URL",
                        message=f"Invalid URL format: {url}",
                        fix="Use format: http://localhost:4000 or http://host:port",
                        auto_fixable=False
                    ))
            except Exception:
                pass  # Invalid URL, but not critical
    
    def _check_ollama(self):
        """Check Ollama configuration."""
        ollama_url = self.env_vars.get("OLLAMA_BASE_URL", "")
        if ollama_url and not ollama_url.startswith(("http://", "https://")):
            self.issues.append(ConfigIssue(
                severity="warning",
                key="OLLAMA_BASE_URL",
                message=f"Invalid Ollama URL: {ollama_url}",
                fix="Use format: http://localhost:11434",
                auto_fixable=False
            ))
    
    def _check_master_key_format(self):
        """Check master key format."""
        key = self.env_vars.get("AI_PROXY_MASTER_KEY", "")
        if key:
            # Check for external keys that won't work with dashboard
            if key.startswith("sk-or-"):
                self.issues.append(ConfigIssue(
                    severity="warning",
                    key="AI_PROXY_MASTER_KEY",
                    message="Using external OpenRouter key - dashboard SSE streams may not work",
                    fix="Generate local key: proxym keys generate",
                    auto_fixable=True
                ))
            elif not key.startswith("sk-proxy-") and len(key) < 20:
                self.issues.append(ConfigIssue(
                    severity="warning",
                    key="AI_PROXY_MASTER_KEY",
                    message="Master key format looks invalid",
                    fix="Generate new key: proxym keys generate",
                    auto_fixable=True
                ))
    
    def has_errors(self) -> bool:
        """Check if any error-level issues exist."""
        return any(i.severity == "error" for i in self.issues)
    
    def has_warnings(self) -> bool:
        """Check if any warning-level issues exist."""
        return any(i.severity == "warning" for i in self.issues)
    
    def print_report(self):
        """Print formatted validation report."""
        if not self.issues:
            print("\033[0;32m✓\033[0m Configuration valid - no issues found")
            return
            
        print(f"\n\033[1m{'═' * 50}\033[0m")
        print(f"\033[1m  Configuration Validation Report\033[0m")
        print(f"\033[1m{'═' * 50}\033[0m\n")
        
        errors = [i for i in self.issues if i.severity == "error"]
        warnings = [i for i in self.issues if i.severity == "warning"]
        
        if errors:
            print(f"\033[0;31m✗ {len(errors)} Error(s) - must fix before using LLM features:\033[0m")
            for issue in errors:
                print(f"\n  • \033[1m{issue.key}\033[0m")
                print(f"    {issue.message}")
                print(f"    \033[2mFix: {issue.fix}\033[0m")
                if issue.auto_fixable:
                    print(f"    \033[0;33m[Auto-fix available]\033[0m")
        
        if warnings:
            print(f"\n\033[0;33m⚠ {len(warnings)} Warning(s):\033[0m")
            for issue in warnings:
                print(f"\n  • \033[1m{issue.key}\033[0m")
                print(f"    {issue.message}")
                print(f"    \033[2mFix: {issue.fix}\033[0m")
        
        print(f"\n\033[1m{'═' * 50}\033[0m\n")


def prompt_user_for_fix(issues: list[ConfigIssue]) -> bool:
    """Interactive prompt to fix configuration issues.
    
    Returns True if user wants to continue anyway.
    """
    errors = [i for i in issues if i.severity == "error"]
    auto_fixable = [i for i in errors if i.auto_fixable]
    
    if auto_fixable and len(auto_fixable) == len(errors):
        print(f"\n\033[0;33m► {len(auto_fixable)} issue(s) can be auto-fixed.\033[0m")
        response = input("  Run auto-fix? [Y/n]: ").strip().lower()
        if response in ("", "y", "yes"):
            _run_auto_fix(auto_fixable)
            return True
    
    if errors:
        print(f"\n\033[0;31m► {len(errors)} error(s) must be fixed before using LLM.\033[0m")
        print("  Options:")
        print("    1. Edit .env manually")
        print("    2. Run: proxym keys init")
        print("    3. Run: proxym doctor (diagnostics)")
        response = input("\n  Continue anyway? [y/N]: ").strip().lower()
        return response in ("y", "yes")
    
    return True


def _run_auto_fix(issues: list[ConfigIssue]):
    """Auto-fix configuration issues."""
    from proxym.cli._groups.keys_ctl import generate_key, update_env_file
    
    for issue in issues:
        if issue.key == "AI_PROXY_MASTER_KEY":
            new_key = generate_key()
            if update_env_file(Path(".env"), new_key):
                print(f"  \033[0;32m✓\033[0m Generated new {issue.key}")
            else:
                print(f"  \033[0;31m✗\033[0m Failed to fix {issue.key}")


def validate_before_llm(interactive: bool = True) -> bool:
    """Validate configuration before using LLM features.
    
    If interactive=True, prompts user to fix issues.
    Returns False if validation fails and user doesn't want to continue.
    """
    validator = ConfigValidator()
    issues = validator.validate()
    
    if not issues:
        return True
    
    validator.print_report()
    
    if interactive and sys.stdin.isatty():
        return prompt_user_for_fix(issues)
    
    # Non-interactive: fail on errors
    return not validator.has_errors()


# Convenience function for CLI commands
def check_config_or_exit():
    """Check config and exit if invalid (for CLI commands that need LLM)."""
    if not validate_before_llm(interactive=True):
        print("\n\033[0;31mConfiguration errors must be fixed before proceeding.\033[0m")
        print("\nQuick fix:")
        print("  1. proxym keys init          # Initialize API keys")
        print("  2. proxym doctor             # Full diagnostics")
        print("  3. Edit .env manually        # Manual configuration")
        sys.exit(1)
