<?php

namespace Inside\IIDE\Console;

use GuzzleHttp\Client;
use Illuminate\Console\Command;
use Inside\Content\Models\Contents\CommercialFirms;
use Inside\Content\Models\Contents\ProjectFolders;
use Inside\Content\Models\Contents\ProjectNews;
use Inside\Content\Models\Contents\Projects;
use Inside\Host\Bridge\BridgeContent;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Carbon;
use Illuminate\Filesystem\Filesystem;

class ImportProjectsFromApi extends Command
{
    /**
     * @var string
     */
    protected $name = 'iide:projects:import';

    /**
     * @var string
     */
    protected $signature = 'iide:projects:import {--offset=0}';

    /**
     * @var string
     */
    protected $description = 'Import des projets depuis l\'API';

    private string $token = "";

    /**
     * @var int
     */
    private int $createdProjectsCount = 0;

    /**
     * @var Client
     */
    private $client;

    /**
     * @var mixed
     */
    private $projectDetails;

    private string $apiUrl;

    public function __construct()
    {
        parent::__construct();
        $this->client = new Client();
        $this->apiUrl = env('IIDE_PROJECT_API_URL', 'https://www.groupeidec-info.com/api');
    }

    /**
     * @return void
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function handle()
    {
        $this->setAuthToken();
        $this->createProjects();
        $this->getOutput()->writeln('<fg=green>End of import ('. $this->createdProjectsCount .')✔</fg=green>');
        Log::channel('import')->info('[Projects] End of import ('. $this->createdProjectsCount .') !');
    }

    /**
     * @return void
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    private function createProjects()
    {
        Log::channel('import')->info('[Projects] Import started !');
        $offset = intval($this->option('offset'));
        $projects = $this->getProjects($offset);

        while (!empty($projects->data)) {
            foreach ($projects->data as $project) {
                $mainContentUuid = $this->createProjectContent($project->id, 'fr');
                if (!$mainContentUuid) {
                    continue;
                }

                $this->createProjectContent($project->id, 'en', $mainContentUuid);
            }
            Log::channel('import')->info('[Projects] offset '. $offset .' !');
            $offset += 10;
            $projects = $this->getProjects($offset);
        }
    }

    /**
     * @param string $projectId
     * @param string $languageCode
     * @param string $mainContentUuid
     * @return string|null
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    private function createProjectContent(string $projectId, string $languageCode, string $mainContentUuid = '')
    {
        $this->projectDetails = $this->getProjectDetails($projectId, $languageCode);

        $projectContent = $this->buildProjectContent($languageCode);
        if ($projectContent === null) {
            $this->getOutput()->writeln('<fg=red>Import skipped / Empty title x</fg=red>');
            Log::channel('import')->info('[Projects] ['. $languageCode .'] [projectId:' . $projectId . '] Import skipped / Empty title x !');
            return null;
        }

        $bridge = new BridgeContent();
        $existingProject = Projects::query()
            ->where('project_id', $projectContent['project_id'])
            ->first();
        if ($existingProject) {
            $projectContent['uuid'] = $existingProject->uuid;
            $uuid = $bridge->contentUpdate('projects', $projectContent);
            $this->ImportPdfFiles($uuid, $languageCode);
            $this->getOutput()->writeln('<fg=green>'. $projectContent['title'] .' / updated ✔</fg=green>');
            Log::channel('import')->info('[Projects] ['. $languageCode .'] [projectId:' . $projectId . '] updated !');
        } elseif ($mainContentUuid) {
            $projectContent['uuid'] = $mainContentUuid;
            $uuid = $bridge->contentUpdate('projects', $projectContent);
            $this->ImportPdfFiles($uuid, $languageCode);
            $this->getOutput()->writeln('<fg=green>'. $projectContent['title'] .' / updated ✔</fg=green>');
            Log::channel('import')->info('[Projects] ['. $languageCode .'] [projectId:' . $projectId . '] updated !');
        } else {
            $uuid = $bridge->contentInsert('projects', $projectContent);
            $this->ImportPdfFiles($uuid, $languageCode);
            $this->getOutput()->writeln('<fg=green>'. $projectContent['title'] .' ✔</fg=green>');
            Log::channel('import')->info('[Projects] ['. $languageCode .'] [projectId:' . $projectId . '] created !');
            $this->createdProjectsCount++;
        }

        return $uuid;
    }

    /**
     * @param int $offset
     * @param int $limit
     * @return mixed
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    private function getProjects(int $offset = 0, int $limit = 10)
    {
        $response = $this->client->request('GET', $this->apiUrl.'/projects', [
            'headers'   => [
                'Accept' => 'application/json',
                'Content-type' => 'application/json',
                'Authorization' => "Bearer {$this->token}",
            ],
            'query' => [
                'offset' => $offset,
                'limit' => $limit
            ]
        ]);

        if ($response->getStatusCode() !== 200) {
            throw new \Exception("An error occured while trying to fetch data from the api !");
        }

        return json_decode($response->getBody());
    }

    /**
     * @param string $projectId
     * @param string $languageCode
     * @return mixed
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    private function getProjectDetails(string $projectId, string $languageCode)
    {
        $response = $this->client->request('GET', $this->apiUrl.'/projects/' . $projectId, [
            'headers'   => [
                'Accept' => 'application/json',
                'Content-type' => 'application/json',
                'Authorization' => "Bearer {$this->token}",
            ],
            'query' => [
                'locale' => $languageCode,
            ]
        ]);

        if ($response->getStatusCode() !== 200) {
            throw new \Exception("An error occured while trying to get project details !");
        }

        return json_decode($response->getBody());
    }

    /**
     * @return void
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    private function setAuthToken()
    {
        $response = $this->client->request('POST', $this->apiUrl.'/login', [
            'form_params' => [
                'username' => 'idec-mobile',
                'password' => 'oij262#dd-PbDSR',
            ]
        ]);
        if ($response->getStatusCode() !== 200) {
            throw new \Exception("An error occured while trying to login to the api !");
        }

        $jsonResponse = json_decode($response->getBody());
        $this->token = $jsonResponse->token;
    }

    /**
     * @param string $languageCode
     * @return array|null
     * @throws \Exception
     */
    private function buildProjectContent(string $languageCode)
    {
        $contentTitle = $this->getProjectTitle();
        if (!$contentTitle) {
            return null;
        }

        $content = [
            'type'                      => 'node',
            'bundle'                    => 'projects',
            'langcode'                  => $languageCode,
            'project_id'                => $this->getAttributeValueFromProject('id'),
            'title'                     => $this->getProjectTitle(),
            'project_description'       => $this->textDataToTextField(
                $this->getAttributeValueFromProject('descriptions', 'data'),
                false
            ),
            'subsidiaries'              => $this->getEntityReference(
                $this->getAttributeValueFromProject('company', 'title'),
                'subsidiaries',
                [
                    'subsidiaries_logo' => $this->getFile($this->getAttributeValueFromProject('company', 'logo')),
                    'subsidiaries_website' => $this->getAttributeValueFromProject('company', 'website')
                ]
            ),
            'industries'                => $this->getEntityReference(
                $this->getAttributeValueFromProject('secteurActiviteUser', 'name'),
                'industries'
            ),
            'building_types'            => $this->getReferences(
                $this->getAttributeValueFromProject('buildingTypes', 'data'),
                'building_types'
            ),
            'address'                   => $this->getAttributeValueFromProject('address'),
            'cities'                    => $this->getEntityReference(
                $this->getAttributeValueFromProject('city', 'label'),
                'cities'
            ),
            'departments'               => $this->getEntityReference(
                $this->getAttributeValueFromProject('department', 'label'),
                'departments',
                ['code' => $this->getAttributeValueFromProject('department', 'code')]
            ),
            'country'                   => $this->getAttributeValueFromProject('country', 'label'),
            'latitude'                  => $this->getAttributeValueFromProject('geolocation', 'lat'),
            'longitude'                 => $this->getAttributeValueFromProject('geolocation', 'lng'),
            'communicable_reference'    => $this->getAttributeValueFromProject('reference', 'label') === "Oui",
            'work_duration'             => $this->getAttributeValueFromProject('constructionTime'),
            'main_image'                => $this->getMainImage(),
            'content'                    => $this->getMedias(),
            'architect'                 => $this->getAttributeValueFromProject('architect'),
            'project_amount'            => $this->getAttributeValueFromProject('projectPrice'),
            'built_area'                => $this->getAttributeValueFromProject('totalSurface'),
            'project_types_ing'         => $this->getReferences(
                $this->getAttributeValueFromProject('natures', 'data'),
                'project_types_ing'
            ),
            'building_function'         => $this->getReferences(
                $this->getAttributeValueFromProject('buildingTypes', 'data'),
                'building_function'
            ),
            'issues'                    => $this->textDataToTextField(
                $this->getAttributeValueFromProject('issues'),
                true
            ),
            'our_answer'                => $this->getAttributeValueFromProject('ourAnswer', 'text'),
            'certificates'          => $this->getReferences(
                $this->getAttributeValueFromProject('certifications', 'data'),
                'certificates'
            ),
            'investor'                  => $this->getAttributeValueFromProject('investor', 'label'),
            'projects_statuses'         => $this->getEntityReference(
                $this->getAttributeValueFromProject('projectState', 'label'),
                'projects_statuses'
            ),
            'benefits_and_user_comfort' => $this->listToFormatedText(
                $this->getAttributeValueFromProject('prestations')
            ),
            'technical_description'     => $this->listToText(
                $this->getAttributeValueFromProject('features')
            ),
            'marketing_statuses'        => $this->getReferences(
                $this->getAttributeValueFromProject('commercialStatuses', 'data'),
                'marketing_statuses'
            ),
            'available_area'            => $this->getAttributeValueFromProject('availableSurface'),
            'land_area'                 => $this->getAttributeValueFromProject('landSurface'),
            'number_of_created_jobs'    => $this->getAttributeValueFromProject('nbCreatedJobs'),
            'leasing_user'              => $this->getLeasingUser(),
            'leasing_date'              => $this->getLeasingDate(),
            'leasing_duration'          => $this->getLeasingDuration(),
            'lease'                     => $this->textsDataToParagraph(
                $this->getAttributeValueFromProject('situation'),
                true
            ),
            'divisible'                 => $this->getAttributeValueFromProject('divisible') == true,
            'progress_projects'         => $this->getEntityReference(
                $this->getAttributeValueFromProject('projectProgression', 'label'),
                'progress_projects'
            ),
            'commercial_area'           => $this->getAttributeValueFromProject('surfaceCommerciale'),
            'tax_benefits'              => $this->getReferences(
                $this->getAttributeValueFromProject('categories', 'data'),
                'tax_benefits'
            ),
            'pinel_areas'               => $this->getEntityReference(
                $this->getAttributeValueFromProject('pinelZone', 'label'),
                'pinel_areas'
            ),
            'ptz_areas'                 => $this->getEntityReference(
                $this->getAttributeValueFromProject('ptzZone', 'label'),
                'ptz_areas'
            ),
            'hook'                      => $this->getAttributeValueFromProject('header'),
            'distance_and_round_time'   => $this->listTextToParagraph(
                $this->getAttributeValueFromProject('distances')
            ),
            'benefits'                  => $this->listToText('prestations'),
            'commercial_firms'          => $this->getCommercialFirms(),
            'construction_start'        => $this->getConstructionStartDate(),
            'construction_quarters'     => $this->getEntityReference(
                $this->getAttributeValueFromProject('DateDebutChantierTrimestre'),
                'construction_quarters'
            ),
            'delivery_month'           => $this->getAttributeValueFromProject('DateLivraisonMois'),
            'delivery_year'             => $this->getAttributeValueFromProject('deliveryDateYear'),
            'delivery_date_quarters'    => $this->getAttributeValueFromProject('deliveryDateQuarter'),
            'planner'                   => $this->getAttributeValueFromProject('developer', 'label'),
            'number_of_lots'            => $this->getAttributeValueFromProject('nbPackages'),
            'builder'                   => $this->getAttributeValueFromProject('constructor', 'label'),
            'building_floor_area'       => $this->getAttributeValueFromProject('buildingFloorSurface'),
            'co_promoter'               => $this->getCopromoter()
        ];

        return $content;
    }

