<?php

/*
# -- BEGIN LICENSE BLOCK ----------------------------------
#
# This file is part of Mage Pattern.
# The toolkit PHP for developer
# Copyright (C) 2012 - 2026 Gerits Aurelien contact[at]gerits-aurelien[dot]be
#
# OFFICIAL TEAM MAGE PATTERN:
#
#   * Gerits Aurelien (Author - Developer) contact[at]gerits-aurelien[dot]be
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
# Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# DISCLAIMER
*/

namespace Magepattern\Component\XML;

use Magepattern\Component\Debug\Logger;
use Magepattern\Component\Tool\DateTool;
use Magepattern\Component\Tool\StringTool;
use XMLWriter;
use RuntimeException;
use Throwable;
/*
 *
 $sitemap = new \Magepattern\Component\XML\Sitemap();
$sitemap->init(__DIR__ . '/sitemap-produits.xml');

// Ajout simple
$sitemap->addUrl('https://site.com/produit-1', '2026-02-01');

// Ajout avec images
$sitemap->addUrl(
    'https://site.com/produit-2',
    'now',
    'daily',
    0.8,
    [
        ['loc' => 'https://site.com/img/p2.jpg', 'caption' => 'Photo de face'],
        ['loc' => 'https://site.com/img/p2-back.jpg', 'title' => 'Dos']
    ]
);

$sitemap->save();

$index = new \Magepattern\Component\XML\Sitemap();
// Le paramètre true indique que c'est un INDEX
$index->init(__DIR__ . '/sitemap_index.xml', true);

$index->addSitemap('https://site.com/sitemap-produits.xml', 'now');
$index->addSitemap('https://site.com/sitemap-blog.xml', 'yesterday');

$index->save();
*/

class Sitemap extends XMLWriter
{
    // Namespaces officiels
    protected const NS_SITEMAP = 'http://www.sitemaps.org/schemas/sitemap/0.9';
    protected const NS_IMAGE   = 'http://www.google.com/schemas/sitemap-image/1.1';
    protected const NS_VIDEO   = 'http://www.google.com/schemas/sitemap-video/1.1';

    // Validation
    protected const VALID_FREQS = [
        'always', 'hourly', 'daily', 'weekly', 'monthly', 'yearly', 'never'
    ];

    /**
     * Constructeur : Vérifie les dépendances.
     */
    public function __construct()
    {
        if (!extension_loaded('xmlwriter')) {
            $e = new RuntimeException('L\'extension PHP xmlwriter est requise.');
            Logger::getInstance()->log($e, "php", "critical");
            throw $e;
        }
    }

    /**
     * Initialise un nouveau fichier Sitemap.
     *
     * @param string $uri Chemin du fichier (ex: /var/www/sitemap.xml)
     * @param bool $isIndex True si c'est un index de sitemaps, False pour des URLs
     * @return bool
     */
    /**
     * Initialise un nouveau fichier Sitemap.
     *
     * @param string $uri Chemin du fichier (ex: /var/www/sitemap.xml)
     * @param bool $isIndex True si c'est un index de sitemaps, False pour des URLs
     * @param bool $hasImages True si le sitemap contiendra des images (ajoute le namespace)
     * @return bool
     */
    public function init(string $uri, bool $isIndex = false, bool $hasImages = false): bool // 🟢 Ajout du 3e paramètre
    {
        try {
            // Création du dossier si inexistant
            $dir = dirname($uri);
            if (!is_dir($dir) && !mkdir($dir, 0755, true) && !is_dir($dir)) {
                throw new RuntimeException("Impossible de créer le dossier : $dir");
            }

            $this->openUri($uri);
            $this->setIndent(true);
            $this->startDocument('1.0', 'UTF-8');
            $this->writeComment('Generated by Magix CMS 4');

            // Choix de la racine (Sitemap Index vs UrlSet)
            $rootElement = $isIndex ? 'sitemapindex' : 'urlset';

            $this->startElement($rootElement);
            $this->writeAttribute('xmlns', self::NS_SITEMAP);

            // 🟢 MODIFICATION ICI : On n'ajoute le namespace que si c'est explicitement demandé
            if (!$isIndex && $hasImages) {
                $this->writeAttribute('xmlns:image', self::NS_IMAGE);
            }

            return true;
        } catch (Throwable $e) {
            Logger::getInstance()->log($e, "xml", "error");
            return false;
        }
    }

    /**
     * Ajoute une URL standard (Page).
     *
     * @param string $loc URL absolue
     * @param string|int $lastMod Date de modif (Compatible DateTool)
     * @param string $changeFreq Fréquence (daily, weekly...)
     * @param float $priority Priorité (0.0 à 1.0)
     * @param array $images Tableau d'images [['loc' => 'url', 'caption' => 'txt'], ...]
     * @return self
     */
    public function addUrl(
        string $loc,
        string|int $lastMod = 'now',
        string $changeFreq = 'weekly',
        float $priority = 0.5,
        array $images = []
    ): self {
        if (!StringTool::isURL($loc)) {
            return $this; // On ignore silencieusement les URLs invalides
        }

        $this->startElement('url');
        $this->writeElement('loc', $loc);

        // Date W3C via notre DateTool
        $this->writeElement('lastmod', DateTool::toW3C($lastMod));

        // Fréquence
        if (in_array($changeFreq, self::VALID_FREQS)) {
            $this->writeElement('changefreq', $changeFreq);
        }

        // Priorité (Clamp entre 0 et 1)
        $priority = max(0.0, min(1.0, $priority));
        $this->writeElement('priority', number_format($priority, 1, '.', ''));

        // Gestion des Images Google
        foreach ($images as $img) {
            $this->addImageNode($img);
        }

        $this->endElement(); // </url>
        return $this;
    }

    /**
     * Ajoute une entrée dans un Index de Sitemaps.
     * (Utilisé pour sitemap_index.xml qui liste sitemap_1.xml, sitemap_2.xml...)
     */
    public function addSitemap(string $loc, string|int $lastMod = 'now'): self
    {
        if (!StringTool::isURL($loc)) return $this;

        $this->startElement('sitemap');
        $this->writeElement('loc', $loc);
        $this->writeElement('lastmod', DateTool::toW3C($lastMod));
        $this->endElement(); // </sitemap>

        return $this;
    }

    /**
     * Finalise et ferme le fichier.
     */
    public function save(): bool
    {
        try {
            $this->endElement(); // Ferme urlset ou sitemapindex
            $this->endDocument();
            $this->flush();
            return true;
        } catch (Throwable $e) {
            Logger::getInstance()->log($e, "xml", "error");
            return false;
        }
    }

    /**
     * Helper interne pour les images
     */
    private function addImageNode(string|array $image): void
    {
        // Support du format simple (string) ou complexe (array)
        $loc = is_array($image) ? ($image['loc'] ?? '') : $image;

        if (empty($loc)) return;

        $this->startElement('image:image');
        $this->writeElement('image:loc', $loc);

        if (is_array($image)) {
            if (!empty($image['caption'])) {
                // Utilisation de CDATA pour éviter de casser le XML avec des caractères spéciaux
                $this->startElement('image:caption');
                $this->writeCData($image['caption']);
                $this->endElement();
            }
            if (!empty($image['title'])) {
                $this->writeElement('image:title', $image['title']);
            }
            if (!empty($image['geo_location'])) {
                $this->writeElement('image:geo_location', $image['geo_location']);
            }
        }

        $this->endElement();
    }
}