<?php

namespace App\Libs\Services;

use App\Facades\ContentTypes;
use App\Models\GenericBackoffice\WebsiteObject;
use App\Models\GenericBackoffice\WebsiteObjectLanguage;
use App\Facades\Services\BackofficeService;
use GuzzleHttp\Client;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use App\Facades\Services\PropertyService;
use App\Facades\Services\EpigraphService;
use Grimzy\LaravelMysqlSpatial\Types\Point;
use Image;
use Illuminate\Support\Facades\DB;
use App\Libs\Import\Import;
use App\Models\Technique;
use App\Models\TechniqueLanguage;
use App\Models\Idiom;
use App\Models\IdiomLanguage;
use App\Models\InscriptionType;
use App\Models\InscriptionTypeLanguage;
use App\Exceptions\ImportException;
use App\Models\Chronology;
use App\Models\ChronologyLanguage;
use App\Models\Property;
use App\Models\PropertyLanguage;
use App\Models\AudioVisual;
use App\Models\AudioVisualLanguage;
use App\Models\GlossaryTerm;
use App\Models\GlossaryTermLanguage;
use App\Models\Epigraph;
use App\Models\Occasion;
use App\Models\PropertyImage;
use Illuminate\Support\Facades\Log;
use Embed\Embed;
use Embed\Http\Crawler;
use Spatie\Sitemap\SitemapGenerator;
use Spatie\Sitemap\Tags\Url;
use Throwable;
use Exception;

class ImportService
{
    protected $glossaryTerms;
    public function __construct()
    {
        $this->availableLanguages = getAvailableLanguages();
        $this->defaultLanguage = "pt";
        $this->allowedTags = '<b><i><a><br>';
        $this->allowedTagsWithSup = '<b><i><a><br><sup>';
    }

    public function createTaxonomy($list, $type)
    {
        $size = count($list[$this->defaultLanguage]);
        $class = $type;
        $classLang = $type."Language";
        $contentType = Str::Plural(strtolower($type));
        $idField = strtolower(Str::snake($type))."_id";

        for ($i = 0; $i < $size; $i++) {
            $listItem = [];
            foreach ($this->availableLanguages as $key => $lang) {
                $listItem[$key] = $list[$key][$i];
            }
            $itemID = $this->_createTaxonomyItem($listItem, $class, $classLang, $contentType, $idField, null);

            // Add children if exists
            if (property_exists($listItem[$this->defaultLanguage], "children")) {
                $parentId = property_exists($listItem[$this->defaultLanguage], "ID") ? $listItem[$this->defaultLanguage]->ID : null;
                $childrenSize = count($listItem[$this->defaultLanguage]->children);
                for ($j = 0; $j < $childrenSize; $j++) {
                    $childrenItem = [];
                    foreach ($this->availableLanguages as $key => $lang) {
                        $children = $listItem[$key]->children;
                        if (!empty($children[$j])) {
                            $childrenItem[$key] = $children[$j];
                        }
                    }
                    $childrenItem[$this->defaultLanguage]->ParentID = $parentId;
                    $this->_createTaxonomyItem($childrenItem, $class, $classLang, $contentType, $idField, $itemID);
                }
            }
        }
    }

    public function createProperties($list)
    {
        foreach ($list as $property) {
            $this->_createPropertyItem($property);
        }
    }

    public function createEpigraphs($list)
    {
        foreach ($list as $epigraph) {
            $this->_createEpigraphItem($epigraph);
        }
    }

    private function createInscriptionTaxonomy($className, $taxonomy, $field, $idField, $contentType, $languageFieldId)
    {
        if (!is_null($taxonomy[$this->defaultLanguage][$field])) {
            $externalIdField = $taxonomy[$this->defaultLanguage][$idField];
            $itemClass = $className::where('external_id', $externalIdField)->first();

            $websiteObject = null;
            if (is_null($itemClass)) {
                $itemClass = $className::create([
                    'external_id' => $externalIdField,
                ]);
            }
            $slug = BackofficeService::generateSlug(
                $this->decodeItemText($taxonomy[$this->defaultLanguage][$field]),
                $this->defaultLanguage
            );

            // Create Website Objects
            $websiteObject = $this->factoryFind("GenericBackoffice\WebsiteObject", [
                'object_id'     => $itemClass->id,
                'object_type'   => get_class($itemClass),
                'content_type'  => $contentType,
            ]);
            if (is_null($websiteObject)) {
                $websiteObject = $this->createWebsiteObject($itemClass, $contentType, $slug);
            }

            // Associate Website Object
            $itemClass->websites()->save($websiteObject);

            // Create language relationships
            foreach ($this->availableLanguages as $langCode => $lang) {
                if (!empty($taxonomy[$langCode]) && $taxonomy[$langCode][$field] !== null) {
                    $taxonomyLang = $this->factoryUpdateOrInsert(class_basename($className) . "Language", [
                        $languageFieldId => $itemClass->id,
                        "language_id" => $langCode,
                    ], [
                        'title'              => $this->decodeItemText($taxonomy[$langCode][$field]),
                    ]);

                    if (!is_null($websiteObject)) {
                        // Create Website Object Language
                        $this->createWebsiteObjectLanguage($websiteObject, $langCode);
                    }
                }
            }

            $websiteObject = $itemClass->websites()->first();
            if (!is_null($websiteObject)) {
                $this->updateUri($websiteObject, $contentType);
            }
            return $itemClass->id;
        } else {
            return null;
        }
    }

