<?php

namespace Devrabiul\ToastMagic;

use Exception;
use Illuminate\Contracts\Config\Repository;
use Illuminate\Session\SessionManager as Session;
use Illuminate\Config\Repository as Config;
use Illuminate\Support\MessageBag;
use Illuminate\Support\Facades\File;

/**
 * Class ToastMagic
 *
 * This class handles the management of toast notifications in a Laravel application.
 * It provides methods to add flash messages to the session and render the necessary
 * styles and scripts for displaying these messages.
 */
class ToastMagic
{
    /**
     * The session manager.
     *
     * @var \Illuminate\Session\SessionManager
     */
    protected $session;

    /**
     * The Config Handler.
     *
     * @var Repository
     */
    protected $config;

    /**
     * The messages stored in the session.
     *
     * @var array
     */
    protected array $messages = [];

    /**
     * The JavaScript type for script tags.
     *
     * @var string
     */
    protected string $jsType = 'text/javascript';

    /**
     * ToastMagic constructor.
     *
     * @param Session $session The session manager instance.
     * @param Config $config The configuration repository instance.
     */
    function __construct(Session $session, Config $config)
    {
        $this->session = $session;
        $this->config = $config;
    }

    /**
     * Generate the HTML for the required styles.
     *
     * @return string The HTML link tag for the stylesheet.
     */
    public function styles(): string
    {
        $stylePath = 'packages/devrabiul/laravel-toaster-magic/css/laravel-toaster-magic.css';
        if (File::exists(public_path($stylePath))) {
            return '<link rel="stylesheet" href="' . $this->getDynamicAsset($stylePath) . '">';
        }
        return '<link rel="stylesheet" href="' . url('vendor/devrabiul/laravel-toaster-magic/assets/css/laravel-toaster-magic.css') . '">';
    }

    /**
     * Generate the HTML for the required scripts.
     *
     * @return string The HTML link tag for the scripts.
     */
    public function scriptsPath(): string
    {
        $config = (array)$this->config->get('laravel-toaster-magic');
        $scripts = [];

        if (!empty($config['livewire_enabled'])) {
            $file1 = 'packages/devrabiul/laravel-toaster-magic/js/livewire-v3/laravel-toaster-magic.js';
            $file2 = 'packages/devrabiul/laravel-toaster-magic/js/livewire-v3/livewire-toaster-magic-v3.js';
            if (File::exists(public_path($file1)) && File::exists(public_path($file2))) {
                $scripts[] = $this->scriptTag($file1);
                $scripts[] = $this->scriptTag($file2);
            }
            return implode('', $scripts);
        }

        $defaultJsPath = 'packages/devrabiul/laravel-toaster-magic/js/laravel-toaster-magic.js';
        if (File::exists(public_path($defaultJsPath))) {
            return $this->scriptTag($defaultJsPath);
        }

        return $this->scriptTag('vendor/devrabiul/laravel-toaster-magic/assets/js/laravel-toaster-magic.js');
    }

    private function scriptTag(string $src): string
    {
        return '<script src="' . $this->getDynamicAsset($src) . '"></script>';
    }

    private function getDynamicAsset(string $path): string
    {
        if (config('laravel-toaster-magic.system_processing_directory') == 'public') {
            $position = strpos($path, 'public/');
            $result = $path;
            if ($position === 0) {
                $result = preg_replace('/public/', '', $path, 1);
            }
        } else if (
            (str_contains(realpath(public_path()), 'public\public') ||
            str_contains(realpath(public_path()), 'public/public')) &&
            PHP_OS_FAMILY === 'Windows'
        ) {
            $result = 'public/' . $path;
        } else {
            $result = in_array(request()->ip(), ['127.0.0.1']) && (config('laravel-toaster-magic.system_processing_directory') != 'root') ? $path : 'public/' . $path;
        }

        return asset($result);
    }

