<?php

/**
 * Fonctions pour générer des données structurées JSON-LD
 * pour améliorer le SEO et l'affichage dans les moteurs de recherche
 */

/**
 * Génère le JSON-LD pour un objet WebSite
 *
 * @return string JSON-LD pour le site web
 */
function generateWebSiteJsonLd() {
    $baseUrl = getBaseUrl();

    $data = [
        "@context" => "https://schema.org",
        "@type" => "WebSite",
        "name" => SITE_NAME,
        "description" => SITE_DESCRIPTION,
        "url" => $baseUrl,
        "potentialAction" => [
            "@type" => "SearchAction",
            "target" => [
                "@type" => "EntryPoint",
                "urlTemplate" => $baseUrl . "/recherche.php?q={search_term_string}"
            ],
            "query-input" => "required name=search_term_string"
        ],
        "publisher" => [
            "@type" => "Organization",
            "name" => ORGANIZATION_NAME,
            "url" => $baseUrl,
            "logo" => [
                "@type" => "ImageObject",
                "url" => $baseUrl . "/img/logo.png"
            ]
        ]
    ];

    return json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
}

/**
 * Génère le JSON-LD pour un objet VideoObject
 *
 * @param array $videoData Données de la vidéo depuis l'API PeerTube
 * @param array $video Données formatées de la vidéo
 * @return string JSON-LD pour la vidéo
 */
function generateVideoObjectJsonLd($videoData, $video) {
    $baseUrl = getBaseUrl();
    $videoUrl = $baseUrl . "/video.php?id=" . $video['id'];

    // Construire l'URL de la vignette
    $thumbnailUrl = isset($videoData['thumbnailPath'])
        ? PEERTUBE_URL . $videoData['thumbnailPath']
        : $baseUrl . "/img/default-thumbnail.jpg";

    // Formater la durée en format ISO 8601 (PT1H30M pour 1h30min)
    $duration = formatDurationISO8601($video['duration'] ?? 0);

    // Construire les données de base
    $data = [
        "@context" => "https://schema.org",
        "@type" => "VideoObject",
        "name" => $video['title'],
        "description" => !empty($video['description'])
            ? truncateText(strip_tags($video['description']), 300)
            : "Regardez cette vidéo sur " . SITE_NAME,
        "url" => $videoUrl,
        "embedUrl" => PEERTUBE_URL . "/videos/embed/" . $video['id'],
        "inLanguage" => "fr-FR",
        "thumbnailUrl" => $thumbnailUrl,
        "uploadDate" => formatDateISO8601($video['date']),
        "duration" => $duration,
        "publisher" => [
            "@type" => "Organization",
            "name" => SITE_NAME,
            "url" => $baseUrl,
            "logo" => [
                "@type" => "ImageObject",
                "url" => $baseUrl . "/img/logo.png"
            ]
        ]
    ];

    // Ajouter les informations de la chaîne/créateur
    if (!empty($video['channel'])) {
        $data["creator"] = [
            "@type" => "Person",
            "name" => $video['channel'],
            "url" => PEERTUBE_URL . "/c/" . ($videoData['channel']['name'] ?? '')
        ];
    }

    // Ajouter les statistiques d'interaction
    if (isset($video['views']) && $video['views'] > 0) {
        $data["interactionStatistic"] = [
            "@type" => "InteractionCounter",
            "interactionType" => [
                "@type" => "WatchAction"
            ],
            "userInteractionCount" => $video['views']
        ];
    }

    // Ajouter les likes si disponibles
    if (isset($video['likes']) && $video['likes'] > 0) {
        if (!isset($data["interactionStatistic"])) {
            $data["interactionStatistic"] = [];
        } else {
            // Convertir en array si c'était un seul élément
            if (isset($data["interactionStatistic"]["@type"])) {
                $data["interactionStatistic"] = [$data["interactionStatistic"]];
            }
        }

        $data["interactionStatistic"][] = [
            "@type" => "InteractionCounter",
            "interactionType" => [
                "@type" => "LikeAction"
            ],
            "userInteractionCount" => $video['likes']
        ];
    }

    // Ajouter les tags/mots-clés si disponibles
    if (!empty($video['tags'])) {
        $data["keywords"] = implode(", ", $video['tags']);
    }

    // Ajouter les informations de licence si disponibles
    if (isset($videoData['licence']) && !empty($videoData['licence'])) {
        $licenceId = $videoData['licence']['id'];
        $licenceMapping = [
            1 => "https://creativecommons.org/licenses/by/4.0/",
            2 => "https://creativecommons.org/licenses/by-sa/4.0/",
            3 => "https://creativecommons.org/licenses/by-nd/4.0/",
            4 => "https://creativecommons.org/licenses/by-nc/4.0/",
            5 => "https://creativecommons.org/licenses/by-nc-sa/4.0/",
            6 => "https://creativecommons.org/licenses/by-nc-nd/4.0/",
            7 => "https://creativecommons.org/publicdomain/zero/1.0/"
        ];

        if (isset($licenceMapping[$licenceId])) {
            $data["license"] = $licenceMapping[$licenceId];
        }
    }

    // Ajouter la catégorie si disponible
    if (isset($videoData['category']) && !empty($videoData['category'])) {
        $data["genre"] = $videoData['category']['label'] ?? 'Vidéo';
    }

    // Ajouter les dimensions si c'est un short (format portrait)
    if (isset($video['aspectRatio']) && $video['aspectRatio'] <= 1) {
        $data["videoFrameSize"] = "Portrait";
    }

    return json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
}