    private function createAudioVisuals($item, $createdItemId = null, $field = null)
    {
        //field is either property_id or epigraph_id
        $audiovisual = $item['audiovisuals'];
        $ids = [];
        if (is_iterable($audiovisual)) {
            foreach ($audiovisual as $key => $row) {
                if (!is_null($row[$this->defaultLanguage]['title'])) {
                    $externalIdField = $row[$this->defaultLanguage]['external_id'];
                    $rowModel = AudioVisual::where('external_id', $externalIdField)->first();
                    $websiteObject = null;
                    if (is_null($rowModel)) {
                        $dataToInsert = ['external_id' => $externalIdField];
                        if (!is_null($createdItemId) && !is_null($field)) {
                            $dataToInsert[$field] = $externalIdField;
                        }
                        $rowModel = AudioVisual::create($dataToInsert);
                    } else {
                        //This is the place if we want new repeated audiovisual entries for different properties although the audiovisual already exists
                    }
                    $slug = BackofficeService::generateSlug(
                        $this->decodeItemText($row[$this->defaultLanguage]['title']),
                        $this->defaultLanguage
                    );

                    // Create Website Objects
                    $websiteObject = $this->factoryFind("GenericBackoffice\WebsiteObject", [
                        'object_id'     => $rowModel->id,
                        'object_type'   => get_class($rowModel),
                        'content_type'  => 'audiovisuals',
                    ]);
                    if (is_null($websiteObject)) {
                        $websiteObject = $this->createWebsiteObject($rowModel, 'audiovisuals', $slug);
                    }

                    // Associate Website Object
                    $rowModel->websites()->save($websiteObject);

                    // Create language relationships
                    foreach ($this->availableLanguages as $langCode => $lang) {
                        if (!empty($row[$langCode]) && $row[$langCode]['title'] !== null) {
                            $info = Embed::create($row[$langCode]['link']);
                            $image = $info->image;
                            $rowLang = $this->factoryUpdateOrInsert("AudioVisualLanguage", [
                                'audio_visual_id' => $rowModel->id,
                                "language_id" => $langCode,
                            ], [
                                'title' => $this->decodeItemText($row[$langCode]['title']),
                                'link'  => $row[$langCode]['link'],
                                'type'  => $row[$langCode]['type'],
                                'image' => $image
                            ]);
                        } else {
                            $info = Embed::create($row[$this->defaultLanguage]['link']);
                            $image = $info->image;
                            $rowLang = $this->factoryUpdateOrInsert("AudioVisualLanguage", [
                                'audio_visual_id' => $rowModel->id,
                                "language_id" => $langCode,
                            ], [
                                'title' => $this->decodeItemText($row[$this->defaultLanguage]['title']),
                                'link'  => $row[$this->defaultLanguage]['link'],
                                'type'  => $row[$this->defaultLanguage]['type'],
                                'image' => $image
                            ]);
                        }
                        if (!is_null($websiteObject)) {
                            // Create Website Object Language
                            $this->createWebsiteObjectLanguage($websiteObject, $langCode);
                        }
                    }

                    $websiteObject = $rowModel->websites()->first();
                    if (!is_null($websiteObject)) {
                        $this->updateUri($websiteObject, 'audiovisuals');
                    }
                    $ids[] = $rowModel->id;
                }
            }
            return $ids;
        }
    }

    private function createChronologyTaxonomy($chronology, $createdItemId, $relation, $relationTable, $foreignField)
    {
        $ids = [];
        $deleted = $this->deleteOldChronologies($createdItemId, $relationTable, $foreignField);
        if (!$deleted) {
            Log::stack(['importLogStack', 'rollbar'])->error('Something went wrong trying to delete the old chronologies');
        }
        if (is_iterable($chronology)) {
            foreach ($chronology as $key => $row) {
                if (!is_null($row[$this->defaultLanguage]['title'])) {
                    $externalIdField = $row[$this->defaultLanguage]['external_id'];
                    $websiteObject = null;
                    $rowModel = Chronology::create();
                    $slug = BackofficeService::generateSlug(
                        $this->decodeItemText($row[$this->defaultLanguage]['title'] . "-" . random_string()),
                        $this->defaultLanguage
                    );

                    // Create Website Objects
                    $websiteObject = $this->createWebsiteObject($rowModel, 'chronologies', $slug);
                    // Associate Website Object
                    $rowModel->websites()->save($websiteObject);

                    // Create language relationships
                    foreach ($this->availableLanguages as $langCode => $lang) {
                        if (!empty($row[$langCode]) && $row[$langCode]['title'] !== null) {
                            $rowLang = $this->factoryUpdateOrInsert("ChronologyLanguage", [
                                'chronology_id' => $rowModel->id,
                                "language_id" => $langCode,
                            ], [
                                'title'            => $this->decodeItemText($row[$langCode]['title']),
                                'date_start'       => $row[$langCode]['date_start'],
                                'date_end'         => $row[$langCode]['date_end'],
                                'descriptive_text' => $row[$langCode]['descriptive_text'],
                            ]);
                            if (!is_null($websiteObject)) {
                                // Create Website Object Language
                                $this->createWebsiteObjectLanguage($websiteObject, $langCode);
                            }
                        }
                    }

                    $websiteObject = $rowModel->websites()->first();
                    if (!is_null($websiteObject)) {
                        $this->updateUri($websiteObject, 'chronologies');
                    }
                    $ids[] = $rowModel->id;
                }
            }
        }
        return $ids;
    }

    private function deleteOldChronologies($parentId, $relationTable, $foreignField)
    {
        try {
            WebsiteObject::whereExists(function ($query) use ($parentId, $foreignField, $relationTable) {
                $query->select(DB::raw(1))
                    ->from('chronologies')
                    ->join($relationTable, 'chronologies.id', '=', "{$relationTable}.chronology_id")
                    ->whereRaw('chronologies.id = website_objects.object_id')
                    ->where("{$relationTable}.{$foreignField}", $parentId);
            })->where('object_type', Chronology::class)
            ->delete();
            Chronology::whereExists(function ($query) use ($parentId, $foreignField, $relationTable) {
                $query->select(DB::raw(1))
                    ->from($relationTable)
                    ->whereRaw("chronologies.id = {$relationTable}.chronology_id")
                    ->where("{$relationTable}.{$foreignField}", $parentId);
            })->delete();
        } catch (Throwable $e) {
            Log::stack(['importLogStack', 'rollbar'])->error($e);
            return false;
        }
        return true;
    }

