<?php

namespace Inside\IIDE\Services;

use Barryvdh\DomPDF\PDF as PdfType;
use Barryvdh\DomPDF\Facade\Pdf;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Http\Response;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Storage;
use Inside\Content\Models\Contents\Projects;
use Inside\Support\Str;
use InvalidArgumentException;
use ReflectionClass;
use Throwable;

/**
 * Transform a Project in a downloadable PDF.
 */
class ProjectToPDFService
{
    protected Projects $project;
    protected Collection $relationships;
    protected string $view;

    /**
     * @throws Throwable
     */
    private function __construct(string $uuid, string $view)
    {
        $relationships = static::relationships();

        $project = Projects::withoutGlobalScopes()->with($relationships->toArray())->find($uuid);

        if (! $project instanceof Projects) {
            throw new ModelNotFoundException();
        }

        if (! view()->exists($view)) {
            throw new InvalidArgumentException();
        }

        $this->project = $project;
        $this->view = $view;
        $this->relationships = $relationships;
    }

    public static function load(string $uuid, string $view): ?self
    {
        try {
            return new self($uuid, $view);
        } catch (\Exception) {
            return null;
        }
    }

    /**
     * Return every BelongsToMany methods of Project to simplify the eager-load process.
     *
     * @return Collection
     */
    public static function relationships(): Collection
    {
        return collect((new ReflectionClass(Projects::class))->getMethods())
            ->filter(fn ($method) => Str::endsWith($method?->getReturnType()?->getName(), 'BelongsToMany'))
            ->pluck('name');
    }

    /**
     * Return a collection who contain simplified data of current project.
     *
     * @return array
     */
    public function getAttributes(): array
    {
        $logoName = $this->project->subsidiaries->first()?->subsidiaries_logo;
        $imageName = $this->project->main_image;
        $headerData = [
            'title'       => $this->project->title,
            'content'     => preg_replace('/<\/b>(<br>)+/', '</b><br>', $this->project->project_description),
            'main_image'  => $imageName ? Storage::get($imageName) : '',
            'logo'        => $logoName ? Storage::get($logoName) : '',
        ];

        $sectionData = [
            'lease'                     => $this->project->lease,
            'certifications'            => $this->project->certifications,
            'address'                   => $this->project->address,
            'city'                      => $this->flatten($this->project->cities),
            'country'                   => $this->project->country,
            'benefits_and_user_comfort' => $this->project->benefits_and_user_comfort,
            'our_answer'                => $this->project->our_answer,
            'commercialFirm'            => $this->project->commercialFirms,
        ];

        $data = [
            'Métier'                            => $this->flatten($this->project->professions),
            'Référence communicable	'           => ($this->project->communicable_reference) ? "Oui" : "Non",
            'Début chantier'                    => $this->project->construction_start ? date('m/d/Y', $this->project->construction_start) : '',
            'Trimestre debut de chantier'       => $this->project->construction_quarters,
            'Trimestre de livraison'            => $this->project->delivery_date ? date('m/d/Y', $this->project->delivery_date) : '',
            'Durée travaux'                     => $this->project->work_duration,
            'Architecte'                        => $this->project->architect,
            'Approche énergétique'              => $this->project->energy_approaches,
            'Date du mise à bail'               => $this->project->leasing_date ? date('m/d/Y', $this->project->leasing_date) : '',
            'Filiale'                           => $this->flatten($this->project->subsidiaries),
            'Secteur'                           => $this->flatten($this->project->industries),
            'Typologie de bâtiment'             => $this->flatten($this->project->buildingTypes),
            'Localisation'                      => $this->flatten($this->project->departments),
            'Date de livraison'                 => implode(' ', [
                $this->project->delivery_date_quarters,
                $this->project->delivery_month . " /",
                $this->project->delivery_year,
            ]),
            'Nature du projet'                  => $this->flattenThroughPrefix('project_types'),
            'Fonction du bâtiment'              => $this->flatten($this->project->buildingFunction),
            'Montant du projet (en €)'          => $this->project->project_amount,
            'Surface construite (en m2)'        => $this->project->built_area,
            'Utilisateur(s)'                    => $this->project->user,
            'Aménageur'                         => $this->flattenThroughPrefix('planner_'),
            'Investisseur'                      => $this->project->investor,
            'Co promoteur'                      => $this->flattenThroughPrefix('co_promoter_'),
            'Constructeur'                      => $this->flattenThroughPrefix('builder_'),
            'Descriptif technique'              => $this->project->technical_description,
            'Statut de commercialisation'       => $this->flatten($this->project->marketingStatuses),
            'Surface disponible (en m2)'        => $this->project->available_area,
            'Surface constructible de plancher (en m2)'       => $this->flattenThroughPrefix('building_floor_area_'),
            'Surface foncier (en m2)'           => $this->project->land_area,
            'Nombre d\'emplois créés'           => $this->project->number_of_created_jobs,
            'Nombre de lots'                    => $this->flattenThroughPrefix('number_of_lots_'),
            'Mise à bail - Utilisateur'         => $this->project->leasing_user,
            'Durée du bail en années'           => $this->project->leasing_duration,
            'Les accès'                         => $this->project->accesses,
            'Divisible'                         => ($this->project->divisible) ? "Oui" : "Non",
            'Divisible à partir de'             => $this->project->divisible_from,
            'Etat du projet'                    => $this->flatten($this->project->projectsStatuses),
            'Avancement du projet'              => $this->flatten($this->project->progressProjects),
            'Surface commerciale (en m2)'       => $this->project->commercial_area,
            'Avantages fiscaux'                 => $this->flatten($this->project->taxBenefits),
            'Zone PINEL'                        => $this->flatten($this->project->pinelAreas),
            'Zone PTZ'                          => $this->flatten($this->project->ptzAreas),
            'Accroche'                          => $this->project->hook,
            'Distance et temps de parcours'     => $this->project->distance_and_round_time,
            'Prestations'                       => $this->project->field_benefits,
        ];

        $filteredData = array_filter($data);
        /**
         * @var int<1, max> $nbrOfElements
         */
        $nbrOfElements = intval(ceil(count($filteredData) / 2));
        list($data1, $data2) = array_chunk($filteredData, $nbrOfElements, true);

        return [
            'headerData'    => collect(array_filter($headerData)),
            'sectionData'   => collect(array_filter($sectionData)),
            'data1'         => $data1,
            'data2'         => $data2
        ];
    }

