<?php

namespace App\Libs\Services;

use App\Libs\Backoffice\Generic\Fields\MultiplePickerField;
use App\Libs\Facades\ContentTypes;
use App\Libs\Facades\Localization;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Spatie\TranslationLoader\LanguageLine;
use Language;
use MenuItemLanguage;
use RuntimeException;
use Throwable;
use Website;
use WebsiteObjectLanguage;
use WebsiteObjectSearch;

class LanguageService
{
    protected $defaultLang = 'en';

    public function __construct()
    {
        $this->languages = Localization::getAllLanguages();
        $this->defaultLang = Localization::getDefaultLocale();
    }

    public function createOrResetTranslations($targetLanguage, $sourceLanguage, $initialStatus = 'draft')
    {
        Log::info("Start cloning language: " . $targetLanguage);
        DB::beginTransaction();
        try {
            $this->createMenuTranslations($targetLanguage, $sourceLanguage);
            $this->createContentTranslations($targetLanguage, $sourceLanguage);
            $this->createWOTranslations($targetLanguage, $sourceLanguage, $initialStatus);
            DB::commit();
            Log::info("Content created / cloned for language: " . $targetLanguage);
        } catch (Throwable $e) {
            DB::rollback();
            Log::error($e);
            Log::error("ERROR creating / cloning language: " . $targetLanguage);
        }
    }

    public function createMenuTranslations($languageId, $baseLanguageId = 'en')
    {
        $baseTranslations = MenuItemLanguage::where('language_id', $baseLanguageId)->get();
        foreach ($baseTranslations as $translation) {
            $oldTranslation = MenuItemLanguage::where('language_id', $languageId)
                ->where('menu_item_id', $translation->menu_item_id)
                ->first();
            if (!empty($oldTranslation)) {
                $oldTranslation->delete();
            }
            $newTranslation = new MenuItemLanguage($translation->toArray());
            $newTranslation->translation_id = null;
            $newTranslation->language_id = $languageId;
            $newTranslation->save();
        }
    }

    public function createWOTranslations($languageId, $baseLanguageId = 'en', $initialStatus = 'draft')
    {
        //Website objects lang
        $baseWoTranslations = WebsiteObjectLanguage::where('wo_language_id', $baseLanguageId)
            ->where('wo_website_id', 1)
            ->get();
        foreach ($baseWoTranslations as $translation) {
            $oldTranslation = WebsiteObjectLanguage::where('wo_language_id', $languageId)
                ->where('wo_website_id', 1)
                ->where('website_object_id', $translation->website_object_id)->first();
            if (!empty($oldTranslation)) {
                $oldTranslation->delete();
            }

            $newTranslation = new WebsiteObjectLanguage($translation->toArray());
            $newTranslation->wo_translation_id = null;
            $newTranslation->wo_language_id = $languageId;
            if ($initialStatus === 'draft') {
                $newTranslation->lang_status = 'draft';
            }

            $sameSlug = WebsiteObjectLanguage::where('wo_website_id', 1)
                ->where('wo_language_id', $languageId)
                ->where('lang_slug', $translation->lang_slug)
                ->first();
            if (!empty($sameSlug)) {
                Log::warning("Trying to clone wo translation to an existing slug: " . $translation->lang_slug
                    . " origin wo id: " . $translation->website_object_id . ", found wobject id: " . $sameSlug->website_object_id);

                $targetSlug = mb_substr($translation->lang_slug, 0, (149 - strlen($languageId))) . '-' . $languageId;
                $targetUri = mb_substr($translation->lang_uri, 0, (299 - strlen($languageId))) . '-' . $languageId;

                Log::info("Changing slug to: " . $targetSlug);
                $newTranslation->lang_slug = $targetSlug;
                $newTranslation->lang_uri = $targetUri;
            }

            $newTranslation->save();
        }
    }

    public function createContentTranslations($languageId, $baseLanguageId = 'en')
    {
        $contentTypes = ContentTypes::all();

        foreach ($contentTypes as $contentType) {
            $modelName = $contentType->getModelType();

            $model = new $modelName();
            $langModel = $model->languages()->getModel();

            $baseTranslations =  $langModel->where('language_id', $baseLanguageId)->get();
            $modelKey = \Str::snake($modelName) . '_id';

            foreach ($baseTranslations as $translation) {
                $oldTranslation = $langModel::where('language_id', $languageId)
                    ->where($modelKey, $translation->$modelKey)
                    ->first();
                if (!empty($oldTranslation)) {
                    $oldTranslation->delete();
                }
                $newTranslation = new $langModel($translation->toArray());
                $newTranslation->translation_id = null;
                $newTranslation->language_id = $languageId;
                $newTranslation->save();
            }

            // clones multilanguage multiplepicker relations
            foreach ($contentType->getFields()->getMultiLanguageFields() as $field) {
                if ($field instanceof MultiplePickerField && $field->isMultiLanguage()) {
                    $fieldName = $field->getName();
                    $pivotTable = $model->$fieldName()->getTable();
                    $fkColumn = $model->$fieldName()->getQualifiedForeignPivotKeyName();
                    $relatedIdColumn = $model->$fieldName()->getQualifiedRelatedPivotKeyName();
                    DB::statement("INSERT INTO `$pivotTable` (SELECT $fkColumn, $relatedIdColumn, `position`, '$languageId' AS language_id FROM `$pivotTable` WHERE `language_id` = '$baseLanguageId')");
                }
            }
        }
    }

