<?php
// ══════════════════════════════════════════
// ParcoursController.php
// ══════════════════════════════════════════
namespace App\Controllers;

use App\Models\Produit;
use App\Models\SegmentTrackingConfig;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Illuminate\Database\Capsule\Manager as DB;

class ParcoursController
{
    use ControllerTrait;

    private array $defaultTracking = [
        'wake_distance_m'                => 2000,
        'high_accuracy_start_distance_m' => 300,
        'pulse_high_accuracy_duration_s' => 10,
        'pulse_low_accuracy_duration_s'  => 45,
        'finish_high_accuracy_distance_m'=> 400,
        'cooldown_distance_m'            => 150,
        'cooldown_time_s'                => 40,
        'strava_dedup_window_minutes'    => 120,
    ];

    public function index(Request $request, Response $response): Response
    {
        // Endpoint public → tous actifs. Avec JWT admin → filtre optionnel
        $params = $request->getQueryParams();
        $query  = Produit::with('trackingConfig');

        // Si appelé depuis le portail admin (JWT présent), autoriser le filtre actif
        $payload = $this->getAuthPayload($request);
        if ($payload && in_array($payload->role, ['admin_fab', 'super_admin'], true)) {
            if (isset($params['actif']) && $params['actif'] !== '') {
                $query->where('actif', (int)$params['actif']);
            }
            if (!empty($params['statut'])) {
                $query->where('statut', $params['statut']);
            }
            if (!empty($params['categorie'])) {
                $query->where('categorie', $params['categorie']);
            }
            if (!empty($params['q'])) {
                $q = $params['q'];
                $query->where(function($qb) use ($q) {
                    $qb->where('nom', 'like', "%{$q}%")->orWhere('slug', 'like', "%{$q}%");
                });
            }
        } else {
            // Public : actifs + en développement (pour la carte www)
            $query->where(function($q) {
                $q->where('actif', 1)
                  ->orWhere('statut', 'developpement');
            });
        }

        $parcours = $query->get();
        return $this->success($response, $parcours->map(fn($p) => $this->formatParcours($p))->toArray());
    }

