"""
Base Command Class

Provides common functionality for all CLI command classes.
"""

import json
import logging
from pathlib import Path
from datetime import datetime
from typing import Dict, List, Optional, Any
import yaml

logger = logging.getLogger(__name__)

class BaseCommand:
    """Base class for all CLI commands"""
    
    def __init__(self, config):
        self.config = config
        self.logger = logger
    
    def get_cve_directory(self, cve_id: str) -> Path:
        """Get the directory path for a specific CVE"""
        year = cve_id.split('-')[1]  # Extract year from CVE-YYYY-NNNN
        return self.config.cves_dir / year / cve_id
    
    def ensure_cve_directory(self, cve_id: str) -> Path:
        """Ensure CVE directory exists and return its path"""
        cve_dir = self.get_cve_directory(cve_id)
        cve_dir.mkdir(parents=True, exist_ok=True)
        return cve_dir
    
    def load_cve_metadata(self, cve_id: str) -> Optional[Dict]:
        """Load metadata for a specific CVE"""
        cve_dir = self.get_cve_directory(cve_id)
        metadata_file = cve_dir / "metadata.json"
        
        if not metadata_file.exists():
            return None
        
        try:
            with open(metadata_file, 'r') as f:
                return json.load(f)
        except Exception as e:
            self.logger.error(f"Error loading metadata for {cve_id}: {e}")
            return None
    
    def save_cve_metadata(self, cve_id: str, metadata: Dict) -> bool:
        """Save metadata for a specific CVE"""
        cve_dir = self.ensure_cve_directory(cve_id)
        metadata_file = cve_dir / "metadata.json"
        
        # Update timestamps
        if 'updated_at' not in metadata:
            metadata['updated_at'] = datetime.utcnow().isoformat()
        
        try:
            with open(metadata_file, 'w') as f:
                json.dump(metadata, f, indent=2, default=str)
            return True
        except Exception as e:
            self.logger.error(f"Error saving metadata for {cve_id}: {e}")
            return False
    
    def list_cve_rules(self, cve_id: str) -> List[str]:
        """List all SIGMA rule files for a CVE"""
        cve_dir = self.get_cve_directory(cve_id)
        if not cve_dir.exists():
            return []
        
        rule_files = []
        for file in cve_dir.glob("rule_*.sigma"):
            rule_files.append(file.name)
        
        return sorted(rule_files)
    
    def load_sigma_rule(self, cve_id: str, rule_file: str) -> Optional[str]:
        """Load a specific SIGMA rule file content"""
        cve_dir = self.get_cve_directory(cve_id)
        rule_path = cve_dir / rule_file
        
        if not rule_path.exists():
            return None
        
        try:
            with open(rule_path, 'r') as f:
                return f.read()
        except Exception as e:
            self.logger.error(f"Error loading rule {rule_file} for {cve_id}: {e}")
            return None
    
    def save_sigma_rule(self, cve_id: str, rule_file: str, content: str) -> bool:
        """Save a SIGMA rule file"""
        cve_dir = self.ensure_cve_directory(cve_id)
        rule_path = cve_dir / rule_file
        
        try:
            with open(rule_path, 'w') as f:
                f.write(content)
            
            # Update metadata to track this rule file
            metadata = self.load_cve_metadata(cve_id) or {}
            if 'file_manifest' not in metadata:
                metadata['file_manifest'] = []
            
            if rule_file not in metadata['file_manifest']:
                metadata['file_manifest'].append(rule_file)
            
            # Update rule generation info
            if 'rule_generation' not in metadata:
                metadata['rule_generation'] = {}
            
            method = rule_file.replace('rule_', '').replace('.sigma', '')
            metadata['rule_generation'][method] = {
                'generated_at': datetime.utcnow().isoformat(),
                'file': rule_file
            }
            
            self.save_cve_metadata(cve_id, metadata)
            return True
            
        except Exception as e:
            self.logger.error(f"Error saving rule {rule_file} for {cve_id}: {e}")
            return False
    
    def get_all_cves(self, year: Optional[int] = None) -> List[str]:
        """Get list of all CVEs, optionally filtered by year"""
        cves = []
        
        if year:
            year_dir = self.config.cves_dir / str(year)
            if year_dir.exists():
                for cve_dir in year_dir.iterdir():
                    if cve_dir.is_dir() and cve_dir.name.startswith('CVE-'):
                        cves.append(cve_dir.name)
        else:
            # Get all CVEs across all years
            for year_dir in self.config.cves_dir.iterdir():
                if year_dir.is_dir() and year_dir.name.isdigit():
                    for cve_dir in year_dir.iterdir():
                        if cve_dir.is_dir() and cve_dir.name.startswith('CVE-'):
                            cves.append(cve_dir.name)
        
        return sorted(cves)
    
    def get_years_with_data(self) -> List[int]:
        """Get list of years that have CVE data"""
        years = []
        for year_dir in self.config.cves_dir.iterdir():
            if year_dir.is_dir() and year_dir.name.isdigit():
                # Check if year directory has any CVE subdirectories
                has_cves = any(
                    cve_dir.is_dir() and cve_dir.name.startswith('CVE-')
                    for cve_dir in year_dir.iterdir()
                )
                if has_cves:
                    years.append(int(year_dir.name))
        
        return sorted(years)
    
    def validate_cve_id(self, cve_id: str) -> bool:
        """Validate CVE ID format"""
        import re
        pattern = r'^CVE-\d{4}-\d{4,}$'
        return bool(re.match(pattern, cve_id))
    
    def print_table(self, headers: List[str], rows: List[List[str]], title: Optional[str] = None):
        """Print a formatted table"""
        import click
        
        if title:
            click.echo(f"\n{title}")
            click.echo("=" * len(title))
        
        if not rows:
            click.echo("No data found.")
            return
        
        # Calculate column widths
        widths = [len(h) for h in headers]
        for row in rows:
            for i, cell in enumerate(row):
                if i < len(widths):
                    widths[i] = max(widths[i], len(str(cell)))
        
        # Print headers
        header_line = " | ".join(h.ljust(w) for h, w in zip(headers, widths))
        click.echo(header_line)
        click.echo("-" * len(header_line))
        
        # Print rows
        for row in rows:
            row_line = " | ".join(str(cell).ljust(w) for cell, w in zip(row, widths))
            click.echo(row_line)
    
    def format_json_output(self, data: Any, pretty: bool = True) -> str:
        """Format data as JSON"""
        if pretty:
            return json.dumps(data, indent=2, default=str)
        else:
            return json.dumps(data, default=str)
    
    def format_yaml_output(self, data: Any) -> str:
        """Format data as YAML"""
        return yaml.dump(data, default_flow_style=False)
    
    def success(self, message: str):
        """Print success message"""
        import click
        click.echo(click.style(f"✓ {message}", fg='green'))
    
    def error(self, message: str):
        """Print error message"""
        import click
        click.echo(click.style(f"✗ {message}", fg='red'), err=True)
    
    def warning(self, message: str):
        """Print warning message"""
        import click
        click.echo(click.style(f"⚠ {message}", fg='yellow'))
    
    def info(self, message: str):
        """Print info message"""
        import click
        click.echo(click.style(f"ℹ {message}", fg='blue'))