    /**
     * @param string|null $projectUuid
     * @param string $languageCode
     * @param bool $isUpdate
     * @return void
     * @throws \Exception
     */
    private function ImportPdfFiles(string|null $projectUuid, string $languageCode, bool $isUpdate = false)
    {
        if (!$projectUuid) {
            return;
        }

        $bridge = new BridgeContent();
        $createProjectFolder = true;
        $projectFolderUuid = null;
        $medias = $this->projectDetails->medias->data;

        foreach ($medias as $media) {
            if ($media->document) {
                // Creation of project folders
                if ($createProjectFolder) {
                    $projectFolderUuid = $bridge->contentInsert('project_folders', [
                        'type'          => 'node',
                        'bundle'        => 'project_folders',
                        'langcode'      => $languageCode,
                        'title'         => $this->getProjectTitle(),
                        'projects'      => $projectUuid
                    ]);
                    $createProjectFolder = false;
                }
                // Add pdf files to project documents
                $projectDocument = $bridge->contentInsert('project_documents', [
                    'type'              => 'node',
                    'bundle'            => 'project_documents',
                    'file'              => $this->getFile($media->document),
                    'title'             => $media->title,
                    'project_folders'   => $projectFolderUuid
                ]);
            }
        }
    }

    /**
     * @return array|null
     */
    private function getMedias()
    {
        if (!isset($this->projectDetails->medias->data) || empty($this->projectDetails->medias->data)) {
            return null;
        }

        $paragraphs = [];
        $medias = $this->projectDetails->medias->data;
        foreach ($medias as $media) {
            if ($media->video->standard) {
                $paragraphs[] = [
                    'bundle' => 'video',
                    'file' => $this->getFile($media->video->standard)
                ];
            } elseif ($media->video->hd) {
                $paragraphs[] = [
                    'bundle' => 'video',
                    'file' => $this->getFile($media->video->hd)
                ];
            } elseif ($media->visual) {
                $paragraphs[] = [
                    'bundle' => 'image',
                    'image' => $this->getFile($media->visual)
                ];
            }
        }

        return $paragraphs;
    }

