<?php

declare(strict_types=1);

namespace App\Modules\Admin\Application\Services;

use PDO;
use RuntimeException;
use Throwable;

final class WipeDataService
{
    public function __construct(private PDO $pdo)
    {
    }

    /** @return array<int,string> */
    public function preserveTables(): array
    {
        return [
            'company_profile',
            'app_settings',
            'communication_settings',
            'users',
            'roles',
            'permissions',
            'role_permissions',
        ];
    }

    /** @return array<int,string> */
    public function transactionModeKeepTables(): array
    {
        return [
            'items', 'item_units', 'categories', 'units', 'warehouses',
            'parties', 'cashboxes', 'bank_accounts', 'expense_categories', 'expense_items',
        ];
    }

    public function verifyCurrentUserPassword(int $userId, string $password): void
    {
        if ($userId <= 0) {
            throw new RuntimeException('المستخدم الحالي غير صالح.');
        }

        $st = $this->pdo->prepare('SELECT password_hash FROM users WHERE id=? LIMIT 1');
        $st->execute([$userId]);
        $hash = (string) ($st->fetchColumn() ?: '');
        if ($hash === '' || !password_verify($password, $hash)) {
            throw new RuntimeException('كلمة المرور غير صحيحة');
        }
    }

    /** @return array<int,string> */
    public function listTables(): array
    {
        try {
            return $this->pdo->query('SHOW TABLES')->fetchAll(PDO::FETCH_COLUMN) ?: [];
        } catch (Throwable $e) {
            throw new RuntimeException('تعذر قراءة جداول قاعدة البيانات: ' . $e->getMessage(), 0, $e);
        }
    }

    /** @return array<int,string> */
    public function resolveKeepTables(string $mode): array
    {
        $keepTables = $this->preserveTables();
        if ($mode === 'transactions') {
            $keepTables = array_values(array_unique(array_merge($keepTables, $this->transactionModeKeepTables())));
        }
        return $keepTables;
    }

    /** @param array<int,string> $allTables @param array<int,string> $keepTables @return array<int,string> */
    public function resolveTablesToWipe(array $allTables, array $keepTables): array
    {
        $tablesToWipe = [];
        foreach ($allTables as $table) {
            $table = (string) $table;
            if (!in_array($table, $keepTables, true)) {
                $tablesToWipe[] = $table;
            }
        }
        return $tablesToWipe;
    }

    /** @return array{mode:string,kept_tables:array<int,string>,wiped_tables:array<int,string>,errors:array<int,string>} */
    public function wipe(string $mode, int $userId, string $password): array
    {
        $mode = $mode === 'transactions' ? 'transactions' : 'full_reset';
        $this->verifyCurrentUserPassword($userId, $password);

        $keepTables = $this->resolveKeepTables($mode);
        $allTables = $this->listTables();
        $tablesToWipe = $this->resolveTablesToWipe($allTables, $keepTables);

        $errors = [];
        $wipedTables = [];

        try {
            $this->pdo->exec('SET FOREIGN_KEY_CHECKS=0');

            foreach ($tablesToWipe as $table) {
                try {
                    $identifier = sql_escape_identifier($table);
                    $this->pdo->exec('DELETE FROM ' . $identifier);
                    try {
                        $this->pdo->exec('ALTER TABLE ' . $identifier . ' AUTO_INCREMENT=1');
                    } catch (Throwable) {
                    }
                    $wipedTables[] = $table;
                } catch (Throwable $e) {
                    $errors[] = $table . ': ' . $e->getMessage();
                }
            }
        } finally {
            try {
                $this->pdo->exec('SET FOREIGN_KEY_CHECKS=1');
            } catch (Throwable) {
            }
        }

        try {
            audit_log('DATA_WIPE', 'system', null, [
                'mode' => $mode,
                'kept_tables' => $keepTables,
                'wiped_tables_count' => count($wipedTables),
                'errors' => $errors ?: null,
            ]);
        } catch (Throwable) {
        }

        return [
            'mode' => $mode,
            'kept_tables' => $keepTables,
            'wiped_tables' => $wipedTables,
            'errors' => $errors,
        ];
    }
}