    /**
     * Generate the HTML for the required scripts and initialize toast messages.
     *
     * @return string The HTML script tags for the JavaScript.
     */
    public function scripts(): string
    {
        $messages = $this->session->get('laravel-toaster-magic::messages');

        if (!$messages) $messages = [];

        $config = (array)$this->config->get('laravel-toaster-magic.options');

        $script = $this->scriptsPath();
        $script .= '<script type="' . $this->jsType . '">';

        // Output the config as a global JS object
        $script .= 'window.toastMagicConfig = ' . json_encode($config, JSON_UNESCAPED_SLASHES) . ';';

        $script .= 'document.addEventListener("DOMContentLoaded", function() {';
        $script .= 'var toastContainer = document.querySelector(".toast-container");';

        if (isset($config['theme']) && $config['theme'] != 'default') {
            $script .= 'if (toastContainer) {';
            $script .= 'toastContainer.classList.remove("theme-default");';
            $script .= 'toastContainer.classList.add("theme-' . $config['theme'] . '");';
            $script .= '}';
        }

        if (isset($config['gradient_enable']) && $config['gradient_enable']) {
            $script .= 'if (toastContainer) {';
            $script .= 'toastContainer.classList.add("toast-gradient-enable");';
            $script .= '}';
        }

        if (isset($config['color_mode']) && $config['color_mode']) {
            $script .= 'if (toastContainer) {';
            $script .= 'toastContainer.classList.add("toast-color-true");';
            $script .= '}';
        }

        $script .= 'if (toastContainer) {';
        $script .= 'toastContainer.classList.remove("toast-top-start");';
        $script .= 'toastContainer.classList.remove("toast-top-end");';
        $script .= 'toastContainer.classList.remove("toast-top-center");';
        $script .= 'toastContainer.classList.remove("toast-bottom-start");';
        $script .= 'toastContainer.classList.remove("toast-bottom-end");';
        $script .= 'toastContainer.classList.remove("toast-bottom-center");';
        $script .= 'toastContainer.classList.add("' . ($config['positionClass'] ?? "toast-top-end") . '");';
        $script .= '}';

        $delay = 0; // Initial delay of 0ms

        foreach ($messages as $message) {

            if (count($message['options'])) {
                $config = array_merge($config, $message['options']);
            }

            // Add a delay for each message
            $messageText = $message['message'] ?? '';
            $descriptionText = $message['description'] ?? '';


            // Replace 2 or more consecutive newlines with a single newline
            $messageText = preg_replace("/(\r\n|\r|\n){2,}/", "\n", $messageText);
            $descriptionText = preg_replace("/(\r\n|\r|\n){2,}/", "\n", $descriptionText);

            // Then convert remaining single newlines to <br>
            $messageText = str_replace(["\r\n", "\r", "\n"], '<br>', $messageText);
            $descriptionText = str_replace(["\r\n", "\r", "\n"], '<br>', $descriptionText);
            $descriptionText = str_replace('\n', '<br>', $descriptionText);

            // json_encode safely escapes quotes for JS
            $script .= 'setTimeout(function() {';
            $script .= 'toastMagic.' . $message['type'] . '('
                . json_encode($messageText) . ', '
                . json_encode($descriptionText) . ', '
                . (!empty($config['closeButton']) ? 'true' : 'false') . ', '
                . json_encode($config['customBtnText'] ?? '') . ', '
                . json_encode($config['customBtnLink'] ?? '') . ');';
            $script .= '}, ' . $delay . ');';

            // Increase the delay for the next message (500ms for each)
            $delay += 1000;
        }

        $script .= '});'; // End of DOMContentLoaded

        $script .= '</script>';

        return $script;
    }


    /**
     * Add a flash message to the session.
     *
     * @param string $type Must be one of info, success, warning, error.
     * @param string $message The flash message content.
     * @param string|null $description Optional description for the message.
     * @param array $options Optional custom options for the message.
     *
     * @return void
     */
    public function add(string $type, string $message, string|null $description = null, array $options = []): void
    {
        $types = ['error', 'info', 'success', 'warning'];

        if (!in_array($type, $types)) {
            $type = 'info'; // fallback 
        }

        $this->messages[] = [
            'type' => $type,
            'message' => $message,
            'description' => $description,
            'options' => $options,
        ];

        $this->session->flash('laravel-toaster-magic::messages', $this->messages);
    }

    /**
     * Add an info flash message to session.
     *
     * @param string $message The flash message content.
     * @param string|null $description
     * @param array $options The custom options.
     *
     * @return void
     */
    public function info(string $message, string|null $description = null, array $options = []): void
    {
        if ($message instanceof MessageBag) {
            $messageString = "";
            foreach ($message->getMessages() as $messageArray) {
                foreach ($messageArray as $currentMessage)
                    $messageString .= $currentMessage . "<br>";
            }

            $this->add('info', rtrim($messageString, "<br>"), $description, $options);
        } else {
            $this->add('info', $message, $description, $options);
        }
    }

    /**
     * Add a success flash message to session.
     *
     * @param string $message The flash message content.
     * @param string|null $description
     * @param array $options The custom options.
     *
     * @return void
     */
    public function success(string $message, string|null $description = null, array $options = []): void
    {
        if ($message instanceof MessageBag) {
            $messageString = "";
            foreach ($message->getMessages() as $messageArray) {
                foreach ($messageArray as $currentMessage)
                    $messageString .= $currentMessage . "<br>";
            }

            $this->add('success', rtrim($messageString, "<br>"), $description, $options);
        } else {
            $this->add('success', $message, $description, $options);
        }
    }

    /**
     * Add a warning flash message to session.
     *
     * @param string $message The flash message content.
     * @param string|null $description
     * @param array $options The custom options.
     *
     * @return void
     */
    public function warning(string $message, string|null $description = null, array $options = []): void
    {
        if ($message instanceof MessageBag) {
            $messageString = "";
            foreach ($message->getMessages() as $messageArray) {
                foreach ($messageArray as $currentMessage)
                    $messageString .= $currentMessage . "<br>";
            }

            $this->add('warning', rtrim($messageString, "<br>"), $description, $options);
        } else {
            $this->add('warning', $message, $description, $options);
        }
    }

    /**
     * Add an error flash message to session.
     *
     * @param string $message The flash message content.
     * @param string|null $description
     * @param array $options The custom options.
     *
     * @return void
     */
    public function error(string $message, string|null $description = null, array $options = []): void
    {
        if ($message instanceof MessageBag) {
            $messageString = "";
            foreach ($message->getMessages() as $messageArray) {
                foreach ($messageArray as $currentMessage)
                    $messageString .= $currentMessage . "<br>";
            }

            $this->add('error', rtrim($messageString, "<br>"), $description, $options);
        } else {
            $this->add('error', $message, $description, $options);
        }
    }


    /**
     * Clear messages
     *
     * @return void
     */
    public function clear(): void
    {
        $this->messages = [];
    }

    /**
     * Set js type to module for using vite
     *
     * @return void
     */
    public function useVite(): void
    {
        $this->jsType = 'module';
    }
}