    private function _createPropertyItem($propertyInfo)
    {
        $externalID = $propertyInfo["external_id"];

        // Check if we have the property already inserted
        $property = PropertyService::findByExternalId($externalID);
        $geolocation = null;
        // Else create it
        $websiteObject = null;
        if (is_null($property)) {
            $propertyInfoForInsertion = Arr::only($propertyInfo, [
                'external_id',
            ]);
            $propertyInfoForInsertion['external_reference'] = $propertyInfo['title'];
            if (array_key_exists('location', $propertyInfo)) {
                if (array_key_exists('local', $propertyInfo['location'])) {
                    $local = $this->factoryFind('Location', ['external_id' => $propertyInfo['location']['local']]);
                    if ($local) {
                        $propertyInfoForInsertion['local_id'] = $local->id;
                    }
                }
                if (array_key_exists('longitude', $propertyInfo[$this->defaultLanguage]['location']) && array_key_exists('latitude', $propertyInfo[$this->defaultLanguage]['location'])) {
                    $propertyInfoForInsertion['geolocation'] = new Point($propertyInfo[$this->defaultLanguage]['location']['latitude'], $propertyInfo[$this->defaultLanguage]['location']['longitude']);
                }
            } elseif (array_key_exists('location', $propertyInfo[$this->defaultLanguage])) {
                if (array_key_exists('local', $propertyInfo[$this->defaultLanguage]['location'])) {
                    $local = $this->factoryFind('Location', ['external_id' => $propertyInfo[$this->defaultLanguage]['location']['local']]);
                    if ($local) {
                        $propertyInfoForInsertion['local_id'] = $local->id;
                    }
                }
                if (array_key_exists('longitude', $propertyInfo[$this->defaultLanguage]['location']) && array_key_exists('latitude', $propertyInfo[$this->defaultLanguage]['location'])) {
                    $propertyInfoForInsertion['geolocation'] = new Point($propertyInfo[$this->defaultLanguage]['location']['latitude'], $propertyInfo[$this->defaultLanguage]['location']['longitude']);
                }
            }
            $property = PropertyService::create($propertyInfoForInsertion);
            $slug = BackofficeService::generateSlug(
                $this->decodeItemText($propertyInfo[$this->defaultLanguage]['property_title']),
                $this->defaultLanguage
            );

            // Create Website Objects
            $websiteObject = $this->factoryFind("GenericBackoffice\WebsiteObject", [
                'object_id'     => $property->id,
                'object_type'   => get_class($property),
                'content_type'  => 'properties',
            ]);
            if (is_null($websiteObject)) {
                $websiteObject = $this->createWebsiteObject($property, 'properties', $slug);
            }

            // Associate Website Object
            $property->websites()->save($websiteObject);
        } else {
            $websiteObject = $this->factoryFind("GenericBackoffice\WebsiteObject", [
                'object_id'     => $property->id,
                'object_type'   => get_class($property),
                'content_type'  => 'properties',
            ]);
            $slug = $websiteObject->slug ?? null;
            if (array_key_exists('location', $propertyInfo)) {
                if (array_key_exists('longitude', $propertyInfo[$this->defaultLanguage]['location']) && array_key_exists('latitude', $propertyInfo[$this->defaultLanguage]['location'])) {
                    $geolocation = new Point($propertyInfo[$this->defaultLanguage]['location']['geoY'], $propertyInfo[$this->defaultLanguage]['location']['longitude']);
                }
            } elseif (array_key_exists('location', $propertyInfo[$this->defaultLanguage])) {
                if (array_key_exists('longitude', $propertyInfo[$this->defaultLanguage]['location']) && array_key_exists('latitude', $propertyInfo[$this->defaultLanguage]['location'])) {
                    $geolocation = new Point($propertyInfo[$this->defaultLanguage]['location']['latitude'], $propertyInfo[$this->defaultLanguage]['location']['longitude']);
                }
            }
            if (!is_null($geolocation)) {
                $property->geolocation = $geolocation;
                $property->save();
            }
        }
        $propertyType = $this->factoryFind('PropertyType', [
            'external_id' => $propertyInfo['property_type']
        ]);
        if ($propertyType) {
            $property->property_type_id = $propertyType->id;
            $property->save();
        }
        // Create language relationships
        foreach ($this->availableLanguages as $langCode => $lang) {
            if (!empty($propertyInfo[$langCode])) {
                try {
                    $description = $this->prepareItalicText($this->decodeItemText($propertyInfo[$langCode]['description'], $this->allowedTagsWithSup));
                    $description = $this->formatForGlossary($description, $langCode);
                } catch (Throwable $e) {
                    Log::stack(['importLogStack', 'rollbar'])->error($e, ['PropertyExternalID' => $propertyInfo['property_type']]);
                    $description = $this->decodeItemText($propertyInfo[$langCode]['description'], $this->allowedTagsWithSup);
                }
                if (array_key_exists('address', $propertyInfo[$langCode]['location'])) {
                    $address = $propertyInfo[$langCode]['location']['address'];
                } else {
                    $address = null;
                }
                $propertyLang = $this->factoryUpdateOrInsert("PropertyLanguage", [
                    'property_id' => $property->id,
                    "language_id" => $langCode,
                ], [
                    'last_update'        => $propertyInfo[$langCode]['last_update'],
                    'title'              => $this->decodeItemText($propertyInfo[$langCode]['property_title']),
                    'frontend_title'     => $this->decodeItemText($propertyInfo[$langCode]['property_title'], "<i><sup>"),
                    'description'        => $description,
                    'popup_description'  => $this->decodeItemText($propertyInfo[$langCode]['popup_description'], $this->allowedTags),
                    'excavation'         => $this->decodeItemText($propertyInfo[$langCode]['excavation']),
                    'reading_suggestion' => $this->decodeItemText($propertyInfo[$langCode]['reading_suggestion'], $this->allowedTags),
                    'plunder_locations'  => $this->decodeItemText($propertyInfo[$langCode]['plunder_locations'], $this->allowedTags),
                    'address'            => $this->decodeItemText($address)
                ]);

                if (!is_null($websiteObject)) {
                    // Create Website Object Language
                    $websiteObjectLanguage = $this->factoryFind("GenericBackoffice\WebsiteObjectLanguage", [
                        'website_object_id' => $websiteObject->id,
                        'wo_language_id'    => $langCode,
                    ]);
                    if (is_null($websiteObjectLanguage)) {
                        $langSlug = BackofficeService::generateSlug(
                            $this->decodeItemText($propertyInfo[$langCode]['property_title']),
                            $langCode
                        );
                        $this->createWebsiteObjectLanguage($websiteObject, $langCode, $langSlug);
                    }
                }
            }
        }
        $themeIds = $this->factoryMassFind('Theme', 'external_id', $propertyInfo['themes'], true)->pluck('id')->toArray();
        $classificationIds = $this->factoryMassFind('Classification', 'external_id', $propertyInfo['classifications'], true)->pluck('id')->toArray();
        $typologyIds = $this->factoryMassFind('Typology', 'external_id', $propertyInfo['typologies'], true)->pluck('id')->toArray();
        $accessIds = $this->factoryMassFind('Access', 'external_id', $propertyInfo['accesses'], true)->pluck('id')->toArray();
        //COMMENT
        $imageIds = $this->uploadImages($propertyInfo['images'], $property);
        if (!is_null($propertyInfo['chronologies'])) {
            $chronologyIds = $this->createChronologyTaxonomy($propertyInfo['chronologies'], $property->id, 'properties', 'chronology_property', 'property_id');
            try {
                $property->chronologies()->sync($chronologyIds);
            } catch (Throwable $e) {
                Log::stack(['importLogStack', 'rollbar'])->error($e);
                return false;
            }
        } else {
            $this->deleteOldChronologies($property->id, 'chronology_property', 'property_id');
        }
        $audiovisuals = [];
        if (!is_null($propertyInfo['audiovisuals'])) {
            $audiovisuals = $this->createAudioVisuals($propertyInfo);
        }
        $property->themes()->sync($themeIds);
        $property->classifications()->sync($classificationIds);
        $property->typologies()->sync($typologyIds);
        $property->accesses()->sync($accessIds);
        //COMMENT
        $property->images()->sync($imageIds);
        $property->audioVisuals()->sync($audiovisuals);

        $websiteObject = $property->websites()->first();
        if (!is_null($websiteObject)) {
            $this->updateUri($websiteObject, 'properties');
        }

        Log::channel('importLogStack')->info('Created Property with ID: ' . $property->id);

        return $websiteObject->id;
    }
    private function _createEpigraphItem($epigraphInfo)
    {
        $externalID = $epigraphInfo["external_id"];
        // Check if we have the epigraph already inserted
        $epigraph = EpigraphService::findByExternalId($externalID);
        // Else create it
        $websiteObject = null;
        if (is_null($epigraph)) {
            $epigraphInfoForInsertion = Arr::only($epigraphInfo, [
                'external_id',
            ]);
            $inscriptionTypeId = $this->createInscriptionTaxonomy(InscriptionType::class, $epigraphInfo['inscription_type'], 'tipo_inscricao', 'tipo_inscricao_id', 'inscriptiontypes', 'inscription_type_id');
            $techniqueId = $this->createInscriptionTaxonomy(Technique::class, $epigraphInfo['technique'], 'tecnica', 'tecnica_id', 'techniques', 'technique_id');
            $idiomId = $this->createInscriptionTaxonomy(Idiom::class, $epigraphInfo['idiom'], 'idioma', 'idioma_id', 'idioms', 'idiom_id');
            $epigraphInfoForInsertion['inscription_type_id'] = $inscriptionTypeId;
            $epigraphInfoForInsertion['technique_id'] = $techniqueId;
            $epigraphInfoForInsertion['idiom_id'] = $idiomId;
            $epigraphInfoForInsertion['external_reference'] = $epigraphInfo['external_reference'];
            $epigraphInfoForInsertion['local_id'] = $this->getEpigraphLocation($epigraphInfo['local_id']);
            $epigraph = EpigraphService::create($epigraphInfoForInsertion);
            $slug = BackofficeService::generateSlug(
                $this->decodeItemText($epigraphInfo[$this->defaultLanguage]['title']),
                $this->defaultLanguage
            );

            // Create Website Objects
            $websiteObject = $this->factoryFind("GenericBackoffice\WebsiteObject", [
                'object_id'     => $epigraph->id,
                'object_type'   => get_class($epigraph),
                'content_type'  => 'epigraphs',
            ]);
            if (is_null($websiteObject)) {
                $websiteObject = $this->createWebsiteObject($epigraph, 'epigraphs', $slug);
            }

            // Associate Website Object
            $epigraph->websites()->save($websiteObject);
        } else {
            // Create Website Objects
            $websiteObject = $this->factoryFind("GenericBackoffice\WebsiteObject", [
                'object_id'     => $epigraph->id,
                'object_type'   => get_class($epigraph),
                'content_type'  => 'epigraphs',
            ]);
            $slug = $websiteObject->slug ?? null;
        }

        // Create language relationships
        foreach ($this->availableLanguages as $langCode => $lang) {
            if (!empty($epigraphInfo[$langCode])) {
                // When Glossary is working, replace the uncommented line below with the commented one
                try {
                    $description = $this->prepareItalicText($this->decodeItemText($epigraphInfo[$langCode]['description'], $this->allowedTagsWithSup));
                    $description = $this->formatForGlossary($description, $langCode);
                } catch (Throwable $e) {
                    Log::stack(['importLogStack', 'rollbar'])->error($e, ['EpigraphExternalID' => $epigraphInfo['external_reference']]);
                    $description = $this->decodeItemText($epigraphInfo[$langCode]['description'], $this->allowedTagsWithSup);
                }
                $epigraphLang = $this->factoryUpdateOrInsert("EpigraphLanguage", [
                    'epigraph_id' => $epigraph->id,
                    "language_id" => $langCode,
                ], [
                    'last_update'               => $epigraphInfo[$langCode]['last_update'],
                    'inscription_text'          => $epigraphInfo[$langCode]['inscription_text'],
                    'inscription_translation'   => $epigraphInfo[$langCode]['inscription_translation'],
                    'description'               => $description,
                    'plunder_location'          => $this->decodeItemText($epigraphInfo[$langCode]['plunder_location'], $this->allowedTags),
                    'reading_suggestion'        => $this->decodeItemText($epigraphInfo[$langCode]['reading_suggestion'], $this->allowedTags),
                    'title'                     => $this->decodeItemText($epigraphInfo[$langCode]['title']),
                    'present_location'          => $this->decodeItemText($epigraphInfo[$langCode]['present_location']),
                    'location_found'            => $this->decodeItemText($epigraphInfo[$langCode]['location_found']),
                    'epigraph_title'            => $this->decodeItemText($epigraphInfo[$langCode]['epigraph_title'], $this->allowedTagsWithSup),
                ]);

                if (!is_null($websiteObject)) {
                    // Create Website Object Language
                    $websiteObjectLanguage = $this->factoryFind("GenericBackoffice\WebsiteObjectLanguage", [
                        'website_object_id' => $websiteObject->id,
                        'wo_language_id'    => $langCode,
                    ]);
                    if (is_null($websiteObjectLanguage)) {
                        $langSlug = BackofficeService::generateSlug(
                            $this->decodeItemText($epigraphInfo[$langCode]['title']),
                            $langCode
                        );
                        $this->createWebsiteObjectLanguage($websiteObject, $langCode, $langSlug);
                    }
                }
            }
        }

        $themeIds = $this->factoryMassFind('Theme', 'external_id', $epigraphInfo['themes'], true)->pluck('id')->toArray();
        $classificationIds = $this->factoryMassFind('Classification', 'external_id', $epigraphInfo['classifications'], true)->pluck('id')->toArray();
        $properties = $this->factoryMassFind('Property', 'external_id', $epigraphInfo['relatedProperties'], true);
        $propertyIds = [];
        foreach ($properties as $property) {
            $propertyIds[$property->id] = [
                'found_local_id' => Epigraph::foundLocalId($property->property_type_id)
            ];
        }
        //COMMENT
        $imageIds = $this->uploadImages($epigraphInfo['images'], $epigraph);
        $chronologyIds = $this->createChronologyTaxonomy($epigraphInfo['chronologies'], $epigraph->id, 'epigraphs', 'chronology_epigraph', 'epigraph_id');
        $audiovisualIds = [];
        if (!is_null($epigraphInfo['audiovisuals'])) {
            $audiovisualIds = $this->createAudioVisuals($epigraphInfo);
        }
        $epigraph->themes()->sync($themeIds);
        $epigraph->classifications()->sync($classificationIds);
        //COMMENT
        $epigraph->images()->sync($imageIds);
        $epigraph->properties()->sync($propertyIds);
        $epigraph->audioVisuals()->sync($audiovisualIds);

        $websiteObject = $epigraph->websites()->first();
        if (!is_null($websiteObject)) {
            $this->updateUri($websiteObject, 'properties');
        }
        Log::channel('importLogStack')->info('Created Epigraph with ID: ' . $epigraph->id);
        return $websiteObject->id;
    }

