"""
JSEye Pipeline - High-Performance Parallel Execution Pipeline
"""

from pathlib import Path
from typing import Dict, Any
import asyncio
from rich.console import Console
from rich.table import Table

from .utils.fs import load_lines, ensure_dir, save_json
from .utils.logger import log_progress
from .utils.cache import JSEyeCache
from .modules.harvest import URLHarvester
from .modules.js_filter import JSFilter
from .modules.js_download import JSDownloader
from .modules.tiered_analysis import TieredAnalysisEngine
from .modules.analyze_regex import RegexAnalyzer
from .modules.analyze_ast import ASTAnalyzer
from .modules.linkfinder import LinkFinderIntegration
from .modules.secrets import SecretsDetector
from .modules.sinks import SinkDetector
from .modules.correlate import CorrelationEngine

console = Console()

class JSEyePipeline:
    """High-Performance Parallel JSEye Pipeline"""
    
    def __init__(self, input_file: str, output_dir: str, args):
        self.input_file = Path(input_file)
        self.output_dir = Path(output_dir)
        self.args = args
        
        # Ensure output directory exists
        ensure_dir(self.output_dir)
        
        # Initialize cache manager
        self.cache = JSEyeCache(self.output_dir / ".cache")
        
        # Initialize modules with cache support
        self.harvester = URLHarvester(self.output_dir, cache_manager=self.cache)
        self.js_filter = JSFilter(self.output_dir)
        self.js_downloader = JSDownloader(self.output_dir, cache_manager=self.cache)
        self.tiered_analyzer = TieredAnalysisEngine(self.output_dir, cache_manager=self.cache)
        self.regex_analyzer = RegexAnalyzer(self.output_dir)
        self.ast_analyzer = ASTAnalyzer(self.output_dir)
        self.linkfinder = LinkFinderIntegration(self.output_dir)
        self.secrets_detector = SecretsDetector(self.output_dir)
        self.sink_detector = SinkDetector(self.output_dir)
        self.correlation_engine = CorrelationEngine(self.output_dir)
        
        # Performance tracking
        self.performance_stats = {
            'cache_hits': 0,
            'cache_misses': 0,
            'parallel_speedup': 0,
            'files_skipped': 0,
            'total_time': 0
        }
        
        # Results storage
        self.results = {
            'domains_count': 0,
            'urls_harvested': 0,
            'js_files_found': 0,
            'js_files_analyzed': 0,
            'endpoints_found': 0,
            'secrets_found': 0,
            'sinks_found': 0,
            'high_confidence': 0,
            'performance': self.performance_stats
        }
    
    def load_domains(self) -> list:
        """Load domains from input file"""
        log_progress(f"Loading domains from {self.input_file}")
        
        domains = load_lines(self.input_file)
        if not domains:
            raise ValueError(f"No domains found in {self.input_file}")
        
        self.results['domains_count'] = len(domains)
        log_progress(f"Loaded {len(domains)} domains")
        
        return domains
    
    def run_harvest_phase(self, domains: list) -> list:
        """Run parallel URL harvesting phase"""
        log_progress("Starting parallel URL harvesting...")
        urls = self.harvester.harvest_urls_parallel(domains)
        self.results['urls_harvested'] = len(urls)
        
        # Update performance stats
        stats = self.harvester.get_performance_stats()
        self.performance_stats.update(stats)
        
        return urls
    
    def run_js_filter_phase(self, urls: list) -> dict:
        """Run intelligent JavaScript filtering phase"""
        log_progress("Running intelligent JS filtering with early prioritization...")
        js_results = self.js_filter.filter_and_prioritize_js(urls)
        
        self.results['js_files_found'] = len(js_results['all_js'])
        
        # Log prioritization results
        log_progress(f"Tier 1 (High Priority): {len(js_results['tier1'])} files")
        log_progress(f"Tier 2 (Medium Priority): {len(js_results['tier2'])} files") 
        log_progress(f"Tier 3 (Low Priority): {len(js_results['tier3'])} files")
        log_progress(f"Vendor files skipped: {js_results.get('vendor_skipped', 0)}")
        
        self.performance_stats['files_skipped'] = js_results.get('vendor_skipped', 0)
        
        return js_results
    
    def run_js_download_phase(self, js_results: dict) -> dict:
        """Run parallel JavaScript download phase"""
        if self.args.js_only:
            log_progress("Stopping after JS discovery (--js-only flag)")
            return {'tier1': [], 'tier2': [], 'tier3': []}
        
        log_progress("Starting parallel JS downloads...")
        
        # Download files by tier with different limits
        downloaded_files = {}
        
        # Tier 1: High priority - download more files
        if js_results['tier1']:
            log_progress(f"Downloading {len(js_results['tier1'])} Tier 1 files...")
            downloaded_files['tier1'] = self.js_downloader.download_js_files_parallel(
                js_results['tier1'], max_files=50
            )
        
        # Tier 2: Medium priority - moderate limit
        if js_results['tier2']:
            log_progress(f"Downloading {len(js_results['tier2'])} Tier 2 files...")
            downloaded_files['tier2'] = self.js_downloader.download_js_files_parallel(
                js_results['tier2'], max_files=30
            )
        
        # Tier 3: Low priority - limited downloads
        if js_results['tier3']:
            log_progress(f"Downloading {len(js_results['tier3'])} Tier 3 files...")
            downloaded_files['tier3'] = self.js_downloader.download_js_files_parallel(
                js_results['tier3'], max_files=20
            )
        
        # Update performance stats
        download_stats = self.js_downloader.get_performance_stats()
        self.performance_stats.update(download_stats)
        
        return downloaded_files
    
    def run_tiered_analysis_phase(self, downloaded_files: dict) -> dict:
        """Run tiered analysis phase with smart resource allocation"""
        if not any(downloaded_files.values()):
            log_progress("No files to analyze")
            return {'endpoints': [], 'secrets': [], 'sinks': [], 'functions': []}
        
        log_progress("Starting tiered analysis engine...")
        
        # Run tiered analysis
        analysis_results = self.tiered_analyzer.analyze_tiered_files(
            downloaded_files, 
            skip_ast=self.args.skip_ast,
            regex_only=self.args.regex_only
        )
        
        # Update performance stats
        analysis_stats = self.tiered_analyzer.get_performance_stats()
        self.performance_stats.update(analysis_stats)
        
        return analysis_results
    
    def run_secrets_phase(self, downloaded_files: dict) -> list:
        """Run secrets detection phase with tiered files"""
        if self.args.no_secrets:
            log_progress("Skipping secrets detection (--no-secrets flag)")
            return []
        
        all_files = []
        for tier_files in downloaded_files.values():
            all_files.extend(tier_files)
        
        secrets = self.secrets_detector.run_mantra(all_files)
        return secrets
    
    def run_sinks_phase(self, downloaded_files: dict) -> list:
        """Run sink detection phase with tiered files"""
        if self.args.no_sinks:
            log_progress("Skipping sink detection (--no-sinks flag)")
            return []
        
        all_files = []
        for tier_files in downloaded_files.values():
            all_files.extend(tier_files)
        
        sinks = self.sink_detector.detect_sinks(all_files)
        return sinks
    
    def run_linkfinder_phase(self, downloaded_files: dict) -> list:
        """Run LinkFinder phase with tiered file support"""
        all_files = []
        for tier_files in downloaded_files.values():
            all_files.extend(tier_files)
        
        if not all_files:
            log_progress("No JavaScript files to analyze with LinkFinder")
            return []
        
        log_progress("Running LinkFinder endpoint discovery...")
        endpoints = self.linkfinder.run_linkfinder(all_files)
        
        # Additional processing for LinkFinder results
        if endpoints:
            # Categorize endpoints by type
            api_endpoints = [e for e in endpoints if '/api/' in e.get('url', '')]
            admin_endpoints = [e for e in endpoints if any(term in e.get('url', '').lower() 
                                                         for term in ['admin', 'manage', 'dashboard'])]
            
            log_progress(f"LinkFinder discovered {len(endpoints)} total endpoints")
            if api_endpoints:
                log_progress(f"Found {len(api_endpoints)} API endpoints")
            if admin_endpoints:
                log_progress(f"Found {len(admin_endpoints)} admin endpoints")
        
        return endpoints
    
    def run_correlation_phase(self, analysis_results: dict) -> dict:
        """Run correlation phase"""
        if self.args.no_correlate:
            log_progress("Skipping correlation (--no-correlate flag)")
            return analysis_results
        
        correlated_results = self.correlation_engine.correlate_findings(analysis_results)
        return correlated_results
    
    def run(self) -> Dict[str, Any]:
        """Run the high-performance parallel JSEye pipeline"""
        import time
        start_time = time.time()
        
        try:
            # Phase 1: Load domains
            domains = self.load_domains()
            
            # Phase 2: Parallel URL harvesting
            urls = self.run_harvest_phase(domains)
            
            # Phase 3: Intelligent JS filtering with prioritization
            js_results = self.run_js_filter_phase(urls)
            
            # Early exit for --js-only
            if self.args.js_only:
                return self.results
            
            # Phase 4: Parallel JS downloads by tier
            downloaded_files = self.run_js_download_phase(js_results)
            
            # Phase 5: Tiered analysis engine
            analysis_results = self.run_tiered_analysis_phase(downloaded_files)
            
            # Phase 6: LinkFinder with tiered files
            linkfinder_endpoints = self.run_linkfinder_phase(downloaded_files)
            analysis_results['endpoints'].extend(linkfinder_endpoints)
            
            # Phase 7: Secrets detection with tiered files
            secrets = self.run_secrets_phase(downloaded_files)
            analysis_results['secrets'] = secrets
            
            # Phase 8: Sink detection with tiered files
            sinks = self.run_sinks_phase(downloaded_files)
            analysis_results['sinks'] = sinks
            
            # Phase 9: Correlation
            final_results = self.run_correlation_phase(analysis_results)
            
            # Calculate total files analyzed
            total_analyzed = sum(len(tier_files) for tier_files in downloaded_files.values())
            
            # Update results with performance stats
            self.performance_stats['total_time'] = time.time() - start_time
            self.performance_stats.update(self.cache.get_stats())
            
            self.results.update({
                'js_files_analyzed': total_analyzed,
                'endpoints_found': len(analysis_results.get('endpoints', [])),
                'secrets_found': len(secrets),
                'sinks_found': len(sinks),
                'high_confidence': len(final_results.get('high_confidence_endpoints', [])),
                'performance': self.performance_stats
            })
            
            # Save performance report
            save_json(self.performance_stats, self.output_dir / "performance_report.json")
            
            return self.results
            
        except Exception as e:
            console.print(f"[red]Pipeline error: {e}[/red]")
            raise
    
    def show_summary(self, results: Dict[str, Any]):
        """Show enhanced summary with performance metrics"""
        console.print("\n" + "─" * 50)
        console.print("[bold cyan]JSEye v1.0.2 - Performance Summary[/bold cyan]", justify="center")
        console.print("─" * 50)
        
        # Main results table
        table = Table(show_header=False, box=None)
        table.add_column("Metric", style="cyan")
        table.add_column("Count", style="green bold")
        
        table.add_row("Domains Processed", str(results['domains_count']))
        table.add_row("URLs Harvested", str(results['urls_harvested']))
        table.add_row("JS Files Found", str(results['js_files_found']))
        table.add_row("JS Files Analyzed", str(results['js_files_analyzed']))
        table.add_row("Endpoints Found", str(results['endpoints_found']))
        table.add_row("Secrets Found", str(results['secrets_found']))
        table.add_row("Sinks Found", str(results['sinks_found']))
        table.add_row("High Confidence", str(results['high_confidence']))
        
        console.print(table)
        
        # Performance metrics table
        if 'performance' in results:
            perf = results['performance']
            console.print("\n[bold yellow]Performance Metrics[/bold yellow]")
            
            perf_table = Table(show_header=False, box=None)
            perf_table.add_column("Metric", style="yellow")
            perf_table.add_column("Value", style="green bold")
            
            perf_table.add_row("Total Time", f"{perf.get('total_time', 0):.2f}s")
            perf_table.add_row("Cache Hits", str(perf.get('cache_hits', 0)))
            perf_table.add_row("Cache Misses", str(perf.get('cache_misses', 0)))
            perf_table.add_row("Files Skipped", str(perf.get('files_skipped', 0)))
            
            if perf.get('cache_hits', 0) + perf.get('cache_misses', 0) > 0:
                hit_rate = perf.get('cache_hits', 0) / (perf.get('cache_hits', 0) + perf.get('cache_misses', 0)) * 100
                perf_table.add_row("Cache Hit Rate", f"{hit_rate:.1f}%")
            
            console.print(perf_table)
        
        console.print(f"\n[bold green]Output Directory:[/bold green] {self.output_dir}")
        console.print("─" * 50)
        console.print()