"""Command-line interface for Prismor security scanning tool."""

import sys
import json
import click
import threading
import time
from typing import Optional
from .api import PrismorClient, PrismorAPIError, parse_github_repo


def print_success(message: str):
    """Print success message in green."""
    click.secho(f"✓ {message}", fg="green")


def print_error(message: str):
    """Print error message in red."""
    click.secho(f"✗ {message}", fg="red", err=True)


def print_info(message: str):
    """Print info message in blue."""
    click.secho(f"ℹ {message}", fg="blue")


def print_warning(message: str):
    """Print warning message in yellow."""
    click.secho(f"⚠ {message}", fg="yellow")


class Spinner:
    """Simple spinner for showing loading state."""
    
    def __init__(self, message: str = "Processing"):
        self.message = message
        self.spinner_chars = "|/-\\"
        self.spinner_index = 0
        self.running = False
        self.thread = None
    
    def _spin(self):
        """Internal spinner loop."""
        while self.running:
            char = self.spinner_chars[self.spinner_index % len(self.spinner_chars)]
            sys.stdout.write(f"\r{char} {self.message}...")
            sys.stdout.flush()
            self.spinner_index += 1
            time.sleep(0.1)
    
    def start(self):
        """Start the spinner."""
        self.running = True
        self.thread = threading.Thread(target=self._spin, daemon=True)
        self.thread.start()
    
    def stop(self, message: str = None):
        """Stop the spinner and clear the line."""
        self.running = False
        if self.thread:
            self.thread.join(timeout=0.2)
        # Clear the spinner line
        sys.stdout.write("\r" + " " * (len(self.message) + 15) + "\r")
        sys.stdout.flush()
        if message:
            click.echo(message)


def format_scan_results(results: dict, scan_type: str):
    """Format and display scan results."""
    click.echo("\n" + "=" * 60)
    click.secho(f"  Scan Results - {scan_type}", fg="cyan", bold=True)
    click.echo("=" * 60 + "\n")
    
    # Display repository information
    if "repository" in results:
        click.secho("Repository:", fg="yellow", bold=True)
        click.echo(f"  {results['repository']}\n")
    
    if "branch" in results:
        click.secho("Branch:", fg="yellow", bold=True)
        click.echo(f"  {results['branch']}\n")
    
    if "commit_sha" in results:
        click.secho("Commit SHA:", fg="yellow", bold=True)
        click.echo(f"  {results['commit_sha']}\n")
    
    # Display scan results for each scan type
    if "scans" in results:
        scans = results["scans"]
        
        # Vulnerability scan results
        if "vulnerability" in scans:
            vuln_scan = scans["vulnerability"]
            click.secho("Vulnerability Scan:", fg="yellow", bold=True)
            status_color = "green" if vuln_scan.get("status") == "success" else "red"
            click.secho(f"  Status: {vuln_scan.get('status', 'unknown')}", fg=status_color)
            
            if "scan_results" in vuln_scan:
                scan_data = vuln_scan["scan_results"]
                if "vulnerabilities" in scan_data or "Results" in scan_data:
                    vuln_data = scan_data.get("vulnerabilities", scan_data.get("Results", []))
                    if isinstance(vuln_data, list):
                        click.echo(f"  Vulnerabilities Found: {len(vuln_data)}")
                    else:
                        click.echo(f"  Vulnerabilities: Data available")
            
            if "public_url" in vuln_scan:
                click.echo(f"  Results URL: {vuln_scan['public_url']}")
            click.echo()
        
        # SBOM results
        if "sbom" in scans:
            sbom_scan = scans["sbom"]
            click.secho("SBOM Generation:", fg="yellow", bold=True)
            status_color = "green" if sbom_scan.get("status") == "success" else "red"
            click.secho(f"  Status: {sbom_scan.get('status', 'unknown')}", fg=status_color)
            
            if "sbom" in sbom_scan:
                sbom_data = sbom_scan["sbom"]
                if isinstance(sbom_data, list):
                    click.echo(f"  Artifacts Found: {len(sbom_data)}")
                else:
                    click.echo(f"  SBOM: Data available")
            
            if "public_url" in sbom_scan:
                click.echo(f"  Results URL: {sbom_scan['public_url']}")
            click.echo()
        
        # Secret scan results
        if "secret" in scans:
            secret_scan = scans["secret"]
            click.secho("Secret Detection:", fg="yellow", bold=True)
            status_color = "green" if secret_scan.get("status") == "success" else "red"
            click.secho(f"  Status: {secret_scan.get('status', 'unknown')}", fg=status_color)
            
            if "summary" in secret_scan:
                summary = secret_scan["summary"]
                if isinstance(summary, dict):
                    for key, value in summary.items():
                        click.echo(f"  {key}: {value}")
                else:
                    click.echo(f"  Summary: {summary}")
            
            if "public_url" in secret_scan:
                click.echo(f"  Results URL: {secret_scan['public_url']}")
            click.echo()
    
    # Display scan timestamp
    if "scanned_at" in results:
        click.secho("Scanned At:", fg="yellow", bold=True)
        click.echo(f"  {results['scanned_at']}\n")
    
    click.echo("=" * 60 + "\n")