    private function getEpigraphLocation($id)
    {
        if ($id) {
            $local = $this->factoryFind('Location', ['external_id' => $id]);
            if ($local) {
                return $local->id;
            }
        }
        return null;
    }

    private function _createTaxonomyItem($listItemLangs, $class, $classLang, $contentType, $idField, $parentID = null)
    {
        $item = $this->factoryFind($class, [
            'external_id' => $listItemLangs[$this->defaultLanguage]->ID
        ]);
        if (!is_null($item) && property_exists($listItemLangs[$this->defaultLanguage], 'ParentID')) {
            $item['parent_id'] = $listItemLangs[$this->defaultLanguage]->ParentID;
            $item->save();
        }
        $websiteObject = null;
        if (empty($item)) {
            $item = $this->factoryCreate($class, [
                'external_id' => $listItemLangs[$this->defaultLanguage]->ID
            ]);
            if (property_exists($listItemLangs[$this->defaultLanguage], 'ParentID')) {
                $item['parent_id'] = $listItemLangs[$this->defaultLanguage]->ParentID;
            }
            $item->save();

            $slug = BackofficeService::generateSlug(
                $listItemLangs[$this->defaultLanguage]->Valor,
                $this->defaultLanguage
            );


            $websiteObject = $this->factoryFind("GenericBackoffice\WebsiteObject", [
                'object_id'     => $item->id,
                'object_type'   => get_class($item),
                'content_type'  => $contentType,
            ]);
            // Create Website Objects
            if (is_null($websiteObject)) {
                $websiteObject = $this->createWebsiteObject($item, $contentType, $slug, $parentID);
            }

            // Associate Website Object
            $item->websites()->save($websiteObject);
        }

        // Create language relationships
        foreach ($this->availableLanguages as $langCode => $lang) {
            if (!empty($listItemLangs[$langCode])) {
                $itemLang = $this->factoryUpdateOrInsert(
                    $classLang,
                    [
                    $idField      => $item->id,
                    "language_id" => $langCode,
                ],
                    [
                    "title" => $listItemLangs[$langCode]->Valor
                    ]
                );
                if (is_null($websiteObject)) {
                    $slug = BackofficeService::generateSlug(
                        $listItemLangs[$this->defaultLanguage]->Valor,
                        $this->defaultLanguage
                    );
                    $websiteObject = $this->createWebsiteObject($item, $contentType, $slug, $parentID);
                }
                if (!is_null($websiteObject)) {
                    // Create Website Object Language
                    $this->createWebsiteObjectLanguage($websiteObject, $langCode);
                }
            }
        }

        $websiteObject = $item->websites()->first();
        if (!is_null($websiteObject)) {
            $this->updateUri($websiteObject, $contentType);
        }
        Log::channel('importLogStack')->info('Created ' . $class . ' with ID: ' . $item->id);

        return $websiteObject->id;
    }

