<?php

declare(strict_types=1);

namespace Inside\BCLH\Services;

use Barryvdh\DomPDF\Facade as PDF;
use Dompdf\Dompdf;
use Dompdf\Options;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\View;
use Illuminate\Support\Str;
use Inside\Content\Models\Contents\Countries;
use Inside\Content\Models\Contents\DeclinedDocuments;
use Inside\Content\Models\Contents\Documents;
use Inside\Content\Models\Contents\Folders;
use Inside\Content\Models\Contents\ManagementModes;
use Inside\Content\Models\Contents\Pages;
use Inside\Content\Models\Contents\Procedures;
use Inside\Content\Models\Contents\Rops;
use Inside\Content\Models\Contents\Sops;
use Webklex\PDFMerger\Facades\PDFMergerFacade as PDFMerger;

final class PdfService
{
    public function generatePdf(string $contentType, string $uuid, bool $update): void
    {
        switch ($contentType) {
            case 'folders':
                $folder = Folders::query()->find($uuid);

                $view = 'folder';
                $filename = 'pdf/folders/'.$uuid.'.pdf';

                $data = ['folder' => [
                    'uuid' => $folder->uuid,
                    'title' => $folder->title,
                    'langcode' => $folder->langcode,
                    'type' => 'folder',
                ]];
                break;
            case 'procedures':
                $view = 'sop';

                $content = Procedures::query()->find($uuid);
                $filename = 'pdf/procedures/'.$uuid.'.pdf';

                $data = ['sop' => $this->formatContentData($content, $contentType)];

                if (empty($data['sop'])) {
                    return;
                }

                break;
            case 'sops':
            case 'rops':
                $view = 'sop';

                if ($contentType === 'sops') {
                    $content = Sops::query()->find($uuid);
                    $filename = 'pdf/sops/'.$uuid.'.pdf';
                } else {
                    $content = Rops::query()->find($uuid);
                    $filename = 'pdf/rops/'.$uuid.'.pdf';
                }

                $data = ['sop' => $this->formatContentData($content, $contentType)];

                if (empty($data['sop'])) {
                    return;
                }

                break;
            case 'documents':
            case 'declined_documents':
                $view = 'document';

                if ($contentType === 'documents') {
                    $content = Documents::query()->find($uuid);
                    $filename = 'pdf/documents/'.$uuid.'.pdf';
                } else {
                    $content = DeclinedDocuments::query()->find($uuid);
                    $filename = 'pdf/declined_documents/'.$uuid.'.pdf';
                }

                $data = ['document' => $this->formatContentData($content, $contentType)];

                if (empty($data['document'])) {
                    return;
                }

                break;
            case 'pages':
                $view = 'page';
                $content = Pages::query()->find($uuid);
                $filename = 'pdf/pages/'.$uuid.'.pdf';

                $data = ['page' => $this->formatContentData($content, $contentType)];

                if (empty($data['page'])) {
                    return;
                }

                break;
            default:
                return;
        }

        try {
            $this->createPdf($view, $filename, $data, $update);
        } catch (\Throwable $e) {
        }
    }

    public function createPdf(string $view, string $filename, ?array $data = null, bool $update = false, int $dpi = 0): void
    {
        if (! $update && Storage::disk('protected')->exists($filename)) {
            return;
        }

        $view = View::make($view, $data ?? []);
        $html = $view->render();
        /** @var string $html */
        $html = preg_replace('/>\s+</', '><', $html);

        if ($dpi) {
            $options = new Options();
            $options->setDpi($dpi);
            $options->setFontDir(cms_base_path().'/vendor/maecia/bclh-back/resources/fonts');
            $options->setChroot(cms_base_path());
            $dompdf = new Dompdf($options);
            $dompdf->loadHtml($html);
            $dompdf->render();
            Storage::disk('protected')->put($filename, $dompdf->output() ?? '');
            $dompdf = null;
            unset($dompdf);
        } else {
            $pdf = PDF::loadHtml($html);
            Storage::disk('protected')->put($filename, $pdf->download()->getOriginalContent());
            $pdf = null;
            unset($pdf);
        }
    }

    public function updatePdf(string $view, string $filename, ?array $data = null): void
    {
        $this->createPdf($view, $filename, $data, true);
    }

    public function mergePdfs(array $pdfs, string $filename): void
    {
        $pdfMerger = PDFMerger::init();

        foreach ($pdfs as $pdf) {
            if (! file_exists($pdf['filename'])) {
                $this->createPdf($pdf['view'], $pdf['filename'], $pdf['data'], $pdf['update']);
            }

            $pdfMerger->addPDF('storage/app/protected/'.$pdf['filename']);
        }

        $pdfMerger->merge();
        $pdfMerger->save($filename);

        $pdfs = null;
        $pdfMerger = null;
    }