/**
 * Génère le JSON-LD pour un podcast (PodcastSeries + PodcastEpisode + AudioObject)
 *
 * @param array $episodes Épisodes formatés par getCastopodEpisodes()
 * @return string JSON-LD pour le podcast, chaîne vide si aucun épisode
 */
function generatePodcastJsonLd($episodes) {
    if (empty($episodes)) {
        return '';
    }

    $baseUrl = getBaseUrl();
    $first = $episodes[0];

    // Informations de la série, déduites du flux RSS
    $seriesName = !empty($first['podcastTitle']) ? $first['podcastTitle'] : SITE_NAME;
    $seriesUrl = !empty($first['podcastLink']) ? $first['podcastLink'] : $baseUrl;
    $seriesImage = !empty($first['image']) ? $first['image'] : $baseUrl . '/img/logo.png';
    $webFeed = !empty($first['podcastSlug'])
        ? rtrim(CASTOPOD_URL, '/') . '/@' . $first['podcastSlug'] . '/feed'
        : null;

    $series = [
        "@type" => "PodcastSeries",
        "@id" => $seriesUrl . '#podcast',
        "name" => $seriesName,
        "url" => $seriesUrl,
        "image" => $seriesImage,
        "inLanguage" => "fr-FR",
        "author" => [
            "@type" => "Organization",
            "name" => ORGANIZATION_NAME,
            "url" => $baseUrl
        ],
        "publisher" => [
            "@type" => "Organization",
            "name" => SITE_NAME,
            "url" => $baseUrl,
            "logo" => [
                "@type" => "ImageObject",
                "url" => $baseUrl . "/img/logo.png"
            ]
        ]
    ];

    if ($webFeed) {
        $series["webFeed"] = $webFeed;
    }

    $episodeItems = [];
    foreach ($episodes as $episode) {
        $item = [
            "@type" => "PodcastEpisode",
            "name" => $episode['title'],
            "url" => $episode['link'],
            "partOfSeries" => ["@id" => $seriesUrl . '#podcast']
        ];

        if (!empty($episode['pubDate'])) {
            $item["datePublished"] = formatDateISO8601($episode['pubDate']);
        }
        if (!empty($episode['image'])) {
            $item["image"] = $episode['image'];
        }
        if (!empty($episode['description'])) {
            $item["description"] = truncateText($episode['description'], 300);
        }

        $seconds = parseDurationToSeconds($episode['duration'] ?? '');
        if ($seconds > 0) {
            $item["timeRequired"] = formatDurationISO8601($seconds);
        }

        if (!empty($episode['audioUrl'])) {
            $audio = [
                "@type" => "AudioObject",
                "contentUrl" => $episode['audioUrl']
            ];
            $format = guessAudioEncodingFormat($episode['audioUrl']);
            if ($format !== null) {
                $audio["encodingFormat"] = $format;
            }
            $item["associatedMedia"] = $audio;
        }

        $episodeItems[] = $item;
    }

    $data = [
        "@context" => "https://schema.org",
        "@graph" => array_merge([$series], $episodeItems)
    ];

    return json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
}

/**
 * Convertit une durée iTunes en secondes
 * Formats acceptés : "3600", "90", "MM:SS", "HH:MM:SS"
 *
 * @param string $duration Durée brute issue du flux RSS
 * @return int Durée en secondes (0 si non parsable)
 */
function parseDurationToSeconds($duration) {
    $duration = trim((string) $duration);
    if ($duration === '') {
        return 0;
    }

    if (strpos($duration, ':') !== false) {
        $parts = array_map('intval', explode(':', $duration));
        $seconds = 0;
        foreach ($parts as $part) {
            $seconds = $seconds * 60 + $part;
        }
        return $seconds;
    }

    return max(0, (int) $duration);
}

/**
 * Déduit le type MIME d'un fichier audio depuis son extension
 *
 * @param string $url URL du fichier audio
 * @return string|null Type MIME (audio/mpeg, audio/mp4, audio/ogg) ou null
 */
