<?php

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;

class ApiDocController extends AbstractController
{
    private const MIME_JSON = 'application/json';
    private const STATUS_401 = 'Non authentifie';
    private const STATUS_403_EVENT = 'Evenement non accessible';
    private const STATUS_403_BILLET = 'Billet non accessible';
    private const STATUS_403_CATEGORY = 'Categorie non accessible';
    private const STATUS_404_EVENT = 'Evenement introuvable';
    private const STATUS_404_BILLET = 'Billet introuvable';
    private const STATUS_404_CATEGORY = 'Categorie introuvable';

    #[Route('/api/doc', name: 'app_api_doc', methods: ['GET'])]
    public function index(): Response
    {
        return $this->render('api/doc.html.twig', [
            'sections' => $this->getApiSpec(),
            'environments' => $this->getEnvironments(),
        ]);
    }

    #[Route('/api/doc/spec.json', name: 'app_api_doc_json', methods: ['GET'])]
    public function spec(): Response
    {
        return $this->json($this->getApiSpec());
    }

    #[Route('/api/doc/insomnia.json', name: 'app_api_doc_insomnia', methods: ['GET'])]
    public function insomnia(Request $request): Response
    {
        $baseUrl = $request->getSchemeAndHttpHost();
        $resources = [];
        $workspaceId = 'wrk_eticket';

        $resources[] = [
            '_type' => 'workspace',
            '_id' => $workspaceId,
            'name' => 'E-Ticket API',
            'description' => 'API E-Ticket - Organisateur & Scanner',
        ];

        $envId = 'env_eticket';
        $resources[] = [
            '_type' => 'environment',
            '_id' => $envId,
            'parentId' => $workspaceId,
            'name' => 'E-Ticket',
            'data' => [
                'base_url' => $baseUrl,
                'env' => 'sandbox',
                'email' => '',
                'password' => '',
                'jwt_token' => '',
            ],
        ];

        $folderIndex = 0;
        foreach ($this->getApiSpec() as $section) {
            ++$folderIndex;
            $folderId = 'fld_'.$folderIndex;

            $resources[] = [
                '_type' => 'request_group',
                '_id' => $folderId,
                'parentId' => $workspaceId,
                'name' => $section['name'],
            ];

            foreach ($section['endpoints'] as $reqIndex => $endpoint) {
                $resources[] = $this->buildInsomniaRequest($endpoint, $folderIndex, $reqIndex, $folderId);
            }
        }

        $export = [
            '_type' => 'export',
            '__export_format' => 4,
            '__export_date' => date('Y-m-d\TH:i:s\Z'),
            '__export_source' => 'eticket.api.doc',
            'resources' => $resources,
        ];

        return new Response(
            json_encode($export, \JSON_PRETTY_PRINT | \JSON_UNESCAPED_UNICODE | \JSON_UNESCAPED_SLASHES),
            200,
            [
                'Content-Type' => self::MIME_JSON,
                'Content-Disposition' => 'attachment; filename="eticket-api-insomnia.json"',
            ]
        );
    }

    /**
     * @param array<string, mixed> $endpoint
     *
     * @return array<string, mixed>
     */
    private function buildInsomniaRequest(array $endpoint, int $folderIndex, int $reqIndex, string $folderId): array
    {
        $isAuthRoute = str_starts_with($endpoint['path'], '/api/auth');
        $isLogin = '/api/auth/login' === $endpoint['path'];
        $url = $isAuthRoute
            ? '{{ _.base_url }}'.$endpoint['path']
            : '{{ _.base_url }}/api/{{ _.env }}'.str_replace('/api', '', $endpoint['path']);

        $headers = [['name' => 'Content-Type', 'value' => self::MIME_JSON]];
        if (!$isLogin) {
            $headers[] = ['name' => 'ETicket-Email', 'value' => '{{ _.email }}'];
            $headers[] = ['name' => 'ETicket-JWT', 'value' => '{{ _.jwt_token }}'];
        }

        $body = $this->buildInsomniaBody($endpoint, $isLogin);

        $req = [
            '_type' => 'request',
            '_id' => 'req_'.$folderIndex.'_'.$reqIndex,
            'parentId' => $folderId,
            'name' => $endpoint['summary'],
            'method' => $endpoint['method'],
            'url' => $url,
            'headers' => $headers,
            'body' => $body,
        ];

        if ($isAuthRoute) {
            $req['afterResponseScript'] = "const res = insomnia.response.json();\nif (res && res.success && res.data && res.data.token) {\n  insomnia.environment.set('jwt_token', res.data.token);\n}";
        }

        return $req;
    }