@click.group(invoke_without_command=True)
@click.option(
    "--repo",
    "scan_repo",
    type=str,
    help="Repository to scan (username/repo, GitHub URL, SSH URL, etc.)"
)
@click.option("--scan", is_flag=True, help="Perform vulnerability scanning")
@click.option("--sbom", is_flag=True, help="Generate Software Bill of Materials")
@click.option("--detect-secret", is_flag=True, help="Detect secrets in repository")
@click.option("--fullscan", is_flag=True, help="Perform all scan types")
@click.option("--branch", type=str, help="Specific branch to scan (defaults to main/master)")
@click.option("--json", "output_json", is_flag=True, help="Output results in JSON format")
@click.version_option(version="1.0.5", prog_name="prismor")
@click.pass_context
def cli(ctx, scan_repo: Optional[str], scan: bool, sbom: bool, detect_secret: bool, 
        fullscan: bool, branch: Optional[str], output_json: bool):
    """Prismor CLI - Security scanning tool for GitHub repositories.
    
    Examples:
        prismor --repo username/repo --scan
        prismor --repo username/repo --fullscan
        prismor --repo https://github.com/username/repo --detect-secret
        prismor --repo git@github.com:username/repo.git --sbom
        prismor --repo github.com/username/repo --fullscan --branch develop
        prismor status
        prismor repos
    """
    # If no command and no scan option, show help
    if ctx.invoked_subcommand is None and not scan_repo:
        click.echo(ctx.get_help())
        return
    
    # If scan option is provided, perform the scan
    if scan_repo:
        # Check if at least one scan type is selected
        if not any([scan, sbom, detect_secret, fullscan]):
            print_error("Please specify at least one scan type: --scan, --sbom, --detect-secret, or --fullscan")
            sys.exit(1)
        
        try:
            # Initialize API client
            print_info(f"Initializing Prismor scan for: {scan_repo}")
            client = PrismorClient()
            
            # Determine scan type for display
            scan_types = []
            if fullscan:
                scan_types.append("Full Scan (scan + SBOM + Secret Detection)")
            else:
                if scan:
                    scan_types.append("scan")
                if sbom:
                    scan_types.append("SBOM")
                if detect_secret:
                    scan_types.append("Secret Detection")
            
            print_info(f"Scan type: {', '.join(scan_types)}")
            if scan or fullscan:
                print_info("Starting scan... (vulnerability scans run asynchronously and may take up to 10 minutes)")
            else:
                print_info("Starting scan... (this may take a few minutes)")
            
            # Show loading spinner during scan
            spinner = Spinner("Scanning repository")
            spinner.start()
            
            try:
                # Perform scan
                results = client.scan(
                    repo=scan_repo,
                    scan=scan,
                    sbom=sbom,
                    detect_secret=detect_secret,
                    fullscan=fullscan,
                    branch=branch
                )
                spinner.stop()
            except Exception as e:
                spinner.stop()
                raise e
            
            # Output results
            if output_json:
                click.echo(json.dumps(results, indent=2))
            else:
                print_success("Scan completed successfully!")
                format_scan_results(results, ', '.join(scan_types))
                
                # Try to get repository ID and display dashboard link
                try:
                    # Extract repo name from scan input using the comprehensive parser
                    repo_name = parse_github_repo(scan_repo)
                    
                    # Get repository ID
                    repo_info = client.get_repository_by_name(repo_name)
                    if repo_info.get("success") and "repository" in repo_info:
                        repo_id = repo_info["repository"]["id"]
                        dashboard_url = f"https://prismor.dev/repositories/{repo_id}"
                        
                        click.echo("\n" + "=" * 60)
                        click.secho("  📊 Dashboard Analysis", fg="cyan", bold=True)
                        click.echo("=" * 60)
                        click.secho(f"🔗 View detailed analysis and insights:", fg="blue")
                        click.secho(f"   {dashboard_url}", fg="green", bold=True)
                        click.echo("\n💡 The dashboard provides:")
                        click.echo("   • Interactive visualizations and charts")
                        click.echo("   • Historical vulnerability trends")
                        click.echo("   • Detailed security reports")
                        click.echo("   • Team collaboration features")
                        click.echo("   • Export capabilities")
                        click.echo("=" * 60 + "\n")
                        
                except PrismorAPIError as e:
                    # Repository might not be found, continue without dashboard link
                    print_warning(f"Could not generate dashboard link: {str(e)}")
                except Exception as e:
                    # Any other error, continue without dashboard link
                    print_warning(f"Could not generate dashboard link: {str(e)}")
            
        except PrismorAPIError as e:
            print_error(str(e))
            sys.exit(1)
        except Exception as e:
            print_error(f"Unexpected error: {str(e)}")
            sys.exit(1)


