<?php
declare(strict_types=1);

namespace ROITheme\Public\AdsensePlacement\Infrastructure\Services;

use ROITheme\Public\AdsensePlacement\Infrastructure\Ui\AdsensePlacementRenderer;

/**
 * Inyecta anuncios dentro del contenido del post
 * via filtro the_content
 *
 * Soporta:
 * - Modo aleatorio (random) con posiciones variables
 * - Configuracion de 1-8 ads maximo
 * - Espacio minimo entre anuncios
 */
final class ContentAdInjector
{
    public function __construct(
        private array $settings,
        private AdsensePlacementRenderer $renderer
    ) {}

    /**
     * Filtra the_content para insertar anuncios
     */
    public function inject(string $content): string
    {
        if (!($this->settings['behavior']['post_content_enabled'] ?? false)) {
            return $content;
        }

        // Verificar longitud minima
        $minLength = (int)($this->settings['forms']['min_content_length'] ?? 500);
        if (strlen(strip_tags($content)) < $minLength) {
            return $content;
        }

        // Obtener configuracion
        $minAds = (int)($this->settings['behavior']['post_content_min_ads'] ?? 1);
        $maxAds = (int)($this->settings['behavior']['post_content_max_ads'] ?? 3);
        $afterParagraphs = (int)($this->settings['behavior']['post_content_after_paragraphs'] ?? 3);
        $minBetween = (int)($this->settings['behavior']['post_content_min_paragraphs_between'] ?? 4);
        $randomMode = ($this->settings['behavior']['post_content_random_mode'] ?? true) === true;

        // Validar min <= max
        if ($minAds > $maxAds) {
            $minAds = $maxAds;
        }

        // Dividir contenido en parrafos
        $paragraphs = $this->splitIntoParagraphs($content);
        $totalParagraphs = count($paragraphs);

        // Necesitamos al menos afterParagraphs + 1 parrafos
        if ($totalParagraphs < $afterParagraphs + 1) {
            return $content;
        }

        // Calcular posiciones de insercion
        $adPositions = $this->calculateAdPositions(
            $totalParagraphs,
            $afterParagraphs,
            $minBetween,
            $minAds,
            $maxAds,
            $randomMode
        );

        if (empty($adPositions)) {
            return $content;
        }

        // Reconstruir contenido con anuncios insertados
        return $this->buildContentWithAds($paragraphs, $adPositions);
    }

    /**
     * Divide el contenido en parrafos preservando el HTML
     */
    private function splitIntoParagraphs(string $content): array
    {
        // Dividir por </p>, pero mantener el tag
        $parts = preg_split('/(<\/p>)/i', $content, -1, PREG_SPLIT_DELIM_CAPTURE);

        $paragraphs = [];
        $current = '';

        foreach ($parts as $part) {
            $current .= $part;
            if (strtolower($part) === '</p>') {
                $paragraphs[] = $current;
                $current = '';
            }
        }

        // Si hay contenido restante (sin cerrar), agregarlo
        if (!empty(trim($current))) {
            $paragraphs[] = $current;
        }

        return $paragraphs;
    }

    /**
     * Calcula las posiciones donde insertar anuncios
     *
     * @return int[] Indices de parrafos despues de los cuales insertar ads
     */
    private function calculateAdPositions(
        int $totalParagraphs,
        int $afterFirst,
        int $minBetween,
        int $minAds,
        int $maxAds,
        bool $randomMode
    ): array {
        // Calcular posiciones disponibles respetando el espacio minimo
        $availablePositions = [];
        $lastPosition = $afterFirst; // Primera posicion fija

        // La primera posicion siempre es despues del parrafo indicado
        if ($afterFirst < $totalParagraphs) {
            $availablePositions[] = $afterFirst;
        }

        // Calcular posiciones adicionales respetando minBetween
        $nextPosition = $afterFirst + $minBetween;
        while ($nextPosition < $totalParagraphs - 1) { // -1 para no insertar al final
            $availablePositions[] = $nextPosition;
            $nextPosition += $minBetween;
        }

        // Determinar cuantos ads insertar
        $maxPossible = count($availablePositions);
        if ($maxPossible === 0) {
            return [];
        }

        // Limitar por maxAds y lo que el contenido permite
        $actualMax = min($maxAds, $maxPossible);
        $actualMin = min($minAds, $actualMax);

        // Determinar cantidad final de ads
        if ($randomMode) {
            // En modo random, elegir cantidad aleatoria entre min y max
            $numAds = rand($actualMin, $actualMax);
        } else {
            // En modo fijo, usar el maximo posible
            $numAds = $actualMax;
        }

        if ($numAds === 0) {
            return [];
        }

        // Seleccionar posiciones
        if ($randomMode && $numAds < $maxPossible) {
            // Modo random: elegir posiciones aleatorias
            // Siempre incluir la primera posicion
            $selectedPositions = [$availablePositions[0]];

            if ($numAds > 1) {
                // Elegir aleatoriamente del resto
                $remainingPositions = array_slice($availablePositions, 1);
                shuffle($remainingPositions);
                $additionalPositions = array_slice($remainingPositions, 0, $numAds - 1);
                $selectedPositions = array_merge($selectedPositions, $additionalPositions);
            }

            // Ordenar para insertar en orden correcto
            sort($selectedPositions);
            return $selectedPositions;
        } else {
            // Modo fijo o todas las posiciones necesarias
            return array_slice($availablePositions, 0, $numAds);
        }
    }

    /**
     * Reconstruye el contenido insertando anuncios en las posiciones indicadas
     */
    private function buildContentWithAds(array $paragraphs, array $adPositions): string
    {
        $newContent = '';
        $adsInserted = 0;

        foreach ($paragraphs as $index => $paragraph) {
            $newContent .= $paragraph;

            // Verificar si debemos insertar un ad despues de este parrafo
            // El indice es 0-based, las posiciones son 1-based (parrafo #3 = index 2)
            $paragraphNumber = $index + 1;

            if (in_array($paragraphNumber, $adPositions, true)) {
                $adsInserted++;
                $adHtml = $this->renderer->renderSlot($this->settings, 'post-content-' . $adsInserted);
                $newContent .= $adHtml;
            }
        }

        return $newContent;
    }
}