    /**
     * @param array<string, mixed> $endpoint
     *
     * @return array{mimeType: string, text: string}|null
     */
    private function buildInsomniaBody(array $endpoint, bool $isLogin): ?array
    {
        if ($isLogin) {
            return [
                'mimeType' => self::MIME_JSON,
                'text' => json_encode([
                    'email' => '{{ _.email }}',
                    'password' => '{{ _.password }}',
                ], \JSON_PRETTY_PRINT | \JSON_UNESCAPED_UNICODE),
            ];
        }

        if ($endpoint['request']) {
            $example = [];
            foreach ($endpoint['request'] as $name => $field) {
                $example[$name] = $field['example'] ?? '';
            }

            return [
                'mimeType' => self::MIME_JSON,
                'text' => json_encode($example, \JSON_PRETTY_PRINT | \JSON_UNESCAPED_UNICODE),
            ];
        }

        return null;
    }

    /**
     * @return list<array{name: string, slug: string, baseUrl: string, description: string, badge: string, badgeColor: string}>
     */
    private function getEnvironments(): array
    {
        return [
            [
                'name' => 'Sandbox',
                'slug' => 'sandbox',
                'baseUrl' => '/api/sandbox',
                'description' => 'Environnement de test. Les donnees ne sont pas modifiees. Ideal pour developper et tester votre integration.',
                'badge' => 'TEST',
                'badgeColor' => 'bg-orange-500',
            ],
            [
                'name' => 'Live',
                'slug' => 'live',
                'baseUrl' => '/api/live',
                'description' => 'Environnement de production. Les donnees sont reelles. A utiliser uniquement en production.',
                'badge' => 'PROD',
                'badgeColor' => 'bg-green-600',
            ],
        ];
    }

    /**
     * @return list<array<string, mixed>>
     */
    private function getApiSpec(): array
    {
        return [
            $this->authSection(),
            $this->eventsSection(),
            $this->categoriesSection(),
            $this->billetsSection(),
            $this->scannerSection(),
        ];
    }

