<?php

declare(strict_types=1);

namespace App\Services;
use App\Core\Database;
use PDO;
use Throwable;

final class MaintenanceService
{
    private array $lastChecks = [];
    private BackupService $backupService;
    private RestoreService $restoreService;

    public function __construct()
    {
        $this->backupService = new BackupService();
        $this->restoreService = new RestoreService($this->backupService);
    }

    public function runChecks(): array
    {
        $checks = [];

        $checks[] = $this->checkPhpVersion();
        $checks[] = $this->checkExtension('pdo_mysql', 'Extension PHP pdo_mysql');
        $checks[] = $this->checkExtension('mbstring', 'Extension PHP mbstring');
        $checks[] = $this->checkExtension('zip', 'Extension PHP zip — nécessaire pour les fichiers Excel .xlsx');
        $checks[] = $this->checkExtension('xml', 'Extension PHP xml — utile pour PhpSpreadsheet');

        $checks[] = $this->checkDirectory('storage/imports', 'Dossier imports');

        foreach ($this->requiredTables() as $table => $label) {
            $checks[] = $this->checkTable($table, $label);
        }

        foreach ($this->requiredColumns() as $table => $columns) {
            foreach ($columns as $column => $label) {
                $checks[] = $this->checkColumn($table, $column, $label);
            }
        }


        $this->lastChecks = $checks;
        return $checks;
    }

public function summary(): array
{
    $checks = $this->lastChecks ?: $this->runChecks();

    $ok = count(array_filter(
        $checks,
        fn (array $check): bool => $check['status'] === 'ok'
    ));

    $warning = count(array_filter(
        $checks,
        fn (array $check): bool => $check['status'] === 'warning'
    ));

    $error = count(array_filter(
        $checks,
        fn (array $check): bool => $check['status'] === 'error'
    ));

    $total = count($checks);

    $score = $total > 0
        ? (int) round((($ok + ($warning * 0.5)) / $total) * 100)
        : 0;

    if ($error > 0 || $score < 70) {
        $healthStatus = 'critical';
        $healthLabel = 'Installation non conforme';
        $healthClass = 'danger';
    } elseif ($warning > 0 || $score < 95) {
        $healthStatus = 'warning';
        $healthLabel = 'Points à surveiller';
        $healthClass = 'warning';
    } else {
        $healthStatus = 'ok';
        $healthLabel = 'Installation conforme';
        $healthClass = 'success';
    }

    return [
        'ok' => $ok,
        'warning' => $warning,
        'error' => $error,
        'total' => $total,
        'score' => $score,
        'health_status' => $healthStatus,
        'health_label' => $healthLabel,
        'health_class' => $healthClass,
    ];
}
    public function createDatabaseBackup(): array
    {
        return $this->backupService->createDatabaseBackup();
    }
    public function createCompleteBackup(): array
    {
        return $this->backupService->createCompleteBackup();
    }
    public function listDatabaseBackups(): array
    {
        return $this->backupService->listDatabaseBackups();
    }
    public function deleteDatabaseBackup(string $fileName): void
    {
        $this->backupService->deleteDatabaseBackup($fileName);
    }

public function restoreDatabaseBackup(string $fileName): array
{
    return $this->restoreService->restore($fileName);
}

public function getBackupStatus(): array
{
    return $this->backupService->backupStatus();
}

public function getBackupMetrics(): array
{
    return $this->backupService->backupMetrics();
}