@cli.command()
def version():
    """Display the version of Prismor CLI."""
    click.echo("Prismor CLI v1.0.5")


@cli.command()
def config():
    """Display current configuration."""
    import os
    
    click.echo("\n" + "=" * 60)
    click.secho("  Prismor CLI Configuration", fg="cyan", bold=True)
    click.echo("=" * 60 + "\n")
    
    # Check API key
    api_key = os.environ.get("PRISMOR_API_KEY")
    if api_key:
        # Show only first and last 4 characters
        masked_key = f"{api_key[:4]}...{api_key[-4:]}" if len(api_key) > 8 else "***"
        print_success(f"PRISMOR_API_KEY: {masked_key}")
    else:
        print_error("PRISMOR_API_KEY: Not set")
        click.echo("\nPlease specify your API key. You can generate one for free at:")
        click.secho("  https://www.prismor.dev/cli", fg="cyan", underline=True)
        click.echo("\nTo set your API key, run:")
        click.echo("  export PRISMOR_API_KEY=your_api_key")
    
    # click.echo("\nAPI Endpoint: http://localhost:3000")
    click.echo("=" * 60 + "\n")


@cli.command()
def repos():
    """List your connected repositories."""
    try:
        client = PrismorClient()
        spinner = Spinner("Loading repositories")
        spinner.start()
        try:
            repos_data = client.get_repositories()
            spinner.stop()
        except Exception as e:
            spinner.stop()
            raise e
        
        click.echo("\n" + "=" * 60)
        click.secho("  Your Repositories", fg="cyan", bold=True)
        click.echo("=" * 60 + "\n")
        
        user_info = repos_data.get("user", {})
        if user_info:
            click.secho(f"User: {user_info.get('name', 'Unknown')} ({user_info.get('email', 'No email')})", fg="yellow")
            click.echo()
        
        repositories = repos_data.get("repositories", [])
        if repositories:
            for repo in repositories:
                click.secho(f"• {repo.get('name', 'Unknown')}", fg="green")
                click.echo(f"  URL: {repo.get('htmlUrl', 'No URL')}")
                click.echo(f"  Owner: {repo.get('githubOwner', 'Unknown')}")
                click.echo()
        else:
            print_warning("No repositories found. Connect repositories through the web interface.")
        
        click.echo("=" * 60 + "\n")
        
    except PrismorAPIError as e:
        print_error(str(e))
        sys.exit(1)
    except Exception as e:
        print_error(f"Unexpected error: {str(e)}")
        sys.exit(1)