    /**
     * @param mixed $content
     * @param string $contentType
     * @return array|false
     */
    public function formatContentData($content, string $contentType)
    {
        $sections = [];

        foreach ($content->sectionContent as $section) {
            $type = str_replace('inside_section_', '', $section->getTable());

            switch ($type) {
                case 'accordion':
                    $sections[] = [
                        'type' => 'accordion',
                        'question' => $section->question,
                        'answer' => $this->convertImages((string) $section->answer),
                    ];
                    break;
                case 'text':
                    $sections[] = [
                        'type' => 'text',
                        'body' => $this->convertImages((string) $section->body, $content),
                    ];
                    break;
                case 'image':
                    $sections[] = [
                        'type' => 'image',
                        'body' => $this->convertImages(sprintf('<p><img src="%s" /></p>', '/'.env('APP_STORAGE_PATH').'/'.$section->image)),
                    ];
                    break;
                case 'text_with_image':
                    $sections[] = [
                        'type' => 'text_with_image',
                        'image' => $this->convertImages(sprintf('<p><img src="%s" /></p>', '/'.env('APP_STORAGE_PATH').'/'.$section->image)),
                        'body' => $this->convertImages((string) $section->body, $content),
                        'display' => $section->display,
                    ];
                    break;
                case 'steps':
                    $steps = [];

                    $position = 'left';
                    foreach ($section->sectionContent as $step) {
                        $path = isset($step->image) && ! empty($step->image) && Storage::disk('local')->exists($step->image) ? Storage::path($step->image) : false;
                        $steps[] = [
                            'title' => $step->title,
                            'body' => trim($step->body ?? ''),
                            'image' => ! empty($path) ? 'data:'.mime_content_type($path).';base64,'.base64_encode(file_get_contents($path) ?: '') : '',
                            'position' => $position,
                        ];

                        if ($position === 'left') {
                            $position = 'right';
                        } else {
                            $position = 'left';
                        }
                    }

                    $sections[] = [
                        'type' => 'steps',
                        'steps' => $steps,
                    ];
                    break;
            }
        }

        $format = $content->langcode === 'en' ? 'Y-m-d' : 'd/m/Y';

        $documentData = [
            'uuid' => $content->uuid,
            'title' => $content->title,
            'langcode' => $content->langcode,
            'type' => 'sop',
            'contents' => $sections,
            'is_rop' => false,
            'date' => date($format, $content->updated_at),
        ];

        switch ($contentType) {
            case 'procedures':
                if (is_null($content->proceduresCategories)) {
                    return false;
                }

                $documentData['folder'] = $content->proceduresCategories->count() === 0 ? '' : $content->proceduresCategories->first()->title;
                break;
            case 'sops':
            case 'documents':
                if (is_null($content->folders)) {
                    return false;
                }

                $documentData['folder'] = $content->folders->count() === 0 ? '' : $content->folders->first()->title;
                break;
            case 'declined_documents':
                $documentData['folder'] = '';

                if ($content->documents->count() > 0 && $content->documents->first()->folders->count() > 0) {
                    $documentData['folder'] = $content->documents->first()->folders->first()->title;
                }

                break;
            case 'rops':
                if ($content->folders->count() === 0 && $content->sops->count() === 0) {
                    $documentData['folder'] = '';
                } else {
                    $documentData['folder'] = $content->folders->count() ? $content->folders->first()->title : $content->sops->first()->folders->first()->title;
                }

                $documentData['is_rop'] = true;
                break;
        }

        switch ($contentType) {
            case 'procedures':
            case 'sops':
            case 'rops':
                $contacts = $content->users;
                $countries = $content->countries;
                $managementModes = $content->managementModes;

                $contact = false;
                $country = false;

                if ($contacts->count()) {
                    $contact = [
                        'firstname' => $contacts->first()->firstname,
                        'lastname' => $contacts->first()->lastname,
                        'email' => $contacts->first()->email,
                    ];
                }

                $query = Countries::withoutGlobalScopes();
                $query->where('langcode', $content->langcode);

                $totalCountries = $query->count();
                $countriesArray = [];

                if ($countries->count()) {
                    foreach ($countries as $country) {
                        $countriesArray[] = $country->title;
                    }
                }

                if (count($countriesArray) < 3) {
                    $country = implode(', ', $countriesArray);
                } elseif (count($countriesArray) < $totalCountries) {
                    $country = implode(', ', array_slice($countriesArray, 0, 2)).' + '.(count($countriesArray) - 2);
                } elseif (count($countriesArray) === $totalCountries) {
                    $country = 'Tous';
                }

                $query = ManagementModes::withoutGlobalScopes();
                $query->where('langcode', $content->langcode);

                $totalManagementModes = $query->count();
                $managementModesArray = [];

                if ($managementModes->count()) {
                    foreach ($managementModes as $managementMode) {
                        $managementModesArray[] = $managementMode->title;
                    }
                }

                if (count($managementModesArray) < 3) {
                    $managementMode = implode(', ', $managementModesArray);
                } elseif (count($managementModesArray) < $totalManagementModes) {
                    $managementMode = implode(', ', array_slice($managementModesArray, 0, 2)).' + '.(count($managementModesArray) - 2);
                } elseif (count($managementModesArray) === $totalManagementModes) {
                    $managementMode = 'Tous';
                }

                $documentData = array_merge($documentData, [
                    'type' => 'sop',
                    'goal' => $this->convertImages((string) $content->goal, $content),
                    'standard' => $this->convertImages((string) $content->standard, $content),
                    'contact' => $contact,
                    'country' => $country,
                    'managementMode' => $managementMode ?? '',
                ]);
                break;
            case 'documents':
            case 'declined_documents':
                $documentData = array_merge($documentData, [
                    'type' => 'document',
                    'introduction' => $this->convertImages((string) $content->introduction, $content),
                ]);
                break;
        }

        return $documentData;
    }