    public function getDatabaseBackupPath(string $fileName): string
    {
        return $this->backupService->getDatabaseBackupPath($fileName);
    }

public function getSystemInformation(): array
{
    $pdo = Database::connection();

    try {
        $mysqlVersion = (string)$pdo
            ->query('SELECT VERSION()')
            ->fetchColumn();
    } catch (\Throwable) {
        $mysqlVersion = 'Indisponible';
    }

    $serverSoftware = (string)($_SERVER['SERVER_SOFTWARE'] ?? '');

    if ($serverSoftware === '' && function_exists('apache_get_version')) {
        $serverSoftware = (string)apache_get_version();
    }

    return [
        'application' => 'Darlink ERP Phoenix',
        'version' => '2026.2.1',
        'php_version' => PHP_VERSION,
        'mysql_version' => $mysqlVersion,
        'server' => $serverSoftware !== ''
            ? $serverSoftware
            : 'Indisponible',
        'operating_system' => PHP_OS_FAMILY . ' — ' . PHP_OS,
        'timezone' => date_default_timezone_get(),
        'memory_limit' => (string)ini_get('memory_limit'),
        'upload_max_filesize' => (string)ini_get('upload_max_filesize'),
        'post_max_size' => (string)ini_get('post_max_size'),
        'max_execution_time' => (string)ini_get('max_execution_time') . ' s',
    ];
}

public function getDatabaseInformation(): array
{
    $pdo = Database::connection();

    $databaseName = (string)$pdo
        ->query('SELECT DATABASE()')
        ->fetchColumn();

    $tableCountStatement = $pdo->prepare(
        "SELECT COUNT(*)
         FROM information_schema.tables
         WHERE table_schema = :database_name
           AND table_type = 'BASE TABLE'"
    );

    $tableCountStatement->execute([
        'database_name' => $databaseName,
    ]);

    $schemaStatement = $pdo->prepare(
        "SELECT default_character_set_name,
                default_collation_name
         FROM information_schema.schemata
         WHERE schema_name = :database_name
         LIMIT 1"
    );

    $schemaStatement->execute([
        'database_name' => $databaseName,
    ]);

    $schema = $schemaStatement->fetch(PDO::FETCH_ASSOC) ?: [];

    $backups = $this->listDatabaseBackups();
    $latestBackup = $backups[0] ?? null;

    return [
        'name' => $databaseName,
        'table_count' => (int)$tableCountStatement->fetchColumn(),
        'charset' => (string)(
            $schema['default_character_set_name'] ?? 'Indisponible'
        ),
        'collation' => (string)(
            $schema['default_collation_name'] ?? 'Indisponible'
        ),
        'backup_count' => count($backups),
        'latest_backup' => $latestBackup['created_at'] ?? null,
        'latest_backup_file' => $latestBackup['file_name'] ?? null,
    ];
}

public function getStatistics(): array
{
    return [
        [
            'label' => 'Clients',
            'icon' => 'bi-people',
            'value' => $this->safeCount('clients'),
        ],
        [
            'label' => 'Fournisseurs',
            'icon' => 'bi-truck',
            'value' => $this->safeCount('fournisseurs'),
        ],
        [
            'label' => 'Articles',
            'icon' => 'bi-box-seam',
            'value' => $this->safeCount('articles'),
        ],
        [
            'label' => 'Devis',
            'icon' => 'bi-file-earmark-text',
            'value' => $this->safeCount('devis'),
        ],
        [
            'label' => 'Bons de livraison',
            'icon' => 'bi-box-arrow-right',
            'value' => $this->safeCount('bons_livraison'),
        ],
        [
            'label' => 'Factures',
            'icon' => 'bi-receipt',
            'value' => $this->safeCount('factures'),
        ],
        [
            'label' => 'Avoirs',
            'icon' => 'bi-arrow-counterclockwise',
            'value' => $this->safeCount('avoirs'),
        ],
        [
            'label' => 'Encaissements',
            'icon' => 'bi-cash-coin',
            'value' => $this->safeCount('encaissements'),
        ],
        [
            'label' => 'Mouvements de stock',
            'icon' => 'bi-arrow-left-right',
            'value' => $this->safeCount('mouvements_stock'),
        ],
    ];
}