@cli.command()
@click.argument("repo", type=str)
@click.option("--branch", type=str, help="Specific branch to scan (defaults to main)")
@click.option("--token", type=str, help="GitHub token (or set GITHUB_TOKEN env var)")
@click.option("--json", "output_json", is_flag=True, help="Output results in JSON format")
def start_scan(repo: str, branch: Optional[str], token: Optional[str], output_json: bool):
    """Start a vulnerability scan and return a job_id for status checking.
    
    REPO is the repository to scan (username/repo, GitHub URL, SSH URL, etc.)
    
    This command starts a scan asynchronously and returns immediately with a job_id.
    Use 'prismor scan-status <job_id>' to check when the scan completes.
    
    Note: Requires GitHub token. Set GITHUB_TOKEN environment variable or use --token option.
    
    Examples:
        prismor start-scan username/repo
        prismor start-scan https://github.com/username/repo --branch develop
        prismor start-scan username/repo --token ghp_xxxxx
        prismor start-scan username/repo --json
    """
    try:
        client = PrismorClient()
        print_info(f"Starting vulnerability scan for: {repo}")
        if branch:
            print_info(f"Branch: {branch}")
        
        spinner = Spinner("Starting scan")
        spinner.start()
        try:
            result = client.start_vulnerability_scan(repo, branch, token)
            spinner.stop()
        except Exception as e:
            spinner.stop()
            raise e
        
        if output_json:
            click.echo(json.dumps(result, indent=2))
        else:
            click.echo("\n" + "=" * 60)
            click.secho("  Scan Started", fg="cyan", bold=True)
            click.echo("=" * 60 + "\n")
            
            job_id = result.get("job_id")
            if job_id:
                print_success(f"Scan started successfully!")
                click.echo()
                click.secho(f"Job ID: {job_id}", fg="yellow", bold=True)
                click.echo()
                click.secho("Repository:", fg="yellow", bold=True)
                click.echo(f"  {result.get('repository', repo)}")
                click.echo()
                if "branch" in result:
                    click.secho("Branch:", fg="yellow", bold=True)
                    click.echo(f"  {result['branch']}")
                    click.echo()
                click.secho("Status:", fg="yellow", bold=True)
                click.echo(f"  {result.get('status', 'accepted')}")
                click.echo()
                click.secho("Next Steps:", fg="cyan", bold=True)
                click.echo(f"  Check scan status with:")
                click.secho(f"    prismor scan-status {job_id}", fg="green", bold=True)
                click.echo()
            else:
                print_error("Failed to get job_id from response")
                click.echo(json.dumps(result, indent=2))
            
            click.echo("=" * 60 + "\n")
            
    except PrismorAPIError as e:
        print_error(str(e))
        sys.exit(1)
    except Exception as e:
        print_error(f"Unexpected error: {str(e)}")
        sys.exit(1)


