<?php

declare(strict_types=1);

namespace App\Controllers;

use App\Models\Chrono;
use App\Models\Produit;
use App\Services\GravureService;
use App\Services\StravaService;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Illuminate\Database\Capsule\Manager as DB;

class ChronoController
{
    use ControllerTrait;

    public function __construct(
        private GravureService $gravure,
        private StravaService  $strava
    ) {}

    // POST /api/chrono/start
    public function start(Request $request, Response $response): Response
    {
        $user = $this->getAuthUser($request);
        if (!$user) return $this->error($response, 'UNAUTHORIZED', 'Non authentifié', 401);

        $body      = (array)$request->getParsedBody();
        $produitId = (int)($body['produit_id'] ?? 0);

        $produit = Produit::where('id', $produitId)->where('actif', 1)->first();
        if (!$produit) {
            return $this->error($response, 'NOT_FOUND', 'Parcours introuvable ou inactif', 404);
        }

        // Vérifier offset si client envoie une date
        $serverNow = date('Y-m-d H:i:s');
        if (!empty($body['date_debut'])) {
            $clientTs = strtotime($body['date_debut']);
            if ($clientTs) {
                $offset = abs(time() - $clientTs);
                $this->logAudit($user->id, 'chrono_start_offset', 'chrono', null, [
                    'ecart_secondes' => $offset,
                    'produit_id'     => $produitId,
                ], $request);
                if ($offset > 60) {
                    return $this->error($response, 'ECART_DATE_TROP_GRAND', 'Écart date trop grand (max 60s)', 422);
                }
            }
        }

        $chronoId = DB::table('chronos')->insertGetId([
            'user_id'    => $user->id,
            'produit_id' => $produitId,
            'source'     => 'qr',
            'statut'     => 'en_cours',
            'date_debut' => $serverNow,
            'created_at' => $serverNow,
        ]);

        return $this->success($response, [
            'id'         => $chronoId,
            'statut'     => 'en_cours',
            'date_debut' => $serverNow,
        ], 201);
    }

    // POST /api/chrono/stop
    public function stop(Request $request, Response $response): Response
    {
        $user = $this->getAuthUser($request);
        if (!$user) return $this->error($response, 'UNAUTHORIZED', 'Non authentifié', 401);

        $body             = (array)$request->getParsedBody();
        $chronoId         = (int)($body['chrono_id'] ?? 0);
        $dureeSecondes    = (int)($body['duree_secondes'] ?? 0);
        $ajustement       = (int)($body['ajustement_secondes'] ?? 0);
        $dateFin          = $body['date_fin'] ?? date('Y-m-d H:i:s');

        $chrono = Chrono::where('id', $chronoId)->where('user_id', $user->id)->first();
        if (!$chrono) {
            return $this->error($response, 'NOT_FOUND', 'Chrono introuvable', 404);
        }
        if ($chrono->user_id !== $user->id) {
            return $this->error($response, 'FORBIDDEN', 'Accès refusé', 403);
        }

        $dureeFinale = $dureeSecondes + $ajustement;

        DB::table('chronos')->where('id', $chronoId)->update([
            'duree_secondes'     => $dureeFinale,
            'ajustement_secondes'=> $ajustement,
            'date_fin'           => $dateFin,
            'statut'             => 'valide',
        ]);

        // Mettre à jour classement
        $this->strava->updateClassement(
            $user->id,
            $chrono->produit_id,
            $chronoId,
            $dureeFinale,
            $chrono->date_debut,
            'qr'
        );

        // Calculer position dans classement
        $position = DB::table('classements')
            ->where('produit_id', $chrono->produit_id)
            ->where('duree_secondes', '<', $dureeFinale)
            ->count() + 1;

        $dureeAffichage = $this->gravure->formaterDureeAffichage($dureeFinale);

        return $this->success($response, [
            'id'                  => $chronoId,
            'statut'              => 'valide',
            'duree_secondes'      => $dureeFinale,
            'duree_affichage'     => $dureeAffichage,
            'classement_position' => $position,
        ]);
    }