    /** @return array<string, mixed> */
    private function authSection(): array
    {
        $tokenResponse = ['success' => ['type' => 'bool', 'example' => true], 'data' => ['type' => 'object', 'example' => '{"token": "eyJ...", "expiresAt": "2026-03-24T12:00:00+00:00"}']];

        return ['name' => 'Authentification', 'description' => 'Route commune aux environnements sandbox et live.', 'endpoints' => [
            ['method' => 'POST', 'path' => '/api/auth/login', 'summary' => 'Obtenir un token JWT', 'description' => 'Authentifie un organisateur et retourne un token JWT valable 24h.', 'headers' => [], 'params' => [], 'request' => ['email' => ['type' => 'string', 'required' => true, 'example' => 'orga@example.com'], 'password' => ['type' => 'string', 'required' => true, 'example' => '********']], 'response' => $tokenResponse, 'statuses' => [200 => 'Token genere avec succes', 401 => 'Identifiants invalides', 429 => 'Trop de tentatives']],
            ['method' => 'GET', 'path' => '/api/auth/login/sso', 'summary' => 'Connexion via SSO (Keycloak)', 'description' => 'Redirige vers la page de connexion Keycloak.', 'headers' => [], 'params' => [], 'request' => null, 'response' => ['redirect' => ['type' => 'string', 'example' => '"https://auth.esy-web.dev/realms/..."']], 'statuses' => [302 => 'Redirection vers Keycloak']],
            ['method' => 'GET', 'path' => '/api/auth/login/sso/validate', 'summary' => 'Callback SSO → JWT', 'description' => 'Echange le code OAuth contre un token JWT E-Ticket.', 'headers' => [], 'params' => ['code' => ['type' => 'string', 'required' => true, 'description' => 'Code OAuth'], 'state' => ['type' => 'string', 'required' => true, 'description' => 'State CSRF']], 'request' => null, 'response' => ['success' => ['type' => 'bool', 'example' => true], 'data' => ['type' => 'object', 'example' => '{"token": "eyJ...", "expiresAt": "...", "email": "orga@example.com"}']], 'statuses' => [200 => 'JWT genere via SSO', 401 => 'SSO echoue', 403 => 'Pas de compte organisateur']],
            ['method' => 'POST', 'path' => '/api/auth/refresh', 'summary' => 'Rafraichir un token expire', 'description' => 'Genere un nouveau JWT a partir d\'un token expire avec signature valide.', 'headers' => $this->authHeaders(), 'params' => [], 'request' => null, 'response' => $tokenResponse, 'statuses' => [200 => 'Nouveau token genere', 400 => 'Token encore valide', 401 => 'Token invalide']],
        ]];
    }

    /** @return array<string, mixed> */
    private function eventsSection(): array
    {
        return ['name' => 'Evenements', 'description' => 'Liste et detail des evenements de l\'organisateur.', 'endpoints' => [
            ['method' => 'GET', 'path' => '/api/events', 'summary' => 'Liste des evenements', 'description' => 'Retourne tous les evenements de l\'organisateur authentifie.', 'headers' => $this->authHeaders(), 'params' => ['page' => ['type' => 'int', 'required' => false, 'default' => 1, 'description' => 'Page courante'], 'limit' => ['type' => 'int', 'required' => false, 'default' => 20, 'description' => 'Nombre par page (max 100)']], 'request' => null, 'response' => ['success' => ['type' => 'bool', 'example' => true], 'data' => ['type' => 'array', 'example' => '[{"id": 1, "title": "Brocante 2026", "startAt": "...", "endAt": "...", "city": "Paris", "isOnline": true}]'], 'meta' => ['type' => 'object', 'example' => '{"page": 1, "limit": 20, "total": 5}']], 'statuses' => [200 => 'Liste retournee', 401 => self::STATUS_401]],
            ['method' => 'GET', 'path' => '/api/events/{id}', 'summary' => 'Detail d\'un evenement', 'description' => 'Retourne les informations completes d\'un evenement.', 'headers' => $this->authHeaders(), 'params' => ['id' => ['type' => 'int', 'required' => true, 'description' => 'ID de l\'evenement']], 'request' => null, 'response' => ['success' => ['type' => 'bool', 'example' => true], 'data' => ['type' => 'object', 'example' => '{"id": 1, "title": "Brocante", "description": "...", "city": "Paris", "imageUrl": "..."}']], 'statuses' => [200 => 'Evenement retourne', 401 => self::STATUS_401, 403 => self::STATUS_403_EVENT, 404 => self::STATUS_404_EVENT]],
        ]];
    }

    /** @return array<string, mixed> */
    private function categoriesSection(): array
    {
        return ['name' => 'Categories', 'description' => 'Categories de billets d\'un evenement.', 'endpoints' => [
            ['method' => 'GET', 'path' => '/api/events/{id}/categories', 'summary' => 'Liste des categories', 'description' => 'Retourne les categories avec dates de vente et visibilite.', 'headers' => $this->authHeaders(), 'params' => ['id' => ['type' => 'int', 'required' => true, 'description' => 'ID de l\'evenement']], 'request' => null, 'response' => ['success' => ['type' => 'bool', 'example' => true], 'data' => ['type' => 'array', 'example' => '[{"id": 1, "name": "General", "position": 0, "startAt": "...", "endAt": "...", "isHidden": false, "isActive": true}]']], 'statuses' => [200 => 'Categories retournees', 401 => self::STATUS_401, 403 => self::STATUS_403_EVENT, 404 => self::STATUS_404_EVENT]],
        ]];
    }