    /**
     * Return HTTP Response as a stream to open the pdf in the navigator.
     *
     * @return Response
     */
    public function stream(): Response
    {
        return $this->build()->stream($this->filename());
    }

    /**
     * Return HTTP Response directly as a download.
     *
     * @return Response
     */
    public function download(): Response
    {
        return $this->build()->download($this->filename());
    }

    /**
     * Return the value as a string after flatten the BelongsToMany relation.
     *
     * @param Collection $relations
     * @return string
     */
    private function flatten(Collection $relations): string
    {
        return $relations->pluck('title')->flatten()->join(', ');
    }

    /**
     * Return every value as a string when Project contains multiple relations
     * Example, Project get 3 belongsToMany (certificates_fi, certificates_fp, certificates_ing)
     * In the PDF, we want to get only one row.
     *
     * @param String $prefix
     * @return string
     */
    private function flattenThroughPrefix(String $prefix): string
    {
        return $this->relationships
            ->filter(fn ($relation, $name) => Str::startsWith($relation, $prefix))
            ->map(fn ($relation) => $this->project->{$relation})
            ->pluck('*.title')
            ->flatten()
            ->join(', ');
    }

    /**
     * Return the PDF instance of the project.
     *
     * @return PdfType
     */
    private function build(): PdfType
    {
        return Pdf::loadView($this->view, $this->getAttributes())
            ->setPaper('a4')
            ->setWarnings(false)
            ->setOptions([
                "defaultFont" => "sans-serif",
            ]);
    }

    /**
     * Return the project's name with the pdf extension.
     *
     * @return string
     */
    private function filename(): string
    {
        return sprintf('%s.pdf', strtolower($this->project->title));
    }
}
