<?php

declare(strict_types=1);

namespace BotGuard\Database\Seeds;

use BotGuard\Core\Auth;
use BotGuard\Core\Database;

/**
 * Idempotent database seeder.
 *
 * Populates the default administrator, the detection module registry (from
 * config/detection.php), score thresholds, baseline settings and a default
 * redirect configuration. Safe to run repeatedly — every insert is guarded by
 * an existence check. Progress is echoed only under the CLI SAPI so the web
 * installer (which reuses the public seed methods) is not corrupted.
 */
final class DatabaseSeeder
{
    public function __construct(private Database $db)
    {
    }

    public function run(): void
    {
        $this->seedAdmin();
        $this->seedModules();
        $this->seedThresholds();
        $this->seedSettings();
    }

    /**
     * Create the default admin account (credentials from .env) plus a default
     * redirect configuration owned by that admin.
     */
    private function seedAdmin(): void
    {
        $email = strtolower((string) env('ADMIN_EMAIL', 'admin@example.com'));

        $this->createAdmin(
            (string) env('ADMIN_NAME', 'Site Administrator'),
            $email,
            (string) env('ADMIN_PASSWORD', 'ChangeMe!123'),
        );
    }

    /**
     * Create (if missing) an admin user + their default redirect config.
     * Reused by the web installer with wizard-supplied credentials.
     *
     * @return int The admin user id.
     */
    public function createAdmin(string $name, string $email, string $password, ?string $company = null): int
    {
        $email = strtolower(trim($email));
        $existing = $this->db->selectOne('SELECT id FROM `users` WHERE `email` = ?', [$email]);

        if ($existing !== null) {
            $userId = (int) $existing['id'];
        } else {
            $userId = $this->db->insert('users', [
                'name'     => $name,
                'email'    => $email,
                'password' => Auth::hash($password),
                'role'     => 'admin',
                'status'   => 'active',
                'company'  => $company,
                'api_key'  => str_random(32),
            ]);
            $this->say("  • Seeded admin user: {$email}");
        }

        $hasConfig = $this->db->selectOne('SELECT id FROM `redirect_configs` WHERE `user_id` = ?', [$userId]);
        if ($hasConfig === null) {
            $base = rtrim((string) config('app.url', 'http://localhost:8000'), '/');
            $this->db->insert('redirect_configs', [
                'user_id'       => $userId,
                'name'          => 'Default',
                'primary_url'   => $base . '/success',
                'alternate_url' => $base . '/blocked',
                'block_message' => 'Automated traffic detected. If you believe this is an error, please try again.',
                'is_default'    => 1,
            ]);
            $this->say('  • Seeded default redirect config');
        }

        return $userId;
    }

    /**
     * Upsert the module registry from config, preserving admin-edited values.
     */
    public function seedModules(): void
    {
        $modules = (array) config('detection.modules', []);
        $order = 0;

        foreach ($modules as $key => $module) {
            $order += 10;
            $exists = $this->db->selectOne('SELECT id FROM `module_configs` WHERE `module_key` = ?', [$key]);
            if ($exists !== null) {
                continue;
            }

            $this->db->insert('module_configs', [
                'module_key' => $key,
                'label'      => (string) ($module['label'] ?? $key),
                'class'      => (string) ($module['class'] ?? ''),
                'group'      => (string) ($module['group'] ?? 'server'),
                'enabled'    => ! empty($module['enabled']) ? 1 : 0,
                'weight'     => (float) ($module['weight'] ?? 10),
                'settings'   => json_encode($module['settings'] ?? new \stdClass()),
                'sort_order' => $order,
            ]);
        }
        $this->say('  • Seeded ' . count($modules) . ' detection modules');
    }

    /**
     * Seed the three score bands and their default actions.
     */
    public function seedThresholds(): void
    {
        $human = (int) config('detection.thresholds.human', 80);
        $susp = (int) config('detection.thresholds.suspicious', 60);
        $actions = (array) config('detection.actions', []);

        $bands = [
            ['band' => 'human',      'min_score' => $human, 'action' => $actions['human'] ?? 'redirect_primary'],
            ['band' => 'suspicious', 'min_score' => $susp,  'action' => $actions['suspicious'] ?? 'redirect_alternate'],
            ['band' => 'bot',        'min_score' => 0,      'action' => $actions['bot'] ?? 'block'],
        ];

        foreach ($bands as $band) {
            $exists = $this->db->selectOne('SELECT id FROM `thresholds` WHERE `band` = ?', [$band['band']]);
            if ($exists === null) {
                $this->db->insert('thresholds', $band);
            }
        }
        $this->say("  • Seeded score thresholds (human>={$human}, suspicious>={$susp})");
    }

    /**
     * Seed baseline settings rows.
     */
    public function seedSettings(): void
    {
        $defaults = [
            ['key' => 'captcha_provider', 'value' => (string) config('captcha.provider', 'puzzle'), 'type' => 'string', 'group' => 'captcha'],
            ['key' => 'brand_name',       'value' => (string) config('app.name', 'BotGuard'),       'type' => 'string', 'group' => 'branding'],
            ['key' => 'log_retention_days', 'value' => '90',                                         'type' => 'int',    'group' => 'general'],
        ];

        foreach ($defaults as $row) {
            $exists = $this->db->selectOne('SELECT id FROM `settings` WHERE `key` = ?', [$row['key']]);
            if ($exists === null) {
                $this->db->insert('settings', $row);
            }
        }
        $this->say('  • Seeded baseline settings');
    }

    private function say(string $message): void
    {
        if (PHP_SAPI === 'cli') {
            echo $message . "\n";
        }
    }
}