    public function bySlug(Request $request, Response $response, array $args): Response
    {
        $slug    = $args['slug'];
        $produit = Produit::where('slug', $slug)->with('trackingConfig')->first();

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

        if (!$produit->actif) {
            return $this->error($response, 'NOT_FOUND', $produit->message_desactivation ?? 'Parcours désactivé', 404);
        }

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

    public function show(Request $request, Response $response, array $args): Response
    {
        $produit = Produit::find((int)$args['id']);
        if (!$produit) return $this->error($response, 'NOT_FOUND', 'Parcours introuvable', 404);
        return $this->success($response, $this->formatParcours($produit));
    }

    public function nearby(Request $request, Response $response): Response
    {
        $params    = $request->getQueryParams();
        $lat       = (float)($params['lat'] ?? 0);
        $lng       = (float)($params['lng'] ?? 0);
        $radiusKm  = (float)($params['radius_km'] ?? 50);

        $parcours = Produit::where('actif', 1)->get();

        $nearby = $parcours->map(function ($p) use ($lat, $lng) {
            $dist = $this->haversine($lat, $lng, (float)$p->depart_lat, (float)$p->depart_lng);
            return array_merge($this->formatParcours($p), ['distance_from_user_km' => round($dist, 1)]);
        })->filter(fn($p) => $p['distance_from_user_km'] <= $radiusKm)
          ->sortBy('distance_from_user_km')->values();

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

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

        $body = (array)$request->getParsedBody();
        $slug = $body['slug'] ?? '';

        if (Produit::where('slug', $slug)->exists()) {
            return $this->error($response, 'CONFLICT', 'Ce slug existe déjà', 409);
        }

        // Extraire tracking_config AVANT Produit::create()
        // (pas de colonne tracking_config dans produits → SQL error → 500)
        $trackingConfig = $body['tracking_config'] ?? [];
        unset($body['tracking_config']);

        // strava_keywords : le frontend envoie un array, la BDD attend du JSON TEXT
        if (isset($body['strava_keywords']) && is_array($body['strava_keywords'])) {
            $body['strava_keywords'] = json_encode($body['strava_keywords'], JSON_UNESCAPED_UNICODE);
        }

        $produit = Produit::create($body);

        // Créer tracking_config par défaut
        SegmentTrackingConfig::create(array_merge(
            $this->defaultTracking,
            ['produit_id' => $produit->id],
            $trackingConfig
        ));

        return $this->success($response, $this->formatParcours($produit->fresh('trackingConfig')), 201);
    }

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

        $produit = Produit::find((int)$args['id']);
        if (!$produit) return $this->error($response, 'NOT_FOUND', 'Parcours introuvable', 404);

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

        // Séparer tracking_config des données produit
        $trackingConfig = $body['tracking_config'] ?? null;
        unset($body['tracking_config']);

        // strava_keywords : le frontend envoie un array, la BDD attend du JSON TEXT
        if (isset($body['strava_keywords']) && is_array($body['strava_keywords'])) {
            $body['strava_keywords'] = json_encode($body['strava_keywords'], JSON_UNESCAPED_UNICODE);
        }

        // Mettre à jour cache nom si fabricant/graveur change
        if (isset($body['fabricant_contact_id'])) {
            try {
                $contact = DB::connection('workflow')->table('contacts')->find($body['fabricant_contact_id']);
                $body['fabricant_nom_cache'] = $contact ? $contact->nom : null;
            } catch (\Exception $e) {
                // connexion workflow absente — on ignore, le nom_cache n'est pas critique
                $body['fabricant_nom_cache'] = null;
            }
        }
        if (isset($body['graveur_contact_id'])) {
            try {
                $contact = DB::connection('workflow')->table('contacts')->find($body['graveur_contact_id']);
                $body['graveur_nom_cache'] = $contact ? $contact->nom : null;
            } catch (\Exception $e) {
                $body['graveur_nom_cache'] = null;
            }
        }

        $produit->update($body);

        if ($trackingConfig) {
            SegmentTrackingConfig::updateOrCreate(
                ['produit_id' => $produit->id],
                $trackingConfig
            );
        }

        return $this->success($response, $this->formatParcours($produit->fresh('trackingConfig')));
    }

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

        $produit = Produit::find((int)$args['id']);
        if (!$produit) {
            return $this->error($response, 'NOT_FOUND', 'Parcours introuvable', 404);
        }

        // Bloquer si des commandes sont liées
        if ($produit->commandes()->count() > 0) {
            return $this->error($response, 'CONFLICT', 'Impossible de supprimer : des commandes sont liées à ce parcours.', 409);
        }

        // Supprimer la tracking config associée
        $produit->trackingConfig()->delete();
        $produit->delete();

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

