<?php

declare(strict_types=1);

namespace App\Controllers;

use App\Models\User;
use App\Services\BackupService;
use App\Services\MailService;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Illuminate\Database\Capsule\Manager as DB;

class AdminController
{
    use ControllerTrait;
    use AgentKeyTrait;

    public function __construct(
        private BackupService $backup,
        private MailService   $mail
    ) {}

    // POST /api/setup
    public function setup(Request $request, Response $response): Response
    {
        // Vérifier le setup token
        $body  = (array)$request->getParsedBody();
        $token = $body['setup_token'] ?? $request->getHeaderLine('X-Setup-Token');

        $expectedToken = $_ENV['SETUP_TOKEN'] ?? '';
        if (!$expectedToken || $token !== $expectedToken) {
            return $this->error($response, 'UNAUTHORIZED', 'Setup token invalide', 401);
        }

        // Bloquer si users table non vide
        if (DB::table('users')->count() > 0) {
            return $this->error($response, 'ALREADY_SETUP', 'Un utilisateur existe déjà', 409);
        }

        $email    = $body['email'] ?? '';
        $password = $body['password'] ?? '';
        $prenom   = $body['prenom'] ?? '';
        $nom      = strtoupper($body['nom'] ?? '');

        if (!filter_var($email, FILTER_VALIDATE_EMAIL) || strlen($password) < 8) {
            return $this->error($response, 'VALIDATION_ERROR', 'Email ou mot de passe invalide', 400);
        }

        DB::table('users')->insert([
            'email'            => $email,
            'password'         => password_hash($password, PASSWORD_BCRYPT),
            'prenom'           => $prenom,
            'nom'              => $nom,
            'role'             => 'super_admin',
            'actif'            => 1,
            'date_inscription' => date('Y-m-d H:i:s'),
        ]);

        // Invalider le setup token
        // (il faudra supprimer la variable .env manuellement)

        return $this->success($response, ['message' => 'Super-admin créé. Supprimez SETUP_TOKEN de votre .env.']);
    }

    // GET /api/admin/stats
    public function stats(Request $request, Response $response): Response
    {
        $guard = $this->requireRole($request, $response, ['admin_fab', 'super_admin']);
        if ($guard) return $guard;

        $commandes = [
            'total'       => DB::table('commandes')->count(),
            'en_lot'      => DB::table('commandes')->where('statut', 'en_lot')->count(),
            'en_gravure'  => DB::table('commandes')->where('statut', 'en_gravure')->count(),
            'expediees'   => DB::table('commandes')->where('statut', 'expedie')->count(),
            'importees'   => DB::table('commandes')->where('statut', 'importee')->count(),
        ];

        $chronos = [
            'total'    => DB::table('chronos')->count(),
            'valides'  => DB::table('chronos')->where('statut', 'valide')->count(),
            'en_cours' => DB::table('chronos')->where('statut', 'en_cours')->count(),
        ];

        $stocks = [
            'alertes' => DB::table('stocks')->whereColumn('quantite', '<', 'seuil_alerte')->count(),
        ];

        // CA estimé du mois
        $caEstime = DB::table('commandes')
            ->whereMonth('created_at', date('m'))
            ->whereYear('created_at', date('Y'))
            ->where('statut', 'expedie')
            ->count() * 42.00; // Prix moyen estimé

        // Désync PS (commandes statut 2 non importées depuis > 15 min)
        $psDesync = DB::table('ps_import_queue')
            ->where('statut', 'en_attente')
            ->where('derniere_tentative', '<', date('Y-m-d H:i:s', time() - 900))
            ->count();

        return $this->success($response, [
            'commandes'          => $commandes,
            'chronos'            => $chronos,
            'stocks'             => $stocks,
            'ca_estime_mois'     => $caEstime,
            'ps_desync_count'    => $psDesync,
        ]);
    }