    private function createWebsiteObject($objectType, $contentType, $slug, $parentID = null)
    {
        if (strlen($slug) > 200) {
            $slug = substr(substr($slug, 0, 200), 0, strripos($slug, '-', 0));
            $slug = BackofficeService::generateSlug($slug, $this->defaultLanguage);
        }
        $websiteObject = $this->factoryFind("GenericBackoffice\WebsiteObject", [
            'object_id'     => $objectType->id,
            'object_type'   => get_class($objectType),
            'content_type'  => $contentType,
        ]);
        if (is_null($websiteObject)) {
            $websiteObject = $this->factoryCreate("GenericBackoffice\WebsiteObject", [
                'object_id'    => $objectType->id,
                'object_type'  => get_class($objectType),
                'content_type' => $contentType,
                'slug'         => $slug,
                'status'       => WebsiteObject::STATUS_PUBLISHED,
                'parent_id'    => $parentID
            ]);
            $websiteObject->save();
        }

        return $websiteObject;
    }

    private function createWebsiteObjectLanguage($websiteObject, $langCode, $slug = null)
    {
        if (is_null($slug)) {
            $slug = $websiteObject->slug;
        }

        $websiteObjectLang = $this->factoryFind("GenericBackoffice\WebsiteObjectLanguage", [
            'website_object_id' => $websiteObject->id,
            'wo_language_id'    => $langCode,
            'lang_slug'         => $slug
        ]);
        if (is_null($websiteObjectLang)) {
            $websiteObjectLang = $this->factoryCreate("GenericBackoffice\WebsiteObjectLanguage", [
                'website_object_id' => $websiteObject->id,
                'wo_language_id'    => $langCode,
                'lang_slug'         => $slug
            ]);
            $saved = $websiteObjectLang->save();
        }

        return $websiteObjectLang;
    }