    public function createOrUpdate(Array $languageData, Website $website, $languageId = null): Language
    {
        DB::beginTransaction();
        try {
            $cloneTranslations = false;

            if ($languageId) {
                $language = $website->languages()->find($languageId);
            } else {
                $language = new Language();
                $language->id = $languageData['id'];
                $languageId = $language->id;
                $cloneTranslations = true;
            }

            $language->name = $languageData['name'];
            $language->native_name = $languageData['native_name'];
            $language->translation_key = urlize($languageData['name']);

            $language->save();

            $generateTranslationData = null;
            if ($cloneTranslations) {
                $baseLanguageId = !empty($languageData['base_language_id']) ? $languageData['base_language_id'] : Localization::getDefaultLocale();
                $generateTranslationData = json_encode([
                    'targetLanguageId' => $language->id,
                    'baseLanguageId' => $baseLanguageId,
                    'initialStatus' => $languageData['initial_status']
                ]);

                $language->will_clone = true;
            } elseif ($language->pivot->generate_translation) {
                $generateTranslationData = $language->pivot->generate_translation;
            }

            $pivotValues = ['position' => $languageData['position'], 'is_active' => !empty($languageData['is_active']), 'generate_translation' => $generateTranslationData];
            $website->languages()->detach($language->id);
            $website->languages()->attach($language->id, $pivotValues);

            DB::commit();
        } catch (Throwable $e) {
            DB::rollback();
            throw $e;
        }

        return $language;
    }

    public function delete(string $languageId): void
    {
        DB::beginTransaction();
        try {
            $language = Language::find($languageId);

            $this->deleteMenuTranslations($languageId);
            $this->deleteContentTranslations($languageId);
            $this->deleteWOTranslations($languageId);

            $language->delete();

            DB::commit();
        } catch (Throwable $e) {
            DB::rollback();
            throw $e;
        }
    }

    public function deleteContentTranslations(string $languageId): void
    {
        $contentTypes = ContentTypes::all();

        foreach ($contentTypes as $contentType) {
            $modelName = $contentType->getModelType();

            $model = new $modelName();
            $langModel = $model->languages()->getModel();

            $langModel::where('language_id', $languageId)->delete();

            // deletes multilanguage multiple picker relations for the deleted language
            foreach ($contentType->getFields()->getMultiLanguageFields() as $field) {
                if ($field instanceof MultiplePickerField && $field->isMultiLanguage()) {
                    $fieldName = $field->getName();
                    $pivotTable = $model->$fieldName()->getTable();
                    DB::statement("DELETE FROM `$pivotTable` WHERE `language_id` = '$languageId'");
                }
            }
        }

        WebsiteObjectSearch::where('language_id', $languageId)->delete();
    }

    public function deleteMenuTranslations(string $languageId)
    {
        MenuItemLanguage::where('language_id', $languageId)->delete();
    }

    public function deleteWOTranslations(string $languageId): void
    {
        WebsiteObjectLanguage::where('wo_language_id', $languageId)
            ->delete();
    }

    public function saveStaticTranslation(string $translationId, string $languageId, string $translation)
    {
        $line = LanguageLine::where('group', 'frontend')
            ->where('key', $translationId)
            ->first();

        if (!$line && $translation) {
            $line = LanguageLine::create([
                'group' => 'frontend',
                'key' => $translationId,
                'text' => [$languageId => $translation],
            ]);
        } else if ($line) {
            $line->flushGroupCache();
            $translations = $line->text;
            if ($translation) {
                $translations[$languageId] = $translation;
            } else if(array_key_exists($languageId, $translations)) {
                unset($translations[$languageId]);
            }
            if (empty($translations)) {
                $line->delete();
                return null;
            } else {
                $line->text = $translations;
                $line->save();
            }
        }

        return $line;
    }

    public function copyStaticTranslations($targetLanguageId, $baseLanguageId)
    {
        $baseTranslations = trans('frontend', [], $baseLanguageId);
        $defaultTranslations = trans('frontend', [], $this->defaultLang);
        foreach ($defaultTranslations as $translationId => $defaultValue) {
            if (empty($baseTranslations[$translationId])) {
                continue;
            }
            $this->saveStaticTranslation($translationId, $targetLanguageId, $baseTranslations[$translationId]);
        }
    }

    public function importTranslations($targetLanguageId, $translations)
    {
        $defaultTranslations = trans('frontend', [], $this->defaultLang);
        foreach ($defaultTranslations as $translationId => $defaultValue) {
            if (empty($translations[$translationId])) {
                continue;
            }
            $this->saveStaticTranslation($translationId, $targetLanguageId, $translations[$translationId]);
        }
    }
}