    private function requiredTables(): array
    {
        return [
            'types_clients' => 'Table types_clients',
            'clients' => 'Table clients',
            'clients_historique' => 'Table clients_historique',
            'familles_articles' => 'Table familles_articles',
            'marques_articles' => 'Table marques_articles',
            'articles' => 'Table articles',
            'stocks' => 'Table stocks',
            'mouvements_stock' => 'Table mouvements_stock',
            'devis' => 'Table devis',
            'devis_lignes' => 'Table devis_lignes',
            'bons_livraison' => 'Table bons_livraison',
            'bons_livraison_lignes' => 'Table bons_livraison_lignes',
            'factures' => 'Table factures',
            'factures_lignes' => 'Table factures_lignes',
        ];
    }

    private function requiredColumns(): array
    {
        return [
            'clients' => [
                'id' => 'clients.id',
                'type_client_id' => 'clients.type_client_id',
                'code_client' => 'clients.code_client',
                'raison_sociale' => 'clients.raison_sociale',
                'contact' => 'clients.contact',
                'telephone' => 'clients.telephone',
                'mobile' => 'clients.mobile',
                'email' => 'clients.email',
                'ville' => 'clients.ville',
                'actif' => 'clients.actif',
            ],
            'articles' => [
                'id' => 'articles.id',
                'reference' => 'articles.reference',
                'designation' => 'articles.designation',
                'prix_achat_ht' => 'articles.prix_achat_ht',
                'coefficient' => 'articles.coefficient',
                'prix_vente_ht' => 'articles.prix_vente_ht',
                'taux_tva' => 'articles.taux_tva',
                'prix_vente_ttc' => 'articles.prix_vente_ttc',
                'marge_dh' => 'articles.marge_dh',
                'marge_pourcentage' => 'articles.marge_pourcentage',
                'conditionnement' => 'articles.conditionnement',
                'unite' => 'articles.unite',
                'stock_minimum' => 'articles.stock_minimum',
                'actif' => 'articles.actif',
            ],
            'stocks' => [
                'id' => 'stocks.id',
                'article_id' => 'stocks.article_id',
                'quantite' => 'stocks.quantite',
                'stock_minimum' => 'stocks.stock_minimum',
                'valeur_stock' => 'stocks.valeur_stock',
                'date_maj' => 'stocks.date_maj',
            ],
            'mouvements_stock' => [
                'id' => 'mouvements_stock.id',
                'article_id' => 'mouvements_stock.article_id',
                'type_mouvement' => 'mouvements_stock.type_mouvement',
                'quantite' => 'mouvements_stock.quantite',
                'prix_achat_ht' => 'mouvements_stock.prix_achat_ht',
                'date_mouvement' => 'mouvements_stock.date_mouvement',
            ],
            'factures' => [
                'id' => 'factures.id',
                'client_id' => 'factures.client_id',
                'bon_livraison_id' => 'factures.bon_livraison_id',
                'numero' => 'factures.numero',
                'date_facture' => 'factures.date_facture',
                'date_echeance' => 'factures.date_echeance',
                'statut' => 'factures.statut',
                'total_ht' => 'factures.total_ht',
                'total_tva' => 'factures.total_tva',
                'total_ttc' => 'factures.total_ttc',
                'montant_regle' => 'factures.montant_regle',
                'reste_du' => 'factures.reste_du',
            ],
            'factures_lignes' => [
                'id' => 'factures_lignes.id',
                'facture_id' => 'factures_lignes.facture_id',
                'article_id' => 'factures_lignes.article_id',
                'designation' => 'factures_lignes.designation',
                'quantite' => 'factures_lignes.quantite',
                'total_ht' => 'factures_lignes.total_ht',
                'total_ttc' => 'factures_lignes.total_ttc',
            ],
        ];
    }

    private function checkPhpVersion(): array
    {
        $version = PHP_VERSION;
        $ok = version_compare($version, '8.3.0', '>=');

        return $this->result(
            $ok ? 'ok' : 'warning',
            'Version PHP',
            'Version détectée : ' . $version,
            $ok ? 'PHP 8.3 ou supérieur détecté.' : 'PHP 8.3 minimum recommandé.'
        );
    }