    /**
     * @param string $attributeName
     * @param string $subFieldName
     * @return string|array|null
     */
    private function getAttributeValueFromProject(string $attributeName, string $subFieldName = '')
    {
        if (!isset($this->projectDetails->{$attributeName}) || empty($this->projectDetails->{$attributeName})) {
            return null;
        }

        if ($subFieldName) {
            return $this->projectDetails->{$attributeName}->{$subFieldName};
        }
        return $this->projectDetails->{$attributeName};
    }

    /**
     * @param array|string|null $values
     * @param string $contentType
     * @return array|null
     */
    private function getReferences($values, string $contentType)
    {
        if (empty($values)) {
            return null;
        }

        if (is_string($values)) {
            throw new \Exception('[getReferences] Values should be an array! !');
        }

        $uuidArray = [];
        foreach ($values as $item) {
            $uuidArray[] = $this->getEntityReference($item->label, $contentType);
        }

        return $uuidArray;
    }

    /**
     * @param mixed $titleValue
     * @param string $contentType
     * @param array $additionalData
     * @return string|null
     * @throws \Exception
     */
    private function getEntityReference($titleValue, string $contentType, array $additionalData = [])
    {
        if (empty($titleValue)) {
            return null;
        }

        $bridge = new BridgeContent();
        $uuid = null;
        $entity = call_user_func(type_to_class($contentType) . '::query')
            ->where('title', $titleValue)
            ->first();

        if ($entity) {
            $uuid = $entity->uuid;
        } else {
            $data = [
                'type' => 'node',
                'bundle' => $contentType,
                'title' => $titleValue
            ];

            if ($additionalData) {
                $data = array_merge($data, $additionalData);
            }

            $uuid = $bridge->contentInsert($contentType, $data);
        }

        return $uuid;
    }