    // POST /api/chrono/manuel
    public function manuel(Request $request, Response $response): Response
    {
        $user = $this->getAuthUser($request);
        if (!$user) return $this->error($response, 'UNAUTHORIZED', 'Non authentifié', 401);

        $body          = (array)$request->getParsedBody();
        $produitId     = (int)($body['produit_id'] ?? 0);
        $duree         = (int)($body['duree_secondes'] ?? 0);
        $dateDebut     = $body['date_debut'] ?? date('Y-m-d H:i:s');
        $notes         = $body['notes'] ?? null;

        $produit = Produit::where('id', $produitId)->where('actif', 1)->first();
        if (!$produit) {
            return $this->error($response, 'NOT_FOUND', 'Parcours introuvable', 404);
        }

        $chronoId = DB::table('chronos')->insertGetId([
            'user_id'        => $user->id,
            'produit_id'     => $produitId,
            'duree_secondes' => $duree,
            'source'         => 'manuel',
            'statut'         => 'valide',
            'date_debut'     => $dateDebut,
            'created_at'     => date('Y-m-d H:i:s'),
        ]);

        return $this->success($response, [
            'id'             => $chronoId,
            'source'         => 'manuel',
            'statut'         => 'valide',
            'duree_secondes' => $duree,
        ], 201);
    }

    // GET /api/chrono/history
    public function history(Request $request, Response $response): Response
    {
        $user = $this->getAuthUser($request);
        if (!$user) return $this->error($response, 'UNAUTHORIZED', 'Non authentifié', 401);

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

        $query = DB::table('chronos')
            ->join('produits', 'chronos.produit_id', '=', 'produits.id')
            ->leftJoin('commandes', 'commandes.chrono_id', '=', 'chronos.id')
            ->where('chronos.user_id', $user->id)
            ->where('chronos.statut', 'valide')
            ->select([
                'chronos.id', 'chronos.duree_secondes', 'chronos.source',
                'chronos.statut', 'chronos.strava_activity_id',
                'chronos.date_debut', 'chronos.created_at',
                'produits.slug as produit_slug', 'produits.nom as produit_nom',
                'commandes.id as commande_id', 'commandes.statut as commande_statut',
                'commandes.numero_suivi',
            ])
            ->orderBy('chronos.date_debut', 'desc');

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

        $data = $chronos->map(function ($c) {
            return [
                'id'              => $c->id,
                'produit'         => ['slug' => $c->produit_slug, 'nom' => $c->produit_nom],
                'duree_secondes'  => $c->duree_secondes,
                'duree_affichage' => (new GravureService())->formaterDureeAffichage((int)$c->duree_secondes),
                'source'          => $c->source,
                'statut'          => $c->statut,
                'strava_activity_id' => $c->strava_activity_id,
                'strava_confirmed'=> !empty($c->strava_activity_id) && $c->source !== 'strava',
                'date_debut'      => $c->date_debut,
                'commande'        => $c->commande_id ? [
                    'id'           => $c->commande_id,
                    'statut'       => $c->commande_statut,
                    'numero_suivi' => $c->numero_suivi,
                ] : null,
            ];
        })->toArray();

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

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

        $user     = $this->getAuthUser($request);
        $chronoId = (int)$args['id'];
        $body     = (array)$request->getParsedBody();

        $chrono = Chrono::find($chronoId);
        if (!$chrono) return $this->error($response, 'NOT_FOUND', 'Chrono introuvable', 404);

        DB::table('chronos')->where('id', $chronoId)->update([
            'statut' => $body['statut'] ?? 'valide',
        ]);

        $this->logAudit($user->id, 'chrono_valide_admin', 'chrono', $chronoId, $body, $request);

        return $this->success($response, ['id' => $chronoId, 'statut' => $body['statut'] ?? 'valide']);
    }

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

        $chronoId = (int)$args['id'];
        $body     = (array)$request->getParsedBody();

