<?php

namespace classes;

require_once WD . '/modules/html2pdf/html2pdf_templates.php';

use Exception;
use html2pdf\html2pdf_templates;
use interfaces\pdf_generator_i;
use Spipu\Html2Pdf\Exception\Html2PdfException;
use Spipu\Html2Pdf\Html2Pdf;

class pdf_generator implements pdf_generator_i
{
    /**
     * The templates
     * @var html2pdf_templates $templates The templates
     */
    public html2pdf_templates $templates;
    /**
     * The HTML content to be converted to PDF
     * @var string $html HTML content to be converted to PDF
     */
    protected string $html;
    /**
     * The filename to save the PDF as
     * @notation When the filename is null, the PDF will be stored as a file with a random name.
     * @var string|null $filename Filename to save the PDF as
     */
    protected string|null $filename;
    /**
     * The path where the module is located
     * @notation The path must end with a /
     * @var string $module_path Path to module files (Example: /var/www/html/pdf/)
     */
    protected string $module_path;
    /**
     * The path where the PDF will be saved (temporary path)
     * @notation This is automatically generated and should not be changed
     * @var string $pdf_path
     */
    protected string $pdf_path;
    /**
     * The tmp pdf location
     * @notation This is automatically generated and should not be changed
     * @var string $tmp_file_path (Example: /tmp/filename.pdf)
     */
    protected string $tmp_file_path;
    /**
     * The path where the templates are located
     * @notation The path must end with a /
     * @var string $template_path Path to templates (Example: /var/www/html/pdf/templates/)
     */
    protected string $template_path;

    /**
     * The unique ID for the tmp file
     * @var string $tmp_file_unique_id The unique ID for the tmp file
     * @notation This is automatically generated and should not be changed
     * @see generate_tmp_file_unique_id()
     * @see get_temp_file_path()
     * @see store_tmp_file()
     * @see is_temp_file_path_set()
     */
    protected string $tmp_file_unique_id;
    /**
     * The HTML2PDF object
     * @var Html2Pdf $html2pdf HTML2PDF object
     */
    protected Html2Pdf $html2pdf;

    /**
     * The constructor for the pdf_generator class
     * @notation This function is used to initialize the class and set the paths
     * @throws Exception If the paths are not valid, writeable or readable
     */
    public function __construct()
    {
        $this->html = '';
        $this->filename = null;
        $this->module_path = WD . '/modules/html2pdf/';
        $this->template_path = $this->module_path . 'templates/';
        $this->html2pdf = new Html2Pdf(
            'P',
            'A4',
            'en',
            true,
            'UTF-8',
            array(0, 0, 0, 0)
        );
        $this->html2pdf->pdf->setDisplayMode('fullpage');
        $this->templates = new html2pdf_templates();
        //self::create_tmp_dir();
        self::require_valid_paths([$this->template_path, $this->module_path]);
    }

    /**
     * @inheritDoc
     */
    public function require_valid_paths(array $paths): bool
    {
        if (!self::validate_paths($paths)) {
            foreach ( $paths as $path ) {
                if (!self::validate_paths(array($path))) {
                    // Check if the path is valid
                    if (!is_dir($path)) {
                        throw new Exception('The path ' . $path . ' is not valid');
                    }
                    // Check if the path is readable
                    if (!is_readable($path)) {
                        throw new Exception('The path ' . $path . ' is not readable');
                    }
                }
            }
        }
        return true;
    }

    /**
     * @inheritDoc
     */
    public function validate_paths(array $paths): bool
    {
        // Check if the paths are valid
        foreach ( $paths as $path ) {
            if (!is_dir($path) || !is_readable($path)) {
                return false;
            }
        }
        return true;
    }

    /**
     * @inheritDoc
     */
    public function is_writeable(string $path): bool
    {
        // Check if the path is writeable
        if (is_writable($path)) {
            return true;
        }
        return false;
    }

    /**
     * Add the HTML content to be converted to PDF
     * @param string $html HTML content to be converted to PDF
     * @return self
     */
    public function add_html(string $html): self
    {
        $this->html .= $html;
        return $this;
    }

    /**
     * Set the filename to save the PDF as (While the file is stored in a temporary location)
     * @param string|null $filename Filename to save the PDF as
     * @return self
     */
    public function set_filename(string|null $filename): self
    {
        $this->filename = $filename;
        return $this;
    }

    /**
     * @inheritDoc
     * @throws Html2PdfException If there is an error generating the PDF
     * @throws Exception
     */
    public function generate_pdf(): string
    {
        // Set the HTML content
        $this->html2pdf->writeHTML($this->html);

        // Generate a random tmp file name if the filename is not set
        self::generate_tmp_file();

        // Set the path to save the PDF
        $pdf_file = $this->pdf_path;

        // Output the PDF to a file
        $this->html2pdf->output($pdf_file, 'F');

        // Set the path to the tmp file
        $this->tmp_file_path = $pdf_file;

        self::store_tmp_file();

        return $this->tmp_file_path;
    }

    /**
     * @inheritDoc
     */
    public function generate_tmp_file(): pdf_generator_i
    {
        // Generate a random filename
        $this->filename = uniqid() . '.pdf';
        // Set the path to save the PDF
        $this->pdf_path = '/tmp/' . $this->filename;
        return $this;
    }

    /**
     * @inheritDoc
     */
    public function store_tmp_file(): pdf_generator_i
    {
        // Check if the tmp_file_path is set
        if (!self::is_temp_file_path_set()) {
            throw new Exception('The tmp_file_path is not set, cannot store unknown file');
        }
        // Save the PDF to the minio store
        $pdf_store = new pdf_store();
        $pdf_store->upload(self::generate_tmp_file_unique_id(), self::get_temp_file_path());
        // Delete the tmp file
        if (file_exists($this->tmp_file_path)) {
            unlink($this->tmp_file_path);
        }
        // Set the path to the tmp file
        $this->tmp_file_path = $pdf_store->get_pdf_key_name($this->tmp_file_unique_id);
        return $this;
    }

    /**
     * Check if the tmp_file_path is set
     * @notation This function checks if the tmp_file_path is set
     * @return bool True if the tmp_file_path is set, false otherwise
     * @see is_temp_file_path_set()
     * @see store_tmp_file()
     * @see generate_tmp_file()
     */
    private function is_temp_file_path_set(): bool
    {
        // Check if the tmp_file_path is set
        if (!empty($this->tmp_file_path)) {
            return true;
        }
        return false;
    }

    /**
     * Generate a unique ID for the tmp file
     * @notation This function generates a unique ID for the tmp file
     * @return string The unique ID for the tmp file
     */
    private function generate_tmp_file_unique_id(): string
    {
        // Generate a unique ID for the tmp file (numbers only)
        $unique_id = uniqid();
        // Set the unique ID for the tmp file
        $this->tmp_file_unique_id = (string)$unique_id;
        return $this->tmp_file_unique_id;
    }

    /**
     * Get the path to the tmp file
     * @notation This function gets the path to the tmp file
     * @return string The path to the tmp file
     * @throws Exception If the tmp_file_path is not set
     */
    public function get_temp_file_path(): string
    {
        // Check if the tmp_file_path is set
        if (!self::is_temp_file_path_set()) {
            throw new Exception('The tmp_file_path is not set, cannot get unknown file');
        }
        return $this->tmp_file_path;
    }
}