    // GET /api/admin/logs
    public function logs(Request $request, Response $response): Response
    {
        $guard = $this->requireRole($request, $response, ['super_admin']);
        if ($guard) return $guard;

        $params = $request->getQueryParams();
        [$page, $limit, $offset] = $this->paginate($request);

        $query = DB::table('logs_audit')
            ->orderBy('created_at', 'desc');

        if (!empty($params['action']))   $query->where('action', $params['action']);
        if (!empty($params['entite']))   $query->where('entite', $params['entite']);

        $total = $query->count();
        $logs  = $query->skip($offset)->take($limit)->get();

        return $this->successList($response, $logs->toArray(), ['page' => $page, 'limit' => $limit, 'total' => $total]);
    }

    // GET/PATCH /api/admin/config
    public function getConfig(Request $request, Response $response): Response
    {
        $guard = $this->requireRole($request, $response, ['admin_fab', 'super_admin']);
        if ($guard) return $guard;

        try {
            $config = DB::connection('workflow')->table('app_config')->get()->keyBy('cle');
            return $this->success($response, $config->toArray());
        } catch (\Exception $e) {
            return $this->success($response, []);
        }
    }

    public function updateConfig(Request $request, Response $response): Response
    {
        $guard = $this->requireRole($request, $response, ['super_admin']);
        if ($guard) return $guard;

        $body = (array)$request->getParsedBody();

        // Maintenance mode
        if (isset($body['maintenance_mode'])) {
            putenv('MAINTENANCE_MODE=' . ($body['maintenance_mode'] ? 'true' : 'false'));
        }

        // Groq daily limit
        if (isset($body['groq_daily_limit'])) {
            try {
                DB::connection('workflow')->table('app_config')->updateOrInsert(
                    ['cle' => 'groq_daily_limit'],
                    ['valeur' => (string)$body['groq_daily_limit']]
                );
            } catch (\Exception $e) {}
        }

        return $this->success($response, ['updated' => true]);
    }

    // GET /api/admin/users
    public function users(Request $request, Response $response): Response
    {
        $guard = $this->requireRole($request, $response, ['super_admin']);
        if ($guard) return $guard;

        $users = DB::table('users')
            ->where('role', '!=', 'sportif')
            ->select(['id', 'email', 'prenom', 'nom', 'role', 'atelier_id', 'actif', 'date_inscription'])
            ->get();

        return $this->success($response, $users->toArray());
    }

    // POST /api/admin/users
    public function createUser(Request $request, Response $response): Response
    {
        $guard = $this->requireRole($request, $response, ['super_admin']);
        if ($guard) return $guard;

        $body     = (array)$request->getParsedBody();
        $email    = strtolower(trim($body['email'] ?? ''));
        $password = $body['password'] ?? '';
        $role     = $body['role'] ?? 'operateur_fab';

        if (DB::table('users')->where('email', $email)->exists()) {
            return $this->error($response, 'EMAIL_TAKEN', 'Email déjà utilisé', 409);
        }

        $id = DB::table('users')->insertGetId([
            'email'            => $email,
            'password'         => password_hash($password, PASSWORD_BCRYPT),
            'prenom'           => $body['prenom'] ?? null,
            'nom'              => strtoupper($body['nom'] ?? ''),
            'role'             => $role,
            'atelier_id'       => $body['atelier_id'] ?? null,
            'actif'            => 1,
            'date_inscription' => date('Y-m-d H:i:s'),
        ]);

        // Envoyer email accès
        if (!empty($body['email'])) {
            $this->mail->accesFabCree($email, $body['prenom'] ?? 'Opérateur', $password);
        }

        return $this->success($response, ['id' => $id, 'email' => $email, 'role' => $role], 201);
    }

    // PATCH /api/admin/users/:id
    public function updateUser(Request $request, Response $response, array $args): Response
    {
        $guard = $this->requireRole($request, $response, ['super_admin']);
        if ($guard) return $guard;

        $body = (array)$request->getParsedBody();
        unset($body['email']); // Email non modifiable

        if (isset($body['password'])) {
            $body['password'] = password_hash($body['password'], PASSWORD_BCRYPT);
        }

        DB::table('users')->where('id', (int)$args['id'])->update($body);
        return $this->success($response, ['id' => (int)$args['id']]);
    }