    private function formatParcours(Produit $p): array
    {
        $tc = $p->trackingConfig ?? (object)$this->defaultTracking;

        // Construire les objets GPS attendus par la PWA
        $gps_depart  = ($p->depart_lat && $p->depart_lng)
            ? ['lat' => (float)$p->depart_lat, 'lng' => (float)$p->depart_lng]
            : null;
        $gps_arrivee = ($p->sommet_lat && $p->sommet_lng)
            ? ['lat' => (float)$p->sommet_lat, 'lng' => (float)$p->sommet_lng]
            : null;

        return [
            'id'                    => $p->id,
            'slug'                  => $p->slug,

            // ── Nom affiché PWA ──────────────────────────────
            'nom'                   => $p->nom,
            'nom_ligne1'            => $p->nom_ligne1,
            'nom_ligne2'            => $p->nom_ligne2,
            'badge'                 => $p->badge,

            // ── Infos générales ──────────────────────────────
            'categorie'             => $p->categorie,
            'difficulte'            => $p->difficulte,
            'sous_titre'            => $p->sous_titre,
            'description'           => $p->description,
            'actif'                 => (bool)$p->actif,
            'message_desactivation' => $p->message_desactivation,

            // ── Métriques — noms normalisés pour la PWA ──────
            'altitude_m'            => $p->altitude   !== null ? (int)$p->altitude   : null,
            'distance_km'           => $p->distance_km !== null ? (float)$p->distance_km : null,
            'denivele_m'            => $p->denivele    !== null ? (int)$p->denivele   : null,
            'rayon_km'              => $p->rayon_km    !== null ? (float)$p->rayon_km : 0.200,

            // ── GPS — objets {lat, lng} pour la PWA ─────────
            'gps_depart'            => $gps_depart,
            'gps_arrivee'           => $gps_arrivee,
            // Champs bruts conservés pour la compatibilité FAB
            'depart_lat'            => $p->depart_lat,
            'depart_lng'            => $p->depart_lng,
            'sommet_lat'            => $p->sommet_lat,
            'sommet_lng'            => $p->sommet_lng,

            // ── Strava & boutique ────────────────────────────
            'strava_keywords'       => $p->strava_keywords
                ? (is_array($p->strava_keywords)
                    ? $p->strava_keywords
                    : json_decode($p->strava_keywords, true) ?? [])
                : [],
            'presta_product_id'     => $p->presta_product_id,
            'lien_boutique'         => $p->lien_boutique,
            'qr_url'                => $p->qr_url,

            // ── Images ──────────────────────────────────────
            'image_url'             => $p->image_url,

            // ── Fabricant / Graveur ──────────────────────────
            'fabricant_contact_id'  => $p->fabricant_contact_id,
            'fabricant_nom_cache'   => $p->fabricant_nom_cache,
            'graveur_contact_id'    => $p->graveur_contact_id,
            'graveur_nom_cache'     => $p->graveur_nom_cache,

            // ── Stock ────────────────────────────────────────
            'stock_medailles'       => $p->stock_medailles ?? 0,
            'stock_seuil_alerte'    => $p->stock_seuil_alerte ?? 20,

            // ── Nouveaux champs carte & fiche ────────────────
            'statut'                => $p->statut ?? ($p->actif ? 'actif' : 'inactif'),
            'discipline'            => $p->discipline ?? 'col',
            'shop_synced'           => (bool)($p->shop_synced ?? false),
            'region'                => $p->region,
            'lieu_depart'           => $p->lieu_depart,
            'pente_moyenne'         => $p->pente_moyenne !== null ? (float)$p->pente_moyenne : null,
            'pente_max'             => $p->pente_max     !== null ? (float)$p->pente_max     : null,
            'duree_estimee'         => $p->duree_estimee,
            'periode_ouverture'     => $p->periode_ouverture,
            'gpx_url'               => $p->gpx_url,
            'url_www'               => $p->url_www,
            'points_interet'        => $p->points_interet,
            'conseils_pratiques'    => $p->conseils_pratiques,
            // Alias altitude/denivele pour compatibilité carte
            'altitude'              => $p->altitude   !== null ? (int)$p->altitude   : null,
            'denivele'              => $p->denivele   !== null ? (int)$p->denivele   : null,

            // ── Machine à états GPS v6 ───────────────────────
            'tracking_config'       => [
                'wake_distance_m'                => $tc->wake_distance_m                ?? 2000,
                'high_accuracy_start_distance_m' => $tc->high_accuracy_start_distance_m ?? 300,
                'pulse_high_accuracy_duration_s' => $tc->pulse_high_accuracy_duration_s ?? 10,
                'pulse_low_accuracy_duration_s'  => $tc->pulse_low_accuracy_duration_s  ?? 45,
                'finish_high_accuracy_distance_m'=> $tc->finish_high_accuracy_distance_m?? 400,
                'cooldown_distance_m'            => $tc->cooldown_distance_m            ?? 150,
                'cooldown_time_s'                => $tc->cooldown_time_s                ?? 40,
                'strava_dedup_window_minutes'    => $tc->strava_dedup_window_minutes    ?? 120,
            ],
        ];
    }

    private function haversine(float $lat1, float $lng1, float $lat2, float $lng2): float
    {
        $R = 6371;
        $dLat = deg2rad($lat2 - $lat1);
        $dLon = deg2rad($lng2 - $lng1);
        $a = sin($dLat/2)**2 + cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * sin($dLon/2)**2;
        return $R * 2 * atan2(sqrt($a), sqrt(1-$a));
    }
}