    /**
     * @param mixed $uri
     * @return string|null
     */
    private function getFile($uri)
    {
        if (!$uri || !($url = parse_url($uri)) || !isset($url['path'])) {
            return null;
        }

        $path = $url['path'];
        $pathArray = explode('/', $path);
        $fileName = preg_replace('/[^A-Za-z0-9\-.]/', '', $pathArray[array_key_last($pathArray)]);

        $chunkId    = Str::random(32);
        $chunkPath = "chunks/$chunkId/$fileName";
        Storage::makeDirectory("chunks/$chunkId");
        $imageUri = file_get_contents($uri);

        if ($imageUri) {
            Storage::disk('local')->put($chunkPath, $imageUri);

            return $chunkPath;
        }

        return null;
    }

    /**
     * @return string
     * @throws \Exception
     */
    private function getMainImage()
    {
        $projectImage = $this->getFile($this->getAttributeValueFromProject('fullImage'));
        if ($projectImage) {
            return $projectImage;
        }

        try {
            $filesystem = new Filesystem();
            $chunkId    = Str::random(32);
            $defaultImagePath = "chunks/$chunkId/default_project_image.jpg";
            Storage::makeDirectory("chunks/$chunkId");
            $filesystem->copy(
                'vendor/maecia/iide-back/resources/assets/img/project/default_project_image.jpg',
                "storage/app/$defaultImagePath"
            );
        } catch (\Exception $exception) {
            throw new \Exception('Error occured while trying to copy default image to storage folder !');
        }

        return $defaultImagePath;
    }

