<?php

use App\Models\Administrador;
use App\Models\Formulario;
use App\Models\LogSeguridad;
use Database\Seeders\BulkDataSeeder;
use Illuminate\Foundation\Inspiring;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Schedule;
use Illuminate\Support\Facades\Storage;

Artisan::command('inspire', function () {
    $this->comment(Inspiring::quote());
})->purpose('Display an inspiring quote');

Artisan::command('logs:purge-old-security', function () {
    $fechaLimite = now()->subMonths(2);

    $eliminados = LogSeguridad::query()
        ->where('fechahora', '<', $fechaLimite)
        ->delete();

    $this->info('Logs eliminados: ' . $eliminados);
})->purpose('Elimina logs de seguridad con más de 2 meses de antigüedad');

Artisan::command('seed:bulk {--modo=masivo} {--clientes=} {--profesionales=} {--servicios=} {--profesiones=} {--fresh}', function () {
    $presets = [
        'rapido' => ['clientes' => 100,  'profesionales' => 20,  'servicios' => 10, 'profesiones' => 2],
        'medio'  => ['clientes' => 500,  'profesionales' => 50,  'servicios' => 20, 'profesiones' => 3],
        'masivo' => ['clientes' => 1000, 'profesionales' => 100, 'servicios' => 30, 'profesiones' => 4],
    ];

    $modo = strtolower((string) $this->option('modo'));

    if (!array_key_exists($modo, $presets)) {
        $this->error('Modo invalido. Usa: rapido, medio o masivo.');

        return 1;
    }

    $clientes      = max(0, (int) ($this->option('clientes')      ?? $presets[$modo]['clientes']));
    $profesionales = max(0, (int) ($this->option('profesionales') ?? $presets[$modo]['profesionales']));
    $servicios     = max(0, (int) ($this->option('servicios')     ?? $presets[$modo]['servicios']));
    $profesiones   = max(0, (int) ($this->option('profesiones')   ?? $presets[$modo]['profesiones']));

    config([
        'bulk_seed.enabled' => false,
        'bulk_seed.clientes'      => $clientes,
        'bulk_seed.profesionales' => $profesionales,
        'bulk_seed.servicios'     => $servicios,
        'bulk_seed.profesiones'   => $profesiones,
    ]);

    if ((bool) $this->option('fresh')) {
        $this->info('Ejecutando migrate:fresh --seed...');
        $exitMigrate = Artisan::call('migrate:fresh', ['--seed' => true]);
        $this->line(Artisan::output());

        if ($exitMigrate !== 0) {
            $this->error('Fallo migrate:fresh --seed');

            return $exitMigrate;
        }
    }

    $this->info("Cargando datos masivos (modo={$modo})...");
    $exitBulk = Artisan::call('db:seed', ['--class' => BulkDataSeeder::class]);
    $this->line(Artisan::output());

    if ($exitBulk !== 0) {
        $this->error('Fallo la ejecucion de BulkDataSeeder.');

        return $exitBulk;
    }

    $this->info("Datos creados: {$profesiones} profesiones, {$servicios} servicios, {$clientes} clientes y {$profesionales} profesionales.");

    return 0;
})->purpose('Carga datos de prueba masivos por modo o por cantidades personalizadas');

Artisan::command('formularios:sync-rejected-status', function () {
    $formulariosPendientes = Formulario::query()
        ->where('estado', 'Pendiente')
        ->get(['id', 'profesion_id']);

    $actualizados = 0;

    foreach ($formulariosPendientes as $formulario) {
        $formularioId = (int) $formulario->id;
        $profesionFormularioId = (int) $formulario->profesion_id;

        $asignadosActivos = DB::table('profesionales_formularios as pf')
            ->join('profesionales as p', 'p.id', '=', 'pf.profesional_id')
            ->where('pf.formulario_id', $formularioId)
            ->where('p.baja_id', 1)
            ->where(function ($query) use ($profesionFormularioId): void {
                $query->where('p.profesion_id', $profesionFormularioId)
                    ->orWhereExists(function ($subquery) use ($profesionFormularioId): void {
                        $subquery->select(DB::raw(1))
                            ->from('profesionales_profesiones as pp')
                            ->whereColumn('pp.profesional_id', 'p.id')
                            ->where('pp.profesion_id', $profesionFormularioId);
                    });
            })
            ->count();

        if ($asignadosActivos === 0) {
            continue;
        }

        $hayActivosSinRechazar = DB::table('profesionales_formularios as pf')
            ->join('profesionales as p', 'p.id', '=', 'pf.profesional_id')
            ->where('pf.formulario_id', $formularioId)
            ->where('p.baja_id', 1)
            ->where(function ($query) use ($profesionFormularioId): void {
                $query->where('p.profesion_id', $profesionFormularioId)
                    ->orWhereExists(function ($subquery) use ($profesionFormularioId): void {
                        $subquery->select(DB::raw(1))
                            ->from('profesionales_profesiones as pp')
                            ->whereColumn('pp.profesional_id', 'p.id')
                            ->where('pp.profesion_id', $profesionFormularioId);
                    });
            })
            ->whereRaw("LOWER(TRIM(pf.estado)) <> 'rechazado'")
            ->exists();

        if (!$hayActivosSinRechazar) {
            Formulario::query()
                ->where('id', $formularioId)
                ->update(['estado' => 'Rechazado por todos']);

            $actualizados++;
        }
    }

    $this->info('Formularios actualizados: ' . $actualizados);
})->purpose('Sincroniza formularios pendientes a Rechazado por todos cuando todos los profesionales activos rechazaron');