function guessAudioEncodingFormat($url) {
    $extension = strtolower(pathinfo(parse_url($url, PHP_URL_PATH) ?? '', PATHINFO_EXTENSION));
    $map = [
        'mp3' => 'audio/mpeg',
        'm4a' => 'audio/mp4',
        'mp4' => 'audio/mp4',
        'ogg' => 'audio/ogg',
        'opus' => 'audio/ogg',
        'wav' => 'audio/wav'
    ];

    return $map[$extension] ?? null;
}

/**
 * Génère le JSON-LD pour les fils d'Ariane (BreadcrumbList)
 *
 * @param array $breadcrumbs Tableau des fils d'Ariane [['name' => 'Nom', 'url' => 'URL']]
 * @return string JSON-LD pour les fils d'Ariane
 */
function generateBreadcrumbJsonLd($breadcrumbs) {
    $baseUrl = getBaseUrl();

    $listItems = [];
    foreach ($breadcrumbs as $index => $crumb) {
        $listItems[] = [
            "@type" => "ListItem",
            "position" => $index + 1,
            "name" => $crumb['name'],
            "item" => $crumb['url']
        ];
    }

    $data = [
        "@context" => "https://schema.org",
        "@type" => "BreadcrumbList",
        "itemListElement" => $listItems
    ];

    return json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
}

/**
 * Génère le JSON-LD pour une page de collection de vidéos
 *
 * @param string $name Nom de la collection
 * @param string $description Description de la collection
 * @param array $videos Tableau des vidéos
 * @param string $url URL de la page de collection
 * @return string JSON-LD pour la collection
 */
function generateVideoCollectionJsonLd($name, $description, $videos, $url) {
    $baseUrl = getBaseUrl();

    $videoItems = [];
    foreach ($videos as $video) {
        $videoItems[] = [
            "@type" => "VideoObject",
            "name" => $video['title'],
            "url" => $baseUrl . "/video.php?id=" . $video['id'],
            "thumbnailUrl" => $video['thumbnail'],
            "uploadDate" => formatDateISO8601($video['date']),
            "duration" => formatDurationISO8601($video['duration'] ?? 0)
        ];
    }

    $data = [
        "@context" => "https://schema.org",
        "@type" => "CollectionPage",
        "name" => $name,
        "description" => $description,
        "url" => $url,
        "mainEntity" => [
            "@type" => "ItemList",
            "itemListElement" => $videoItems,
            "numberOfItems" => count($videos)
        ],
        "publisher" => [
            "@type" => "Organization",
            "name" => SITE_NAME,
            "url" => $baseUrl
        ]
    ];

    return json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
}

/**
 * Formate une durée en secondes au format ISO 8601 (PTnHnMnS)
 *
 * @param int $seconds Durée en secondes
 * @return string Durée au format ISO 8601
 */
function formatDurationISO8601($seconds) {
    $hours = floor($seconds / 3600);
    $minutes = floor(($seconds % 3600) / 60);
    $remainingSeconds = $seconds % 60;

    $duration = 'PT';

    if ($hours > 0) {
        $duration .= $hours . 'H';
    }
    if ($minutes > 0) {
        $duration .= $minutes . 'M';
    }
    if ($remainingSeconds > 0 || ($hours === 0 && $minutes === 0)) {
        $duration .= $remainingSeconds . 'S';
    }

    return $duration;
}

/**
 * Formate une date au format ISO 8601
 *
 * @param string $dateString Date à formater
 * @return string Date au format ISO 8601
 */
function formatDateISO8601($dateString) {
    try {
        $date = new DateTime($dateString);
        return $date->format('c'); // Format ISO 8601
    } catch (Exception $e) {
        // En cas d'erreur, retourner la date actuelle
        return (new DateTime())->format('c');
    }
}

/**
 * Tronque un texte à une longueur donnée
 *
 * @param string $text Texte à tronquer
 * @param int $length Longueur maximale
 * @return string Texte tronqué
 */
function truncateText($text, $length = 200) {
    if (strlen($text) <= $length) {
        return $text;
    }

    $truncated = substr($text, 0, $length);
    $lastSpace = strrpos($truncated, ' ');

    if ($lastSpace !== false) {
        $truncated = substr($truncated, 0, $lastSpace);
    }

    return $truncated . '...';
}

/**
 * Obtient l'URL de base du site
 *
 * @return string URL de base
 */
function getBaseUrl() {
    $scheme = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on') ? 'https' : 'http';
    $host = $_SERVER['HTTP_HOST'] ?? 'localhost';
    return $scheme . '://' . $host;
}

/**
 * Génère et affiche un script JSON-LD
 *
 * @param string $jsonLd Données JSON-LD
 */
function outputJsonLd($jsonLd) {
    $nonce = getCspNonce();
    echo '<script type="application/ld+json" nonce="' . $nonce . '">' . "\n" . $jsonLd . "\n" . '</script>' . "\n";
}

?>