    /**
     * @param mixed $datas
     * @return array[]|null
     */
    private function textsDataToParagraph($datas, bool $hasOneItem = false)
    {
        if (empty($datas)) {
            return null;
        }
        $fieldValue = '';

        if ($hasOneItem) {
            $fieldValue .= '<b>' . $datas->title . '</b><br><br>';
            $fieldValue .= $datas->text . '<br><br>';

            return [
                [
                    'bundle' => 'html',
                    'html' => trim($fieldValue)
                ]
            ];
        }

        foreach ($datas as $data) {
            $fieldValue .= '<b>' . $data->title . '</b><br><br>';
            $fieldValue .= $data->text . '<br><br>';
        }

        return [
            [
                'bundle' => 'html',
                'html' => trim($fieldValue)
            ]
        ];
    }

    /**
     * @param mixed $data
     * @param bool $hasOneItem
     * @return string|null
     */
    private function textDataToTextField($data, bool $hasOneItem): ?string
    {
        if (empty($data)) {
            return null;
        }
        $fieldValue = '';

        if ($hasOneItem) {
            if ($data->title) {
                $fieldValue .= '<b>' . $data->title . '</b><br><br>';
            }
            if ($data->text) {
                $fieldValue .= $data->text . '<br><br>';
            }

            return trim($fieldValue);
        }

        foreach ($data as $item) {
            if ($item->title) {
                $fieldValue .= '<b>' . $item->title . '</b><br><br>';
            }

            if ($item->text) {
                $fieldValue .= $item->text . '<br><br>';
            }
        }

        return trim($fieldValue);
    }