    private function updateUri($websiteObject, $type)
    {
        $contentType = ContentTypes::find($type);
        $uri = $contentType->getBaseUri();
        $languages = getAvailableLanguages();
        $defaultLang = getDefaultLanguage();

        $defaultUri = "";
        foreach ($languages as $langCode => $lang) {
            $woLang = $websiteObject->languages->findByLocale($langCode);
            if ($woLang) {
                if (strlen($woLang->lang_slug) > 200) {
                    $woLang->lang_slug = substr(substr($woLang->lang_slug, 0, 200), 0, strripos($woLang->lang_slug, '-', 0));
                }
                if (is_array($uri)) {
                    $uriLang = $uri[$langCode];
                } else {
                    $uriLang = $uri;
                }
                $woLang->lang_uri = $uriLang.'/'.$woLang->lang_slug;
                if ($defaultLang === $langCode) {
                    $defaultUri = $woLang->lang_uri;
                } elseif ($defaultLang !== 'pt' && $langCode === 'pt') {
                    $defaultUri = $woLang->lang_uri;
                }
                $woLang->save();
            }
        }

        $websiteObject->uri = $defaultUri;
        $websiteObject->save();
    }

    private function uploadImages($images, $model)
    {
        $imageIds = [];
        // Code Below is to Refresh their Positions in the Model, since they are ordered by their ID
        if ($model->images) {
            try {
                $idsToDelete = $model->images()->pluck("id");
                WebsiteObject::where('content_type', 'propertyimages')->whereIn('object_id', $idsToDelete)->delete();
                PropertyImage::destroy($idsToDelete);
                $model->images()->detach();
            } catch (Throwable $e) {
                Log::stack(['importLogStack', 'rollbar'])->error("Error Trying to Refresh images from ({get_class($model)}) with External ID ({$model->external_id})");
            }
        }
        foreach ($images as $image) {
            if (array_key_exists('file', $image)) {
                $mediaItem = $this->factoryFind('MediaItem', ['external_id' => $image['id']]);
                $fileName = random_string();

                $slug = BackofficeService::generateSlug($fileName, $this->defaultLanguage);
                if (is_null($mediaItem)) {
                    if (!empty($image['file'])) {
                        $imageFile = $this->createImageFromExternalFile($fileName, $image['file'], 'jpg');
                        $mediaItem = $this->factoryCreate('MediaItem', [
                            'backend_user_id' => 1,
                            'filename' => $fileName . '.jpg',
                            'original_filename' => $fileName . '.jpg',
                            'extension' => 'jpg',
                            'media_type' => 'image',
                            'width' => $imageFile->width(),
                            'height' => $imageFile->height(),
                            'external_id' => $image['id'],
                        ]);
                        $mediaItem->save();
                    }
                    $propertyImage = $this->factoryFind('PropertyImage', [
                        'external_id' => $image['id']
                    ]);
                } else {
                    $propertyImage = $this->factoryFind('PropertyImage', [
                        'external_id' => $mediaItem->external_id
                    ]);
                }
                $websiteObject = null;
                if (is_null($propertyImage) && !is_null($mediaItem)) {
                    $propertyImage = $this->factoryCreate('PropertyImage', [
                        'external_id' => $mediaItem->external_id,
                        'image_id' => $mediaItem->id,
                    ]);
                    $propertyImage->save();
                    $websiteObject = $this->createWebsiteObject($propertyImage, 'propertyimages', $slug);
                    $propertyImage->websites()->save($websiteObject);
                }
                if (!is_null($mediaItem) && !is_null($propertyImage)) {
                    foreach ($this->availableLanguages as $langCode => $lang) {
                        if (array_key_exists($langCode, $image)) {
                            $propertyImageLanguage = $this->factoryUpdateOrInsert('PropertyImageLanguage', [
                                'property_image_id' => $propertyImage['id'],
                                'language_id' => $langCode
                            ], [
                                'title' => $image[$langCode]['legend']
                            ]);
                        } else {
                            $propertyImageLanguage = $this->factoryUpdateOrInsert('PropertyImageLanguage', [
                                'property_image_id' => $propertyImage['id'],
                                'language_id' => $langCode
                            ], [
                                'title' => $image[$this->defaultLanguage]['legend']
                            ]);
                        }
                        if (is_null($websiteObject)) {
                            $websiteObject = $this->createWebsiteObject($propertyImage, 'propertyimages', $slug);
                            $propertyImage->websites()->save($websiteObject);
                        }
                        if (!is_null($websiteObject)) {
                            $this->createWebsiteObjectLanguage($websiteObject, $langCode);
                        }
                    }
                    $websiteObject = $propertyImage->websites()->first();
                    if (!is_null($websiteObject)) {
                        $this->updateUri($websiteObject, 'propertyimages');
                    }
                }
            }
            if (!is_null($propertyImage)) {
                $imageIds[] = $propertyImage->id;
            }
        }
        return $imageIds;
    }