    /** @return array<string, mixed> */
    private function billetsSection(): array
    {
        return ['name' => 'Billets', 'description' => 'Billets d\'une categorie et detail d\'un billet.', 'endpoints' => [
            ['method' => 'GET', 'path' => '/api/categories/{id}/billets', 'summary' => 'Liste des billets', 'description' => 'Retourne les billets avec stock et quantite vendue.', 'headers' => $this->authHeaders(), 'params' => ['id' => ['type' => 'int', 'required' => true, 'description' => 'ID de la categorie']], 'request' => null, 'response' => ['success' => ['type' => 'bool', 'example' => true], 'data' => ['type' => 'array', 'example' => '[{"id": 1, "name": "Entree", "priceHT": 1500, "quantity": 100, "sold": 42, "type": "billet"}]']], 'statuses' => [200 => 'Billets retournes', 401 => self::STATUS_401, 403 => self::STATUS_403_CATEGORY, 404 => self::STATUS_404_CATEGORY]],
            ['method' => 'GET', 'path' => '/api/billets/{id}', 'summary' => 'Detail d\'un billet', 'description' => 'Retourne toutes les informations avec image, categorie et evenement.', 'headers' => $this->authHeaders(), 'params' => ['id' => ['type' => 'int', 'required' => true, 'description' => 'ID du billet']], 'request' => null, 'response' => ['success' => ['type' => 'bool', 'example' => true], 'data' => ['type' => 'object', 'example' => '{"id": 1, "name": "Entree VIP", "priceHT": 2500, "imageUrl": "...", "category": {"id": 3, "name": "Premium"}, "event": {"id": 1, "title": "Brocante"}}']], 'statuses' => [200 => 'Billet retourne', 401 => self::STATUS_401, 403 => self::STATUS_403_BILLET, 404 => self::STATUS_404_BILLET]],
        ]];
    }

    /** @return array<string, mixed> */
    private function scannerSection(): array
    {
        return ['name' => 'Scanner', 'description' => 'Scan de billets pour l\'application mobile.', 'endpoints' => [
            ['method' => 'POST', 'path' => '/api/scan', 'summary' => 'Scanner un billet', 'description' => 'Verifie la reference et l\'etat du billet, le marque comme scanne si valide.', 'headers' => $this->authHeaders(), 'params' => [], 'request' => ['reference' => ['type' => 'string', 'required' => true, 'example' => 'ETICKET-XXXX-XXXX-XXXX']], 'response' => ['success' => ['type' => 'bool', 'example' => true], 'data' => ['type' => 'object', 'example' => '{"state": "accepted", "reason": null, "reference": "ETICKET-XXXX", "billetName": "Entree", "buyerFirstName": "Jean", "buyerLastName": "Dupont", "details": {...}}']], 'statuses' => [200 => 'Scan traite (accepted/refused)', 401 => self::STATUS_401, 404 => self::STATUS_404_BILLET], 'extra' => ['title' => 'Raisons de refus possibles', 'items' => ['already_scanned' => 'Billet deja scanne', 'invalid' => 'Billet invalide (annule ou rembourse)', 'expired' => 'Billet expire', 'exit_definitive' => 'Sortie definitive deja effectuee', 'wrong_event' => 'Billet n\'appartient pas a cet evenement']]],
        ]];
    }

    /**
     * @return list<array{name: string, description: string, required: bool}>
     */
    private function authHeaders(): array
    {
        return [
            ['name' => 'ETicket-Email', 'description' => 'Email de l\'organisateur', 'required' => true],
            ['name' => 'ETicket-JWT', 'description' => 'Token JWT obtenu via /api/auth/login', 'required' => true],
        ];
    }
}