Artisan::command('backup:daily-email {--keep=4}', function () {
    $includeEnv = (bool) filter_var((string) env('BACKUP_INCLUDE_ENV', false), FILTER_VALIDATE_BOOLEAN);
    $archivePassword = trim((string) env('BACKUP_ARCHIVE_PASSWORD', ''));

    if ($includeEnv && $archivePassword === '') {
        $this->error('BACKUP_INCLUDE_ENV=true requiere BACKUP_ARCHIVE_PASSWORD configurado para cifrar el ZIP.');

        return 1;
    }

    $this->info('Iniciando backup semanal de base de datos y archivos...');

    $backupExitCode = Artisan::call('backup:run');
    $this->line(Artisan::output());

    if ($backupExitCode !== 0) {
        $this->error('No se pudo generar el backup.');

        return 1;
    }

    $disk = Storage::disk('local');
    $backupFolder = trim((string) config('backup.backup.name', 'Laravel'), '/');

    $files = collect($disk->files($backupFolder))
        ->filter(fn (string $file): bool => str_ends_with(strtolower($file), '.zip'))
        ->sortByDesc(fn (string $file): int => $disk->lastModified($file))
        ->values();

    if ($files->isEmpty()) {
        $this->error('No se encontró el archivo ZIP del backup para enviar por email.');

        return 1;
    }

    $latestBackup = (string) $files->first();

    $adminRecipients = Administrador::query()
        ->select('correo')
        ->whereNotNull('correo')
        ->pluck('correo')
        ->map(fn ($correo): string => trim((string) $correo))
        ->filter(fn (string $correo): bool => $correo !== '' && filter_var($correo, FILTER_VALIDATE_EMAIL) !== false)
        ->unique()
        ->values()
        ->all();

    $fallbackRecipient = (string) env('BACKUP_MAIL_TO', (string) config('backup.notifications.mail.to'));
    $recipients = !empty($adminRecipients)
        ? $adminRecipients
        : array_values(array_filter([(string) $fallbackRecipient]));

    if (empty($recipients)) {
        $this->error('No hay correos de administradores ni BACKUP_MAIL_TO configurado para enviar el backup.');

        return 1;
    }

    try {
        Mail::raw('Se adjunta el backup semanal de base de datos y archivos.', function ($message) use ($disk, $latestBackup, $recipients): void {
            $message->to($recipients)
                ->subject('Backup semanal - ' . now()->format('d/m/Y'))
                ->attachData(
                    $disk->get($latestBackup),
                    basename($latestBackup),
                    ['mime' => 'application/zip']
                );
        });
    } catch (\Throwable $e) {
        $this->error('El backup se generó, pero falló el envío de email: ' . $e->getMessage());

        return 1;
    }

    $keep = max(1, (int) $this->option('keep'));

    $files->slice($keep)->each(function (string $file) use ($disk): void {
        $disk->delete($file);
    });

    $deletedCount = max(0, $files->count() - $keep);

    $this->info('Backup enviado a: ' . implode(', ', $recipients));
    $this->info('Backups eliminados por retención: ' . $deletedCount);
    $this->info('Backups conservados: ' . $keep);

    return 0;
})->purpose('Genera backup semanal de BD y archivos, lo envía por mail y conserva solo los últimos N backups');

Schedule::command('logs:purge-old-security')->daily();
Schedule::command('backup:daily-email --keep=4')->dailyAt('16:00'); //ejecuta backup:run
Schedule::command('backup:clean')->dailyAt('16:20'); 
Schedule::command('backup:monitor')->dailyAt('16:30');
