<?php

declare(strict_types=1);

namespace App\Framework\Database\Migration\Services;

use App\Framework\Database\Migration\Migration;

final readonly class MigrationErrorAnalyzer
{
    public function generateMigrationRecoveryHints(Migration $migration, \Throwable $error, array $executedMigrations): array
    {
        $hints = [];

        // Analyze error type and provide specific guidance
        if ($this->isDatabaseConnectionError($error)) {
            $hints[] = 'Database connection lost - check database server status';
            $hints[] = 'Verify database credentials and network connectivity';
            $hints[] = 'Consider retrying after connection is restored';
        }

        if ($this->isPermissionError($error)) {
            $hints[] = 'Database permission denied - verify user privileges';
            $hints[] = 'Ensure migration user has CREATE, ALTER, DROP permissions';
            $hints[] = 'Check if migration user can access the target database';
        }

        if ($this->isSyntaxError($error)) {
            $hints[] = 'SQL syntax error detected - review migration SQL';
            $hints[] = 'Check for database-specific syntax requirements';
            $hints[] = 'Validate migration against target database version';
        }

        if ($this->isDiskSpaceError($error)) {
            $hints[] = 'Insufficient disk space - free up storage';
            $hints[] = 'Consider running migrations in smaller batches';
            $hints[] = 'Check database temp directory space';
        }

        if ($this->isConstraintViolation($error)) {
            $hints[] = 'Database constraint violation - check data integrity';
            $hints[] = 'Review foreign key relationships and unique constraints';
            $hints[] = 'Consider data cleanup before retry';
        }

        // General recovery recommendations
        if ($this->canRetryMigration($error)) {
            $hints[] = 'Migration can be safely retried after resolving the issue';
        }

        if ($this->isRollbackRecommended($error, $executedMigrations)) {
            $hints[] = 'Consider rolling back completed migrations before retry';
            $hints[] = sprintf('Rollback %d successfully completed migrations', count($executedMigrations));
        }

        if (empty($hints)) {
            $hints[] = 'Review migration code and database state manually';
            $hints[] = 'Check application logs for additional context';
        }

        return $hints;
    }

    public function generateRollbackRecoveryHints(Migration $migration, \Throwable $error, array $remainingRollbacks): array
    {
        $hints = [];

        if ($this->isDatabaseConnectionError($error)) {
            $hints[] = 'Database connection lost during rollback - check server status';
            $hints[] = 'Rollback operation can be continued after connection restore';
        }

        if ($this->isDataLossRisk($error)) {
            $hints[] = 'Rollback may cause data loss - backup before continuing';
            $hints[] = 'Review rollback SQL for irreversible operations';
        }

        if ($this->canContinueRollback($remainingRollbacks)) {
            $hints[] = sprintf('Can continue rollback for %d remaining migrations', count($remainingRollbacks));
        } else {
            $hints[] = 'Manual intervention required - some rollbacks may be unsafe';
            $hints[] = 'Review each remaining migration rollback individually';
        }

        if ($this->requiresManualIntervention($error)) {
            $hints[] = 'Automatic rollback failed - manual database intervention required';
            $hints[] = 'Check database state and manually reverse changes if needed';
        }

        return $hints;
    }

    public function analyzeMigrationContext(Migration $migration, string $version, \Throwable $error): array
    {
        return [
            'migration_info' => [
                'version' => $version,
                'description' => $migration->getDescription(),
                'class' => get_class($migration),
            ],
            'error_analysis' => [
                'type' => get_class($error),
                'message' => $error->getMessage(),
                'code' => $error->getCode(),
                'file' => $error->getFile(),
                'line' => $error->getLine(),
                'is_retryable' => $this->canRetryMigration($error),
                'is_connection_error' => $this->isDatabaseConnectionError($error),
                'is_permission_error' => $this->isPermissionError($error),
                'is_syntax_error' => $this->isSyntaxError($error),
                'requires_manual_intervention' => $this->requiresManualIntervention($error),
            ],
            'recovery_recommendations' => $this->generateMigrationRecoveryHints($migration, $error, []),
            'database_state' => $this->getDatabaseStateDebugInfo(),
            'system_info' => [
                'memory_usage' => memory_get_usage(true),
                'memory_peak' => memory_get_peak_usage(true),
                'time_limit' => ini_get('max_execution_time'),
                'php_version' => PHP_VERSION,
            ],
        ];
    }

    public function canRetryMigration(\Throwable $error): bool
    {
        // These errors are typically retryable
        return $this->isDatabaseConnectionError($error) ||
               $this->isTimeoutError($error) ||
               $this->isDiskSpaceError($error) ||
               $this->isTemporaryLockError($error);
    }

    public function isRollbackRecommended(\Throwable $error, array $executedMigrations): bool
    {
        // Recommend rollback for serious structural errors
        return ! empty($executedMigrations) && (
            $this->isConstraintViolation($error) ||
            $this->isSyntaxError($error) ||
            $this->isPermissionError($error)
        );
    }

    public function canContinueRollback(array $remainingRollbacks): bool
    {
        // Can continue rollback if there are migrations to roll back
        return ! empty($remainingRollbacks);
    }

    public function requiresManualIntervention(\Throwable $error): bool
    {
        // These errors typically require manual database intervention
        return $this->isCorruptionError($error) ||
               $this->isDataIntegrityError($error) ||
               $this->isStructuralError($error);
    }

    private function isDatabaseConnectionError(\Throwable $error): bool
    {
        return $this->containsAny(
            $error->getMessage(),
            'connection',
            'server has gone away',
            'lost connection',
            'timeout'
        );
    }

    private function isPermissionError(\Throwable $error): bool
    {
        return $this->containsAny(
            $error->getMessage(),
            'access denied',
            'permission',
            'privilege',
            'unauthorized'
        );
    }

    private function isSyntaxError(\Throwable $error): bool
    {
        return $this->containsAny(
            $error->getMessage(),
            'syntax error',
            'sql syntax',
            'invalid sql',
            'parse error'
        );
    }

    private function isDiskSpaceError(\Throwable $error): bool
    {
        return $this->containsAny(
            $error->getMessage(),
            'disk full',
            'no space',
            'disk space',
            'storage full'
        );
    }

    private function isConstraintViolation(\Throwable $error): bool
    {
        return $this->containsAny(
            $error->getMessage(),
            'constraint',
            'foreign key',
            'unique',
            'duplicate'
        );
    }

    private function isTimeoutError(\Throwable $error): bool
    {
        return $this->containsAny(
            $error->getMessage(),
            'timeout',
            'time limit',
            'execution time'
        );
    }

    private function isTemporaryLockError(\Throwable $error): bool
    {
        return $this->containsAny(
            $error->getMessage(),
            'lock',
            'deadlock',
            'table is locked'
        );
    }

    private function isDataLossRisk(\Throwable $error): bool
    {
        return $this->containsAny(
            $error->getMessage(),
            'drop',
            'delete',
            'truncate'
        );
    }

    private function isCorruptionError(\Throwable $error): bool
    {
        return $this->containsAny(
            $error->getMessage(),
            'corrupt',
            'damaged',
            'invalid header'
        );
    }

    private function isDataIntegrityError(\Throwable $error): bool
    {
        return $this->containsAny(
            $error->getMessage(),
            'integrity',
            'check constraint',
            'data validation'
        );
    }

    private function isStructuralError(\Throwable $error): bool
    {
        return $this->containsAny(
            $error->getMessage(),
            'table structure',
            'schema',
            'column does not exist'
        );
    }

    /**
     * Check if a string contains any of the given matches (case-insensitive)
     */
    private function containsAny(string $haystack, string ...$needles): bool
    {
        $lowerHaystack = strtolower($haystack);

        foreach ($needles as $needle) {
            if (str_contains($lowerHaystack, strtolower($needle))) {
                return true;
            }
        }

        return false;
    }

    private function getDatabaseStateDebugInfo(): array
    {
        return [
            'active_connections' => 'unknown', // Would need platform-specific queries
            'transaction_state' => 'unknown',
            'lock_status' => 'unknown',
            'note' => 'Database state debug info requires platform-specific implementation',
        ];
    }
}
