<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\File;
use App\Models\User;
use App\Models\License;
use App\Models\LicenseLog;

/**
 * Security Audit Command
 * 
 * This command performs comprehensive security audits of the application,
 * checking for potential vulnerabilities and security issues.
 */
class SecurityAuditCommand extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'security:audit 
                            {--report : Generate detailed security report}
                            {--fix : Attempt to fix found issues automatically}
                            {--email= : Send report to specific email address}';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Perform comprehensive security audit of the application';

    /**
     * Security issues found during audit
     *
     * @var array
     */
    private $issues = [];

    /**
     * Execute the console command.
     */
    public function handle()
    {
        $this->info('Starting security audit...');
        
        // Perform various security checks
        $this->checkDatabaseSecurity();
        $this->checkFilePermissions();
        $this->checkConfigurationSecurity();
        $this->checkUserAccountSecurity();
        $this->checkLicenseSystemSecurity();
        $this->checkLogFiles();
        $this->checkDependencies();
        $this->checkEnvironmentSecurity();

        // Generate report if requested
        if ($this->option('report')) {
            $this->generateSecurityReport();
        }

        // Attempt to fix issues if requested
        if ($this->option('fix')) {
            $this->fixSecurityIssues();
        }

        // Send email report if requested
        if ($this->option('email')) {
            $this->sendEmailReport($this->option('email'));
        }

        $this->displaySummary();

        return 0;
    }

    /**
     * Check database security
     */
    private function checkDatabaseSecurity(): void
    {
        $this->info('Checking database security...');

        try {
            // Check for default passwords
            $defaultPasswords = User::where('password', bcrypt('password'))
                ->orWhere('password', bcrypt('123456'))
                ->orWhere('password', bcrypt('admin'))
                ->count();

            if ($defaultPasswords > 0) {
                $this->addIssue('critical', 'Database Security', 
                    "Found {$defaultPasswords} users with default passwords");
            }

            // Check for admin users without proper roles
            $adminUsers = User::whereHas('roles', function ($query) {
                $query->where('name', 'admin');
            })->count();

            if ($adminUsers === 0) {
                $this->addIssue('high', 'Database Security', 'No admin users found');
            } elseif ($adminUsers > 5) {
                $this->addIssue('medium', 'Database Security', 
                    "Large number of admin users ({$adminUsers}) detected");
            }

            // Check for orphaned licenses
            $orphanedLicenses = License::whereDoesntHave('user')->count();
            if ($orphanedLicenses > 0) {
                $this->addIssue('low', 'Database Security', 
                    "Found {$orphanedLicenses} orphaned licenses");
            }

            // Check for suspicious license activity
            $suspiciousLogs = LicenseLog::where('created_at', '>', now()->subDays(7))
                ->where('action', 'verification')
                ->groupBy('ip_address')
                ->havingRaw('COUNT(*) > 100')
                ->count();

            if ($suspiciousLogs > 0) {
                $this->addIssue('medium', 'Database Security', 
                    "Detected {$suspiciousLogs} IPs with high verification activity");
            }

        } catch (\Exception $e) {
            $this->addIssue('high', 'Database Security', 
                'Failed to check database security: ' . $e->getMessage());
        }
    }

    /**
     * Check file permissions
     */
    private function checkFilePermissions(): void
    {
        $this->info('Checking file permissions...');

        $criticalFiles = [
            '.env' => 0600,
            'config/' => 0755,
            'storage/' => 0755,
            'bootstrap/cache/' => 0755,
        ];

        foreach ($criticalFiles as $file => $expectedPerms) {
            $fullPath = base_path($file);
            
            if (File::exists($fullPath)) {
                $currentPerms = fileperms($fullPath) & 0777;
                
                if ($currentPerms !== $expectedPerms) {
                    $this->addIssue('high', 'File Permissions', 
                        "File {$file} has permissions " . decoct($currentPerms) . 
                        " but should have " . decoct($expectedPerms));
                }
            } else {
                $this->addIssue('medium', 'File Permissions', 
                    "Critical file {$file} not found");
            }
        }

        // Check for world-writable files
        $writableFiles = [];
        $this->checkWorldWritableFiles(base_path(), $writableFiles);
        
        if (!empty($writableFiles)) {
            $this->addIssue('medium', 'File Permissions', 
                'Found ' . count($writableFiles) . ' world-writable files');
        }
    }

    /**
     * Check configuration security
     */
    private function checkConfigurationSecurity(): void
    {
        $this->info('Checking configuration security...');

        // Check debug mode
        if (config('app.debug') === true) {
            $this->addIssue('critical', 'Configuration', 
                'Application is running in debug mode in production');
        }

        // Check encryption key
        if (empty(config('app.key'))) {
            $this->addIssue('critical', 'Configuration', 
                'Application encryption key is not set');
        }

        // Check database credentials
        if (config('database.default') === 'mysql') {
            $dbConfig = config('database.connections.mysql');
            
            if ($dbConfig['username'] === 'root') {
                $this->addIssue('high', 'Configuration', 
                    'Database is using root user');
            }
            
            if (empty($dbConfig['password'])) {
                $this->addIssue('critical', 'Configuration', 
                    'Database password is empty');
            }
        }

        // Check session configuration
        if (config('session.secure') === false && config('app.env') === 'production') {
            $this->addIssue('medium', 'Configuration', 
                'Session cookies are not marked as secure');
        }

        // Check HTTPS enforcement
        if (config('app.url') && !str_starts_with(config('app.url'), 'https://')) {
            $this->addIssue('medium', 'Configuration', 
                'Application URL is not using HTTPS');
        }
    }

    /**
     * Check user account security
     */
    private function checkUserAccountSecurity(): void
    {
        $this->info('Checking user account security...');

        // Check for users without email verification
        $unverifiedUsers = User::whereNull('email_verified_at')->count();
        if ($unverifiedUsers > 0) {
            $this->addIssue('low', 'User Security', 
                "Found {$unverifiedUsers} unverified user accounts");
        }

        // Check for inactive admin accounts
        $inactiveAdmins = User::whereHas('roles', function ($query) {
                $query->where('name', 'admin');
            })
            ->where('updated_at', '<', now()->subMonths(6))
            ->count();

        if ($inactiveAdmins > 0) {
            $this->addIssue('medium', 'User Security', 
                "Found {$inactiveAdmins} inactive admin accounts");
        }

        // Check for users with multiple failed login attempts
        // This would require implementing a failed login tracking system
    }

    /**
     * Check license system security
     */
    private function checkLicenseSystemSecurity(): void
    {
        $this->info('Checking license system security...');

        // Check for licenses without proper validation
        $invalidLicenses = License::where('status', 'active')
            ->where('license_expires_at', '<', now())
            ->count();

        if ($invalidLicenses > 0) {
            $this->addIssue('medium', 'License Security', 
                "Found {$invalidLicenses} expired but active licenses");
        }

        // Check for suspicious license patterns
        $duplicateKeys = DB::table('licenses')
            ->select('license_key')
            ->groupBy('license_key')
            ->havingRaw('COUNT(*) > 1')
            ->count();

        if ($duplicateKeys > 0) {
            $this->addIssue('high', 'License Security', 
                "Found {$duplicateKeys} duplicate license keys");
        }

        // Check Envato API configuration
        if (empty(config('license.envato.personal_token'))) {
            $this->addIssue('medium', 'License Security', 
                'Envato personal token is not configured');
        }
    }

    /**
     * Check log files
     */
    private function checkLogFiles(): void
    {
        $this->info('Checking log files...');

        $logPath = storage_path('logs');
        
        if (File::exists($logPath)) {
            $logFiles = File::files($logPath);
            $totalSize = 0;
            
            foreach ($logFiles as $file) {
                $totalSize += File::size($file);
            }
            
            // Check if logs are too large (>100MB)
            if ($totalSize > 100 * 1024 * 1024) {
                $this->addIssue('low', 'Log Files', 
                    'Log files are consuming ' . round($totalSize / 1024 / 1024, 2) . 'MB of space');
            }
            
            // Check for publicly accessible log files
            foreach ($logFiles as $file) {
                if (is_readable($file) && fileperms($file) & 0004) {
                    $this->addIssue('medium', 'Log Files', 
                        'Log file ' . basename($file) . ' is world-readable');
                }
            }
        }
    }

    /**
     * Check dependencies
     */
    private function checkDependencies(): void
    {
        $this->info('Checking dependencies...');

        // Check if composer.lock exists
        if (!File::exists(base_path('composer.lock'))) {
            $this->addIssue('medium', 'Dependencies', 
                'composer.lock file is missing');
        }

        // Check for development dependencies in production
        if (config('app.env') === 'production') {
            $composerJson = json_decode(File::get(base_path('composer.json')), true);
            
            if (isset($composerJson['require-dev']) && !empty($composerJson['require-dev'])) {
                // This is a simplified check - in practice, you'd need to check if dev deps are actually installed
                $this->addIssue('low', 'Dependencies', 
                    'Development dependencies might be installed in production');
            }
        }
    }

    /**
     * Check environment security
     */
    private function checkEnvironmentSecurity(): void
    {
        $this->info('Checking environment security...');

        // Check PHP version
        if (version_compare(PHP_VERSION, '8.1.0', '<')) {
            $this->addIssue('high', 'Environment', 
                'PHP version ' . PHP_VERSION . ' is outdated and may have security vulnerabilities');
        }

        // Check for dangerous PHP functions
        $dangerousFunctions = ['eval', 'exec', 'system', 'shell_exec', 'passthru'];
        foreach ($dangerousFunctions as $function) {
            if (function_exists($function)) {
                $this->addIssue('medium', 'Environment', 
                    "Dangerous PHP function '{$function}' is available");
            }
        }

        // Check server software
        $serverSoftware = $_SERVER['SERVER_SOFTWARE'] ?? 'Unknown';
        if (stripos($serverSoftware, 'apache') !== false) {
            // Check for .htaccess files
            if (!File::exists(public_path('.htaccess'))) {
                $this->addIssue('medium', 'Environment', 
                    'Missing .htaccess file for Apache configuration');
            }
        }
    }

    /**
     * Add security issue to the list
     */
    private function addIssue(string $severity, string $category, string $description): void
    {
        $this->issues[] = [
            'severity' => $severity,
            'category' => $category,
            'description' => $description,
            'timestamp' => now()->toISOString(),
        ];

        $color = match($severity) {
            'critical' => 'error',
            'high' => 'error',
            'medium' => 'warn',
            'low' => 'info',
            default => 'info'
        };

        $this->$color("[{$severity}] {$category}: {$description}");
    }

    /**
     * Check for world-writable files recursively
     */
    private function checkWorldWritableFiles(string $directory, array &$writableFiles): void
    {
        $files = File::allFiles($directory);
        
        foreach ($files as $file) {
            if (is_writable($file) && (fileperms($file) & 0002)) {
                $writableFiles[] = $file->getRelativePathname();
            }
        }
    }

    /**
     * Generate security report
     */
    private function generateSecurityReport(): void
    {
        $this->info('Generating security report...');

        $report = [
            'audit_date' => now()->toISOString(),
            'total_issues' => count($this->issues),
            'issues_by_severity' => [
                'critical' => count(array_filter($this->issues, fn($i) => $i['severity'] === 'critical')),
                'high' => count(array_filter($this->issues, fn($i) => $i['severity'] === 'high')),
                'medium' => count(array_filter($this->issues, fn($i) => $i['severity'] === 'medium')),
                'low' => count(array_filter($this->issues, fn($i) => $i['severity'] === 'low')),
            ],
            'issues' => $this->issues,
        ];

        $reportPath = storage_path('logs/security-audit-' . now()->format('Y-m-d-H-i-s') . '.json');
        File::put($reportPath, json_encode($report, JSON_PRETTY_PRINT));

        $this->info("Security report saved to: {$reportPath}");
    }

    /**
     * Attempt to fix security issues automatically
     */
    private function fixSecurityIssues(): void
    {
        $this->info('Attempting to fix security issues...');

        $fixedCount = 0;

        foreach ($this->issues as $issue) {
            if ($this->canAutoFix($issue)) {
                if ($this->autoFix($issue)) {
                    $fixedCount++;
                    $this->info("Fixed: {$issue['description']}");
                }
            }
        }

        $this->info("Automatically fixed {$fixedCount} security issues.");
    }

    /**
     * Check if an issue can be automatically fixed
     */
    private function canAutoFix(array $issue): bool
    {
        // Define which types of issues can be automatically fixed
        $autoFixablePatterns = [
            'Log files are consuming',
            'world-writable files',
            'Missing .htaccess file',
        ];

        foreach ($autoFixablePatterns as $pattern) {
            if (str_contains($issue['description'], $pattern)) {
                return true;
            }
        }

        return false;
    }

    /**
     * Automatically fix a security issue
     */
    private function autoFix(array $issue): bool
    {
        try {
            if (str_contains($issue['description'], 'Log files are consuming')) {
                // Rotate log files
                $this->call('log:clear');
                return true;
            }

            if (str_contains($issue['description'], 'Missing .htaccess file')) {
                // Create basic .htaccess file
                $htaccessContent = "RewriteEngine On\nRewriteRule ^(.*)$ index.php [QSA,L]\n";
                File::put(public_path('.htaccess'), $htaccessContent);
                return true;
            }

            // Add more auto-fix logic as needed

        } catch (\Exception $e) {
            $this->error("Failed to fix issue: {$e->getMessage()}");
            return false;
        }

        return false;
    }

    /**
     * Send email report
     */
    private function sendEmailReport(string $email): void
    {
        $this->info("Sending security report to: {$email}");
        
        // Implementation would depend on your mail configuration
        // This is a placeholder for the actual email sending logic
        
        $this->info("Email report sent successfully.");
    }

    /**
     * Display audit summary
     */
    private function displaySummary(): void
    {
        $this->info('Security audit completed.');
        
        $criticalCount = count(array_filter($this->issues, fn($i) => $i['severity'] === 'critical'));
        $highCount = count(array_filter($this->issues, fn($i) => $i['severity'] === 'high'));
        $mediumCount = count(array_filter($this->issues, fn($i) => $i['severity'] === 'medium'));
        $lowCount = count(array_filter($this->issues, fn($i) => $i['severity'] === 'low'));

        $this->table(
            ['Severity', 'Count'],
            [
                ['Critical', $criticalCount],
                ['High', $highCount],
                ['Medium', $mediumCount],
                ['Low', $lowCount],
                ['Total', count($this->issues)],
            ]
        );

        if ($criticalCount > 0) {
            $this->error('Critical security issues found! Please address them immediately.');
        } elseif ($highCount > 0) {
            $this->warn('High severity security issues found. Please review and fix.');
        } else {
            $this->info('No critical or high severity issues found.');
        }

        // Log the audit
    }
}