    // GET /api/admin/sportifs
    public function sportifs(Request $request, Response $response): Response
    {
        $guard = $this->requireRole($request, $response, ['admin_fab', 'admin_chrono', 'super_admin']);
        if ($guard) return $guard;

        $params = $request->getQueryParams();
        [$page, $limit, $offset] = $this->paginate($request);

        $query = DB::table('users')
            ->where('role', 'sportif')
            ->where('actif', 1)
            ->select(['id', 'email', 'prenom', 'nom', 'strava_connected', 'presta_customer_id', 'date_inscription', 'derniere_connexion'])
            ->orderBy('date_inscription', 'desc');

        if (!empty($params['search'])) {
            $s = $params['search'];
            $query->where(function($q) use ($s) {
                $q->where('email', 'like', "%{$s}%")
                  ->orWhere('nom', 'like', "%{$s}%")
                  ->orWhere('prenom', 'like', "%{$s}%");
            });
        }

        $total   = $query->count();
        $sportifs = $query->skip($offset)->take($limit)->get();

        return $this->successList($response, $sportifs->toArray(), ['page' => $page, 'limit' => $limit, 'total' => $total]);
    }

    // GET /api/admin/sportifs/:id
    public function sportifDetail(Request $request, Response $response, array $args): Response
    {
        $guard = $this->requireRole($request, $response, ['admin_fab', 'super_admin']);
        if ($guard) return $guard;

        $id      = (int)$args['id'];
        $user    = DB::table('users')->where('id', $id)->where('role', 'sportif')->first();
        if (!$user) return $this->error($response, 'NOT_FOUND', 'Sportif introuvable', 404);

        $chronos   = DB::table('chronos')->where('user_id', $id)->count();
        $commandes = DB::table('commandes')->where('user_id', $id)->count();
        $expedies  = DB::table('commandes')->where('user_id', $id)->where('statut', 'expedie')->count();

        return $this->success($response, array_merge((array)$user, [
            'nb_chronos'   => $chronos,
            'nb_commandes' => $commandes,
            'nb_expedies'  => $expedies,
        ]));
    }

    // GET /api/admin/agent-status
    public function agentStatus(Request $request, Response $response): Response
    {
        $guard = $this->requireRole($request, $response, ['admin_fab', 'super_admin']);
        if ($guard) return $guard;

        $ateliers = DB::table('ateliers')->where('actif', 1)->get();

        $data = $ateliers->map(function ($atelier) {
            $agent = DB::table('users')
                ->where('atelier_id', $atelier->id)
                ->whereNotNull('agent_api_key')
                ->orderBy('agent_last_heartbeat', 'desc')
                ->first();

            if (!$agent || !$agent->agent_last_heartbeat) {
                return ['atelier_id' => $atelier->id, 'atelier_nom' => $atelier->nom, 'statut' => 'inconnu'];
            }

            $minutesSilence = (time() - strtotime($agent->agent_last_heartbeat)) / 60;
            $statut         = $minutesSilence > 15 ? 'alerte' : 'ok';

            return [
                'atelier_id'         => $atelier->id,
                'atelier_nom'        => $atelier->nom,
                'dernier_heartbeat'  => $agent->agent_last_heartbeat,
                'statut'             => $statut,
                'agent_version'      => $agent->agent_version ?? null,
                'message'            => $statut === 'alerte' ? "Silence depuis " . round($minutesSilence) . " minutes" : null,
            ];
        })->toArray();

        return $this->success($response, $data);
    }

    // POST /api/admin/backup
    public function backup(Request $request, Response $response): Response
    {
        $guard = $this->requireRole($request, $response, ['super_admin']);
        if ($guard) return $guard;

        try {
            $filepath = $this->backup->creerBackup();
            $size     = filesize($filepath);

            return $this->success($response, [
                'fichier' => basename($filepath),
                'taille'  => round($size / 1048576, 1) . ' MB',
            ]);
        } catch (\Exception $e) {
            return $this->error($response, 'INTERNAL_ERROR', $e->getMessage(), 500);
        }
    }