    private function checkExtension(string $extension, string $label): array
    {
        $loaded = extension_loaded($extension);

        return $this->result(
            $loaded ? 'ok' : 'warning',
            $label,
            $loaded ? 'Installée' : 'Non installée',
            $loaded ? 'Extension disponible.' : 'Activez cette extension dans WAMP si une fonctionnalité en dépend.'
        );
    }

    private function checkDirectory(string $relativePath, string $label): array
    {
        $path = dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . str_replace('/', DIRECTORY_SEPARATOR, $relativePath);

        if (!is_dir($path)) {
            return $this->result('warning', $label, $relativePath, 'Le dossier n’existe pas encore. Il sera créé automatiquement au besoin.');
        }

        if (!is_writable($path)) {
            return $this->result('warning', $label, $relativePath, 'Le dossier existe mais n’est pas accessible en écriture.');
        }

        return $this->result('ok', $label, $relativePath, 'Dossier présent et accessible.');
    }

    private function checkTable(string $table, string $label): array
    {
        try {
            $exists = $this->tableExists($table);
            return $this->result(
                $exists ? 'ok' : 'error',
                $label,
                $table,
                $exists ? 'Table présente.' : 'Table manquante. Importez le script SQL correspondant.'
            );
        } catch (Throwable $exception) {
            return $this->result('error', $label, $table, 'Erreur de vérification : ' . $exception->getMessage());
        }
    }

    private function checkFutureTable(string $table, string $label): array
    {
        try {
            $exists = $this->tableExists($table);
            return $this->result(
                $exists ? 'ok' : 'warning',
                $label,
                $table,
                $exists ? 'Table présente.' : 'Table pas encore créée. Normal avant le Sprint 3.2.'
            );
        } catch (Throwable $exception) {
            return $this->result('warning', $label, $table, 'Vérification impossible : ' . $exception->getMessage());
        }
    }

    private function checkColumn(string $table, string $column, string $label): array
    {
        try {
            if (!$this->tableExists($table)) {
                return $this->result('error', $label, $table . '.' . $column, 'Impossible de vérifier la colonne : table manquante.');
            }

            $exists = $this->columnExists($table, $column);
            return $this->result(
                $exists ? 'ok' : 'error',
                $label,
                $table . '.' . $column,
                $exists ? 'Colonne présente.' : 'Colonne manquante. La base n’est pas alignée avec le code.'
            );
        } catch (Throwable $exception) {
            return $this->result('error', $label, $table . '.' . $column, 'Erreur de vérification : ' . $exception->getMessage());
        }
    }

    private function tableExists(string $table): bool
    {
        $pdo = Database::connection();
        $stmt = $pdo->prepare('SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = DATABASE() AND table_name = :table');
        $stmt->execute(['table' => $table]);
        return (int) $stmt->fetchColumn() > 0;
    }

    private function columnExists(string $table, string $column): bool
    {
        $pdo = Database::connection();
        $stmt = $pdo->prepare('SELECT COUNT(*) FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = :table AND column_name = :column');
        $stmt->execute([
            'table' => $table,
            'column' => $column,
        ]);
        return (int) $stmt->fetchColumn() > 0;
    }
private function safeCount(string $table): int
{
    if (!$this->tableExists($table)) {
        return 0;
    }

    $escapedTable = str_replace('`', '``', $table);

    try {
        return (int)Database::connection()
            ->query("SELECT COUNT(*) FROM `{$escapedTable}`")
            ->fetchColumn();
    } catch (\Throwable) {
        return 0;
    }
}
    private function result(string $status, string $label, string $value, string $message): array
    {
        return [
            'status' => $status,
            'label' => $label,
            'value' => $value,
            'message' => $message,
        ];
    }
}