    /**
     * @param mixed $objectValue
     * @return array[]
     */
    private function listTextToParagraph($objectValue)
    {
        $fieldValue = '';
        foreach ($objectValue->data as $data) {
            $fieldValue .= '<b>' . $data->title . '</b><br><br>';
            $fieldValue .= implode('<br>', $data->list) . '<br><br>';
        }

        return [
            [
                'bundle' => 'html',
                'html' => trim($fieldValue)
            ]
        ];
    }

    /**
     * @param mixed $objectValue
     * @return string
     */
    private function listToText($objectValue)
    {
        $fieldValue = '';
        foreach ($objectValue as $data) {
            $fieldValue .=  $data . '\n';
        }

        return $fieldValue;
    }

    /**
     * @param mixed $objectValue
     * @return string
     */
    private function listToFormatedText($objectValue)
    {
        $fieldValue = '';
        foreach ($objectValue as $data) {
            $fieldValue .=  $data . '<br>';
        }

        return $fieldValue;
    }

    /**
     * @return null|string
     */
    private function getLeasingUser()
    {
        $leasing = $this->projectDetails->leasing->data;
        if (empty($leasing) || empty($leasing[0]->user->label)) {
            return null;
        }

        return $leasing[0]->user->label;
    }

    /**
     * @return mixed|null
     */
    private function getLeasingDate()
    {
        $leasing = $this->projectDetails->leasing->data;
        if (empty($leasing) || empty($leasing[0]->date)) {
            return null;
        }

        return $leasing[0]->date;
    }

    /**
     * @return null|string
     */
    private function getLeasingDuration()
    {
        $leasing = $this->projectDetails->leasing->data;
        if (empty($leasing) || empty($leasing[0]->duration)) {
            return null;
        }

        return $leasing[0]->duration;
    }

    /**
     * @return array
     * @throws \Exception
     */
    private function getCommercialFirms()
    {
        $commercialFirms = $this->projectDetails->commercialFirms->data;
        if (empty($commercialFirms)) {
            return [];
        }

        $bridge = new BridgeContent();
        $uuids = [];
        foreach ($commercialFirms as $commercialFirm) {
            $content = [
                'title'             => $commercialFirm->title,
                'address'           => $commercialFirm->address,
                'cities'            => $this->getEntityReference(
                    $commercialFirm->city,
                    'cities'
                ),
                'zipcode'           => $commercialFirm->zipcode,
                'phone'             => $commercialFirm->phone,
                'phone_mentions'    => $commercialFirm->phoneMentions,
                'opening_time'      => $commercialFirm->openingTime,
                'contact'           => $commercialFirm->contact,
                'email'             => $commercialFirm->email,
            ];

            $existingCommercialFirms = CommercialFirms::query()->where('title', $content['title'])->first();
            if ($existingCommercialFirms) {
                $content['uuid'] = $existingCommercialFirms->uuid;
                $uuid = $bridge->contentUpdate('commercial_firms', $content);
            } else {
                $uuid = $bridge->contentInsert('commercial_firms', $content);
            }
            $uuids[] = $uuid;
        }

        return $uuids;
    }

    /**
     * @return string|null
     */
    private function getConstructionStartDate()
    {
        $month = $this->projectDetails->DateDebutChantierMois;
        $year = $this->projectDetails->DateDebutChantierAnnee;
        if (!$month || !$year) {
            return null;
        }

        $date = Carbon::createFromFormat('d/m/Y', '01/'.$month.'/'.$year);
        if ($date) {
            return $date->toDateTimeString();
        }

        return null;
    }

    /**
     * @return string|null
     */
    private function getCopromoter()
    {
        $copromoter = $this->getAttributeValueFromProject('copromoter', 'data');
        if (!empty($copromoter) && isset($copromoter[0])) {
            return $copromoter[0]->label;
        }

        return null;
    }

    /**
     * @return array|string|null
     */
    private function getProjectTitle()
    {
        return $this->getAttributeValueFromProject('title') ?: $this->getAttributeValueFromProject('internalTitle');
    }
}