    // GET /api/admin/backup/dropbox-config
    public function dropboxConfig(Request $request, Response $response): Response
    {
        $guard = $this->requireRole($request, $response, ['admin_fab', 'super_admin']);
        if ($guard) return $guard;

        return $this->success($response, $this->backup->getDropboxConfig());
    }

    // POST /api/admin/backup/dropbox-config
    public function saveDropboxConfig(Request $request, Response $response): Response
    {
        $guard = $this->requireRole($request, $response, ['admin_fab', 'super_admin']);
        if ($guard) return $guard;

        $body  = (array)$request->getParsedBody();
        $token = $body['access_token'] ?? '';
        $dossier = $body['dossier'] ?? '/MKO-Backups/';

        if (!$token) return $this->error($response, 'VALIDATION_ERROR', 'Token Dropbox requis', 400);

        try {
            $this->backup->sauvegarderTokenDropbox($token, $dossier);
            return $this->success($response, ['message' => 'Configuration Dropbox enregistrée']);
        } catch (\Exception $e) {
            return $this->error($response, 'TOKEN_INVALIDE', $e->getMessage(), 400);
        }
    }

    // POST /api/admin/backup/dropbox-test
    public function testDropbox(Request $request, Response $response): Response
    {
        $guard = $this->requireRole($request, $response, ['admin_fab', 'super_admin']);
        if ($guard) return $guard;

        try {
            $this->backup->testerConnexionDropbox();
            return $this->success($response, ['statut' => 'ok', 'message' => 'Connexion Dropbox opérationnelle']);
        } catch (\Exception $e) {
            return $this->error($response, 'DROPBOX_ERROR', $e->getMessage(), 502);
        }
    }

    // POST /api/admin/backup/dropbox-now
    public function backupDropboxNow(Request $request, Response $response): Response
    {
        $guard = $this->requireRole($request, $response, ['admin_fab', 'super_admin']);
        if ($guard) return $guard;

        try {
            $filepath = $this->backup->creerBackup();
            $this->backup->uploadDropbox($filepath);

            return $this->success($response, [
                'fichier'      => basename($filepath),
                'taille'       => round(filesize($filepath) / 1048576, 1) . ' MB',
                'dropbox_path' => '/MKO-Backups/' . basename($filepath),
            ]);
        } catch (\Exception $e) {
            return $this->error($response, 'INTERNAL_ERROR', $e->getMessage(), 500);
        }
    }

    // GET /api/admin/schema-version
    public function schemaVersion(Request $request, Response $response): Response
    {
        $guard = $this->requireRole($request, $response, ['super_admin']);
        if ($guard) return $guard;

        $expectedTables = [
            'users', 'produits', 'segment_tracking_config', 'chronos', 'commandes',
            'lots', 'commande_lot', 'fichiers_gravure', 'templates_gravure', 'ateliers',
            'relances_log', 'chat_conversations', 'chat_participants', 'chat_messages',
            'notifications', 'logs_audit', 'sav_tickets', 'defauts', 'rapports',
            'relance_seuils', 'prestashop_config', 'strava_config', 'stocks',
            'classements', 'sessions', 'ps_import_queue', 'dropbox_config',
            'templates_etiquettes', 'rate_limits',
        ];

        $existingTables = DB::select("SHOW TABLES");
        $existing       = array_map(fn($t) => array_values((array)$t)[0], $existingTables);
        $manquantes     = array_values(array_diff($expectedTables, $existing));

        return $this->success($response, [
            'schema_version'     => '1.1.0',
            'tables_mykollufab'  => [
                'attendues'  => count($expectedTables),
                'presentes'  => count($existing),
                'manquantes' => $manquantes,
            ],
        ]);
    }

    // POST /api/admin/notifications/check-sportifs
    public function checkSportifs(Request $request, Response $response): Response
    {
        $guard = $this->requireRole($request, $response, ['super_admin']);
        if ($guard) return $guard;

        $relanceService = new \App\Services\RelanceService();
        $nb  = $relanceService->traiterRelancesJ7();
        $nb += $relanceService->traiterRelancesJ30();
        $nb += $relanceService->traiterRelancesJ60();

        return $this->success($response, ['relances_envoyees' => $nb]);
    }
}