        DB::table('chronos')->where('id', $chronoId)->update(['statut' => 'invalide']);
        $this->logAudit(null, 'chrono_invalide', 'chrono', $chronoId, $body, $request);

        return $this->success($response, ['id' => $chronoId, 'statut' => 'invalide']);
    }

    // PATCH /api/chrono/:id/ajuster-date
    public function ajusterDate(Request $request, Response $response, array $args): Response
    {
        $user     = $this->getAuthUser($request);
        if (!$user) return $this->error($response, 'UNAUTHORIZED', 'Non authentifié', 401);

        $chronoId  = (int)$args['id'];
        $body      = (array)$request->getParsedBody();
        $dateDebut = $body['date_debut'] ?? '';
        $raison    = $body['raison'] ?? '';

        $chrono = Chrono::where('id', $chronoId)->where('user_id', $user->id)->first();
        if (!$chrono) return $this->error($response, 'FORBIDDEN', 'Accès refusé', 403);

        if ($chrono->statut !== 'valide') {
            return $this->error($response, 'VALIDATION_ERROR', 'Chrono non validé', 422);
        }

        // Vérifier pas de commande liée
        $hasCommande = DB::table('commandes')->where('chrono_id', $chronoId)->exists();
        if ($hasCommande) {
            return $this->error($response, 'COMMANDE_LIEE', 'Impossible — une commande est liée', 409);
        }

        // Vérifier écart max 30 min
        $newTs  = strtotime($dateDebut);
        $oldTs  = strtotime($chrono->date_debut ?? '');
        if (!$newTs || !$oldTs || abs($newTs - $oldTs) > 1800) {
            return $this->error($response, 'ECART_TROP_GRAND', 'Écart max 30 minutes', 422);
        }

        DB::table('chronos')->where('id', $chronoId)->update(['date_debut' => $dateDebut]);
        $this->logAudit($user->id, 'chrono_date_ajustee', 'chrono', $chronoId, ['raison' => $raison, 'nouvelle_date' => $dateDebut], $request);

        return $this->success($response, ['id' => $chronoId, 'date_debut' => $dateDebut]);
    }

    // GET /api/chrono/:produit_id/mes
    public function mesByParcours(Request $request, Response $response, array $args): Response
    {
        $user      = $this->getAuthUser($request);
        if (!$user) return $this->error($response, 'UNAUTHORIZED', 'Non authentifié', 401);

        $produitId = (int)$args['produit_id'];

        $chronos = DB::table('chronos')
            ->leftJoin('commandes', 'commandes.chrono_id', '=', 'chronos.id')
            ->where('chronos.user_id', $user->id)
            ->where('chronos.produit_id', $produitId)
            ->where('chronos.statut', 'valide')
            ->select([
                'chronos.id', 'chronos.duree_secondes', 'chronos.source',
                'chronos.strava_activity_id', 'chronos.date_debut',
                'commandes.id as commande_id', 'commandes.statut as commande_statut',
                'commandes.numero_suivi',
            ])
            ->orderBy('chronos.duree_secondes', 'asc')
            ->get();

        return $this->success($response, $chronos->map(fn($c) => [
            'id'             => $c->id,
            'duree_secondes' => $c->duree_secondes,
            'source'         => $c->source,
            'strava_confirmed'=> !empty($c->strava_activity_id) && $c->source !== 'strava',
            'commande'       => $c->commande_id ? [
                'id'           => $c->commande_id,
                'statut'       => $c->commande_statut,
                'numero_suivi' => $c->numero_suivi,
            ] : null,
        ])->toArray());
    }

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

        $chronos = DB::table('chronos')
            ->join('users', 'users.id', '=', 'chronos.user_id')
            ->join('produits', 'produits.id', '=', 'chronos.produit_id')
            ->whereNotNull('chronos.anomalie')
            ->where('chronos.statut', 'valide')
            ->select(['chronos.*', 'users.prenom', 'users.nom', 'produits.nom as parcours'])
            ->get();

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

    // PATCH /api/chrono/{id} — modifier son propre chrono manuel
    public function updateMien(Request $request, Response $response, array $args): Response
    {
        $user = $this->getAuthUser($request);
        if (!$user) return $this->error($response, 'UNAUTHORIZED', 'Non authentifié', 401);

        $chronoId = (int)$args['id'];
        $body     = (array)$request->getParsedBody();

        $chrono = DB::table('chronos')
            ->where('id', $chronoId)
            ->where('user_id', $user->id)
            ->first();

        if (!$chrono) {
            return $this->error($response, 'NOT_FOUND', 'Chrono introuvable', 404);
        }

        // Seuls les chronos manuels ou GPS sans commande liée sont modifiables
        if ($chrono->source === 'strava') {
            return $this->error($response, 'FORBIDDEN',
                'Les chronos Strava ne peuvent pas être modifiés manuellement.', 403);
        }

        $hasCommande = DB::table('commandes')->where('chrono_id', $chronoId)->exists();
        if ($hasCommande) {
            return $this->error($response, 'COMMANDE_LIEE',
                'Ce chrono est lié à une commande — modification impossible.', 409);
        }

        $updates = [];

        if (isset($body['duree_secondes'])) {
            $duree = (int)$body['duree_secondes'];
            if ($duree < 1) {
                return $this->error($response, 'VALIDATION_ERROR', 'Durée invalide', 422);
            }
            $updates['duree_secondes'] = $duree;
        }

        if (array_key_exists('notes', $body)) {
            $updates['notes'] = $body['notes'] ? trim($body['notes']) : null;
        }

        if (array_key_exists('date_debut', $body) && $body['date_debut']) {
            $updates['date_debut'] = $body['date_debut'];
        }

        if (empty($updates)) {
            return $this->error($response, 'VALIDATION_ERROR', 'Aucun champ à mettre à jour', 422);
        }

        DB::table('chronos')->where('id', $chronoId)->update($updates);

        // Mettre à jour le classement si le temps a changé
        if (isset($updates['duree_secondes'])) {
            $this->strava->updateClassement(
                $user->id,
                $chrono->produit_id,
                $chronoId,
                $updates['duree_secondes'],
                $updates['date_debut'] ?? $chrono->date_debut,
                $chrono->source
            );
        }

        $this->logAudit($user->id, 'chrono_update_sportif', 'chrono', $chronoId, $updates, $request);

        return $this->success($response, array_merge(['id' => $chronoId], $updates));
    }

    // DELETE /api/chrono/{id} — supprimer son propre chrono
    public function deleteMien(Request $request, Response $response, array $args): Response
    {
        $user = $this->getAuthUser($request);
        if (!$user) return $this->error($response, 'UNAUTHORIZED', 'Non authentifié', 401);

        $chronoId = (int)$args['id'];

        $chrono = DB::table('chronos')
            ->where('id', $chronoId)
            ->where('user_id', $user->id)
            ->first();

        if (!$chrono) {
            return $this->error($response, 'NOT_FOUND', 'Chrono introuvable', 404);
        }

        // Bloquer si commande liée
        $hasCommande = DB::table('commandes')->where('chrono_id', $chronoId)->exists();
        if ($hasCommande) {
            return $this->error($response, 'COMMANDE_LIEE',
                'Ce chrono est lié à une commande — suppression impossible.', 409);
        }

        // Supprimer du classement
        DB::table('classements')
            ->where('user_id', $user->id)
            ->where('produit_id', $chrono->produit_id)
            ->where(function ($q) use ($chronoId) {
                $q->where('chrono_id', $chronoId)->orWhereNull('chrono_id');
            })
            ->delete();

        DB::table('chronos')->where('id', $chronoId)->delete();

        $this->logAudit($user->id, 'chrono_delete_sportif', 'chrono', $chronoId, [
            'produit_id'     => $chrono->produit_id,
            'duree_secondes' => $chrono->duree_secondes,
        ], $request);

        return $this->success($response, ['message' => 'Chrono supprimé', 'id' => $chronoId]);
    }
}