    private function factoryCreate($type, array $options = [])
    {
        $class = "App\Models\\".$type;

        return new $class($options);
    }

    private function factoryFind($type, $searchArguments, $findAll = false)
    {
        $class = "App\Models\\".$type;
        if ($findAll) {
            return $class::where($searchArguments)->get();
        } else {
            return $class::where($searchArguments)->first();
        }
    }

    private function factoryMassFind($type, $column, $values, $findAll = false)
    {
        $class = "App\Models\\".$type;
        if ($findAll) {
            return $class::whereIn($column, $values)->get();
        } else {
            return $class::whereIn($column, $values)->first();
        }
    }

    private function factoryUpdateOrInsert($type, array $attributes = [], array $values = [])
    {
        $class = "App\Models\\".$type;

        return $class::updateOrInsert($attributes, $values);
    }

    private function createImageFromBase64($fileName, $b64, $extension)
    {
        $image = Image::make($b64)->orientate();
        $image->save(storage_path('app/public/uploads/media') . '/' . $fileName . '.' . $extension, 90, $extension);
        return $image;
    }

    private function createImageFromExternalFile($fileName, $file, $extension)
    {

        $valid_types = ['image/png', 'image/jpeg'];
        $client = new Client(['verify' => false]);

        $response = $client->get($file);
        if (empty($response->getHeaders()['content-type'][0])) {
            throw new Exception("{$file} is not an image");
        }
        $contentType = $response->getHeaders()['content-type'][0];
        if (Str::contains($contentType, ';')) {
            $contentType = explode(';', $contentType)[0];
        }

        if (!in_array($contentType, $valid_types, true)){
            throw new Exception("{$file} is not an image");
        }

        $image = Image::make($response->getBody()->getContents())->orientate();
        $image->save(storage_path('app/public/uploads/media') . '/' . $fileName . '.' . $extension, 90, $extension);
        return $image;
    }

    public function decodeItemText($text, String $allowedTags = '')
    {
        if (!is_null($text)) {
            return strip_tags(html_entity_decode($text, ENT_NOQUOTES, 'UTF-8'), $allowedTags);
        } else {
            return null;
        }
    }

    private function getEpigraphInscriptionFieldId($item, $field)
    {
        if (array_key_exists($field, $item)) {
            return $item[$field];
        }
        return null;
    }

    private function getGlossaryTerms($lang = null) {
        if (empty($this->glossaryTerms)) {
            $this->glossaryTerms = GlossaryTermLanguage::join('website_objects', function($join) {
                $join->on('glossary_term_languages.glossary_term_id', '=', 'website_objects.object_id')
                    ->where('website_objects.object_type', '=', GlossaryTerm::class);
            })->select('glossary_term_languages.title', 'glossary_term_languages.translation_id', 'glossary_term_languages.language_id', 'glossary_term_languages.glossary_term_id')
                ->orderBy('website_objects.position', 'ASC')->get();
        }
        if (!is_null($lang)) {
            return $this->glossaryTerms->filter(function($glossaryTerm) use ($lang) {
                return $glossaryTerm->language_id == $lang;
            });
        } else {
            return $this->glossaryTerms;
        }
    }

    public function formatForGlossary($text, $lang)
    {
        $glossaryTerms = $this->getGlossaryTerms($lang);
        foreach ($glossaryTerms as $term) {
            $title = preg_quote($term->title, "/");
            $text = preg_replace_callback(
                "/(?<=^|,|\.|\s|!|\?|;|\(|\[|<br>)(?:" . $title . "|" . mb_strtolower($title, 'UTF-8') . ")(?=$|,|\.|\s|!|\?|;|\)|\]|:)/i",
                function ($matches) use ($term) {
                    return "<span class='glossary' data-id='{$term->glossary_term_id}'><div class='glossary-tooltip'></div>{$matches[0]}</span>";
                },
                $text
            );
        }
        /* if ($lang == 'en')
        dd($text); */
        return $text;
    }

    public function prepareItalicText($text) {
        //prepare italic with spaces before ending tag (<i>example </i>)
        $text = preg_replace_callback(
            "/\s<\/i>/",
            function () {
                return "</i> ";
            },
            $text
        );
        //prepare italic with spaces after starting tag (<i> example</i>)
        $text = preg_replace_callback(
            "/<i>\s/",
            function () {
                return " <i>";
            },
            $text
        );
        return $text;
    }