    protected function handleWWImage(string $url, mixed $content): ?string
    {
        $images = $content->images;
        if (empty($images) || $images->isEmpty()) {
            return null;
        }
        $image = $images->where('url', $url)->first();
        if (! $image) {
            return null;
        }

        return $image->path;
    }

    public function convertImages(string $text, mixed $content = null): ?string
    {
        $text = $this->applyBordersForTables($text);
        if (! $text) {
            return '';
        }
        /**
         * @var string $text
         */
        $text = preg_replace('#<li>\s*<p([^>]*)>(.*?)<\/p>\s*<\/li>#i', '<li>$2</li>', $text);

        preg_match_all('/< *img[^>]*src *= *["\']?([^"\']*)/i', $text, $matches);

        if (isset($matches[1])) {
            foreach ($matches[1] as $url) {
                $WWImage = Str::contains($url, 'wysiwyg');

                /** @var string|null $path */
                $path = $WWImage ? $this->handleWWImage($url, $content) :
                    str_replace('/storage', 'storage', $url);

                if (! $path || ! file_exists($path) || is_dir($path)) {
                    continue;
                }

                $base64 = 'data:'.mime_content_type($path).';base64,'.base64_encode(file_get_contents($path) ?: '');
                $text = str_replace($url, $base64, $text);
            }
        }

        $text = str_replace('<p>&nbsp;</p>', '<p class="spacer mini"></p>', $text);

        // change empty paragraphe that are not spacers to empty-line
        /** @var string $text */
        $text = preg_replace('#(<p(?![^>]*\bclass\s*=\s*["\']\s*spacer\s*\b)[^>]*></p>)#i', '<br>', $text);

        /** @var string $text */
        $text = preg_replace('/style="width:\s*[0-9.]+pt"/i', '', $text);

        /** @var string $text */
        $text = preg_replace('/font-family:(&quot;)?(.*?)(&quot;);/i', '', $text);

        /** @var string $text */
        $text = preg_replace('/font-size:\s*[0-9.]+(px|rem|pt);/i', '', $text);

        /** @var string $text */
        $text = preg_replace('/(style="[^"]*?)(width|height):\s*[0-9.]+(px|rem|pt);?([^"]*")/i', '$1$4', $text);

        /** @var string $text */
        $text = preg_replace('/(width|height)="([^"]+)"/i', '', $text);

        /** @var string $text */
        $text = preg_replace('/(<img([^>]*)>)/i', '<p class="image-wrapper">$1</p>', $text);

        return iClean($text, 'wysiwyg');
    }

    public function applyBordersForTables(string $text): ?string
    {
        $pattern = '#<table([^>]*)>(.*?)</table>#is';
        $replacement = '<table$1 style="border: 1px solid black; border-collapse: collapse;">$2</table>';
        $text = preg_replace($pattern, $replacement, $text);
        if (is_null($text)) {
            throw new \Exception('');
        }

        return preg_replace_callback('#<t(d|h)([^>]*)>(.*?)</t\1>#is', function ($matches) {
            $tag = $matches[1];
            $attributes = $matches[2];
            $content = $matches[3];

            if (preg_match('/\bsty(le)?\s*=\s*["\'](.*?)["\']/', $attributes, $styleMatches)) {
                $existingStyle = $styleMatches[2];
                $newStyle = 'border: 1px solid black; '.$existingStyle;
                $attributes = str_replace($styleMatches[0], 'style="'.$newStyle.'"', $attributes);
            } else {
                $attributes .= ' style="border: 1px solid black;"';
            }

            return '<t'.$tag.$attributes.'>'.$content.'</t'.$tag.'>';
        }, $text);
    }
}