@cli.command()
@click.argument("job_id", type=str)
@click.option("--json", "output_json", is_flag=True, help="Output results in JSON format")
def scan_status(job_id: str, output_json: bool):
    """Check the status of a vulnerability scan job.
    
    JOB_ID is the job ID returned when starting a scan.
    
    Examples:
        prismor scan-status a724a4663cda4bf087ad171683cb726d
        prismor scan-status 50cbe253e5634227b81fe744c2a0b3e7 --json
    """
    try:
        client = PrismorClient()
        print_info(f"Checking scan status for job: {job_id}")
        
        spinner = Spinner("Checking status")
        spinner.start()
        try:
            status_data = client.check_scan_status(job_id)
            spinner.stop()
        except Exception as e:
            spinner.stop()
            raise e
        
        if output_json:
            click.echo(json.dumps(status_data, indent=2))
        else:
            click.echo("\n" + "=" * 60)
            click.secho("  Scan Status", fg="cyan", bold=True)
            click.echo("=" * 60 + "\n")
            
            click.secho(f"Job ID: {status_data.get('job_id', job_id)}", fg="yellow", bold=True)
            click.echo()
            
            status = status_data.get("status", "unknown")
            if status == "completed":
                print_success(f"Status: {status}")
                click.echo()
                
                if "repository" in status_data:
                    click.secho("Repository:", fg="yellow", bold=True)
                    click.echo(f"  {status_data['repository']}")
                    click.echo()
                
                if "branch" in status_data:
                    click.secho("Branch:", fg="yellow", bold=True)
                    click.echo(f"  {status_data['branch']}")
                    click.echo()
                
                if "duration" in status_data:
                    click.secho("Duration:", fg="yellow", bold=True)
                    click.echo(f"  {status_data['duration']:.2f} seconds")
                    click.echo()
                
                if "public_url" in status_data:
                    click.secho("Results URL:", fg="yellow", bold=True)
                    click.secho(f"  {status_data['public_url']}", fg="green")
                    click.echo()
                
                if "presigned_url" in status_data:
                    click.secho("Presigned URL (expires in 1 hour):", fg="yellow", bold=True)
                    click.secho(f"  {status_data['presigned_url']}", fg="blue")
                    click.echo()
                
                # Display vulnerability summary if available
                if "summary" in status_data:
                    summary = status_data["summary"]
                    click.secho("Vulnerability Summary:", fg="yellow", bold=True)
                    click.echo(f"  Total Vulnerabilities: {summary.get('total_vulnerabilities', 0)}")
                    click.echo(f"  Total Targets Scanned: {summary.get('total_targets', 0)}")
                    click.echo()
                    
                    severity_breakdown = summary.get('severity_breakdown', {})
                    if severity_breakdown:
                        click.secho("  Severity Breakdown:", fg="yellow")
                        if severity_breakdown.get('CRITICAL', 0) > 0:
                            click.secho(f"    CRITICAL: {severity_breakdown['CRITICAL']}", fg="red", bold=True)
                        if severity_breakdown.get('HIGH', 0) > 0:
                            click.secho(f"    HIGH: {severity_breakdown['HIGH']}", fg="red")
                        if severity_breakdown.get('MEDIUM', 0) > 0:
                            click.secho(f"    MEDIUM: {severity_breakdown['MEDIUM']}", fg="yellow")
                        if severity_breakdown.get('LOW', 0) > 0:
                            click.secho(f"    LOW: {severity_breakdown['LOW']}", fg="blue")
                        if severity_breakdown.get('UNKNOWN', 0) > 0:
                            click.secho(f"    UNKNOWN: {severity_breakdown['UNKNOWN']}", fg="white")
                        click.echo()
                
                if "scan_date" in status_data:
                    click.secho("Scan Date:", fg="yellow", bold=True)
                    click.echo(f"  {status_data['scan_date']}")
                    click.echo()
                    
            elif status == "running":
                print_info(f"Status: {status}")
                if "message" in status_data:
                    click.echo(f"  {status_data['message']}")
                click.echo()
                click.echo("The scan is still in progress. Check back in a few moments.")
                click.echo()
                
            elif status == "failed":
                print_error(f"Status: {status}")
                if "error" in status_data:
                    click.echo(f"  Error: {status_data['error']}")
                click.echo()
            else:
                click.secho(f"Status: {status}", fg="yellow")
                click.echo()
            
            click.echo("=" * 60 + "\n")
            
    except PrismorAPIError as e:
        print_error(str(e))
        sys.exit(1)
    except Exception as e:
        print_error(f"Unexpected error: {str(e)}")
        sys.exit(1)


@cli.command()
def status():
    """Check your account status and GitHub integration."""
    try:
        client = PrismorClient()
        spinner = Spinner("Checking account status")
        spinner.start()
        try:
            auth_data = client.authenticate()
            spinner.stop()
        except Exception as e:
            spinner.stop()
            raise e
        
        click.echo("\n" + "=" * 60)
        click.secho("  Account Status", fg="cyan", bold=True)
        click.echo("=" * 60 + "\n")
        
        user_info = auth_data.get("user", {})
        if user_info:
                        
            repositories = user_info.get("repositories", [])
            click.secho(f"Connected Repositories: {len(repositories)}", fg="green")
            
            if repositories:
                click.echo("\nRepository List:")
                for repo in repositories:
                    click.echo(f"  • {repo.get('name', 'Unknown')} ({repo.get('htmlUrl', 'No URL')})")
            else:
                print_warning("No repositories connected.")
                click.echo("\nTo connect repositories:")
                click.echo("  1. Visit https://prismor.dev/dashboard")
                click.echo("  2. Connect your GitHub account")
                click.echo("  3. Select repositories to scan")
        else:
            print_error("Failed to retrieve account information.")
        
        click.echo("\n" + "=" * 60 + "\n")
        
    except PrismorAPIError as e:
        print_error(str(e))
        sys.exit(1)
    except Exception as e:
        print_error(f"Unexpected error: {str(e)}")
        sys.exit(1)


def main():
    """Entry point for the CLI."""
    cli()


if __name__ == "__main__":
    main()