    public function refreshUri(array $contentTypes)
    {
        $contentTypeWo = WebsiteObject::with('languages')
        ->whereIn('content_type', array_keys($contentTypes))
        ->delete();
        foreach ($contentTypes as $contentType => $model) {
            Log::channel('importLogStack')->info("Preparing to re-create {$contentType} website objects");
            $contentTypeFound = ContentTypes::find($contentType);
            $uri = $contentTypeFound->getBaseUri();
            $allModelRows = $model::with('languages')->get();
            foreach ($allModelRows as $row) {
                $titles = [];
                foreach ($this->availableLanguages as $lang => $value) {
                    $modelLang = $row->languages->findByLocale($lang);
                    if ($modelLang) {
                        $titles[$lang] = BackofficeService::generateSlug($this->decodeItemText(substr($modelLang->title, 0, 200)), $lang);
                    }
                }

                $slug = $titles[$this->defaultLanguage];
                $websiteObject = $this->createWebsiteObject($row, $contentType, $slug);
                foreach ($row->languages as $rowLang) {
                    $slug = $titles[$rowLang->language_id];
                    $this->createWebsiteObjectLanguage($websiteObject, $rowLang->language_id, $slug);
                }
                $rowWebsiteObject = $row->websites()->first();
                if (!is_null($rowWebsiteObject)) {
                    $this->updateUri($websiteObject, $contentType);
                }
                Log::channel('importLogStack')->info("Created WebsiteObject (ID -> {$websiteObject->id}) [{$contentType} (ID -> {$websiteObject->object_id})]");
            }
        }
    }

    public function generateSitemap() {
        //TODO: If new languages are added, consider changing this to getAvailableLanguages()
        $defaultLang = getDefaultLanguage();
        $slugFrontend = config('app.env') == 'production' ? 'https://lisboaromana.pt' : 'https://lisboa-romana-frontend.eae.pt';
        $sitemap = SitemapGenerator::create($slugFrontend)->getSitemap();
        //Homepage
        $url = Url::create($slugFrontend);
        foreach($this->availableLanguages as $lang => $value) {
            if ($lang == $defaultLang) {
                $url->addAlternate($slugFrontend, $lang);
            } else {
                $url->addAlternate($slugFrontend . "/" . $lang, $lang);
            }
        }
        $sitemap->add($url);
        //Properties
        $properties = Property::with('websites.languages')->get();
        foreach ($properties as $property) {
            $defaultLangUri = $property->websites->first()->languages->findByLocale($defaultLang)->lang_slug;
            $url = Url::create($slugFrontend . config('backoffice.generic.properties.siteMap.' . $defaultLang) . $defaultLangUri)
                ->setPriority(1)
                ->setLastModificationDate($property->updated_at);
            foreach($this->availableLanguages as $lang => $value) {
                $langUri = $property->websites->first()->languages->findByLocale($lang)->lang_slug;
                if ($lang == $defaultLang) {
                    $url->addAlternate($slugFrontend . config('backoffice.generic.properties.siteMap.' . $lang) . $langUri, $lang);
                } else {
                    $url->addAlternate($slugFrontend . "/" . $lang . config('backoffice.generic.properties.siteMap.' . $lang) . $langUri, $lang);
                }
            }
            $sitemap->add($url);
        }
        //Epigraph
        $epigraphs = Epigraph::with('websites.languages')->get();
        foreach ($epigraphs as $epigraph) {
            $defaultLangUri = $epigraph->websites->first()->languages->findByLocale($defaultLang)->lang_slug;
            $url = Url::create($slugFrontend . config('backoffice.generic.epigraphs.siteMap.' . $defaultLang) . $defaultLangUri)
                ->setPriority(1)
                ->setLastModificationDate($property->updated_at);
            foreach($this->availableLanguages as $lang => $value) {
                $langUri = $epigraph->websites->first()->languages->findByLocale($lang)->lang_slug;
                if ($lang == $defaultLang) {
                    $url->addAlternate($slugFrontend . config('backoffice.generic.epigraphs.siteMap.' . $lang) . $langUri, $lang);
                } else {
                    $url->addAlternate($slugFrontend . "/" . $lang . config('backoffice.generic.epigraphs.siteMap.' . $lang) . $langUri, $lang);
                }
            }
            $sitemap->add($url);
        }
        //Events - Occasions
        $url = Url::create($slugFrontend . config('backoffice.generic.occasions.siteMap.' . $defaultLang));
        foreach($this->availableLanguages as $lang => $value) {
            if ($lang == $defaultLang) {
                $url->addAlternate($slugFrontend . config('backoffice.generic.occasions.siteMap.' . $lang), $lang);
            } else {
                $url->addAlternate($slugFrontend . "/" . $lang . config('backoffice.generic.occasions.siteMap.' . $lang), $lang);
            }
        }
        $sitemap->add($url);

        $events = Occasion::with('websites.languages')->get();
        foreach ($events as $event) {
            $defaultLangUri = $event->websites->first()->languages->findByLocale($defaultLang)->lang_slug;
            $url = Url::create($slugFrontend . config('backoffice.generic.occasions.siteMap.' . $defaultLang) . $defaultLangUri)
                ->setPriority(1)
                ->setLastModificationDate($property->updated_at);
            foreach($this->availableLanguages as $lang => $value) {
                $langUri = $event->websites->first()->languages->findByLocale($lang)->lang_slug;
                if ($lang == $defaultLang) {
                    $url->addAlternate($slugFrontend . config('backoffice.generic.occasions.siteMap.' . $lang) . $langUri, $lang);
                } else {
                    $url->addAlternate($slugFrontend . "/" . $lang . config('backoffice.generic.occasions.siteMap.' . $lang) . $langUri, $lang);
                }
            }
            $sitemap->add($url);
        }
        //Static Pages
        $staticPages = [
            '/about-us',
            '/storytelling',
            '/privacy-policy',
            '/technical-record',
            '/repository'
        ];

        foreach ($staticPages as $staticPage) {
            $url = Url::create($slugFrontend . $staticPage);
            foreach($this->availableLanguages as $lang => $value) {
                if ($lang == $defaultLang) {
                    $url->addAlternate($slugFrontend . $staticPage, $lang);
                } else {
                    $url->addAlternate($slugFrontend . "/" . $lang . $staticPage, $lang);
                }
            }
            $sitemap->add($url);
        }
        //END
        $sitemap->writeToFile(public_path('sitemap.xml'));
    }
}
