<?php

namespace App\Http\Livewire\MainPage;

use App\Helpers\StringHelper;
use App\Http\Livewire\MainPage;
use App\Jobs\SendEmailNewTag;
use App\Models\Category;
use App\Models\Language;
use App\Models\Tag;
use App\Models\TaggableLocale;
use App\Traits\TaggableWithLocale;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\Rule;
use Livewire\Component;
use Throwable;
use WireUi\Traits\WireUiActions;

class SkillsCardFull extends Component
{
    use TaggableWithLocale;
    use WireUiActions;

    public $label;
    public $tagsArray = [];
    public bool $tagsArrayChanged = false;
    public bool $saveDisabled = false;
    public $initTagIds = [];
    public $initTagsArray = [];
    public $initTagsArrayTranslated = [];
    public $newTagsArray;
    public $suggestions = [];

    public $modalVisible = false;

    public $newTag = [];
    public $newTagCategory;
    public $categoryOptions = [];
    public $categoryColor = 'gray';

    public bool $translationPossible = true;
    public bool $translationAllowed = true;
    public bool $translationVisible = false;
    public $translationLanguages = [];
    public $selectTranslationLanguage;
    public $translationOptions = [];
    public $selectTagTranslation;
    public $inputTagTranslation = [];
    public bool $inputDisabled = true;
    public $translateRadioButton = null;

    public bool $sessionLanguageOk = false;
    public bool $sessionLanguageIgnored = false;
    public bool $transLanguageOk = false;
    public bool $transLanguageIgnored = false;

    protected $langDetector = null;
    protected $listeners = [
        'save',
        'cancelCreateTag',
        'refreshComponent' => '$refresh',
        'tagifyFocus',
        'tagifyBlur',
        'saveCard'=> 'save',
        ];

    protected function rules()
    {
        return [
            'newTagsArray' => 'array',
            'newTag' => 'array',
            'newTag.name' => Rule::when(
                function ($input) {
                    // Check if newTag is not an empty array
                    return count($input['newTag']) > 0;
                },
                array_merge(
                    timebank_config('tags.name_rule'),
                    timebank_config('tags.exists_in_current_locale_rule'),
                    [
                        'sometimes',
                        function ($attribute, $value, $fail) {
                            if (!$this->sessionLanguageOk && !$this->sessionLanguageIgnored) {
                                $locale = app()->getLocale();
                                $localeName = \Locale::getDisplayName($locale, $locale);
                                $fail(
                                    __('Is this :locale? Please confirm here below', [
                                        'locale' => $localeName
                                    ])
                                );
                            }
                        },
                    ]
                ),
            ),
            'newTagCategory' => Rule::when(
                function ($input) {
                    if (count($input['newTag']) > 0 && $this->translationVisible === true && $this->translateRadioButton == 'input') {
                        return true;
                    }

                    if (count($input['newTag']) > 0 && $this->translationVisible === false) {
                        return true;
                    }
                },
                ['required', 'int'],
            ),
            'selectTagTranslation' => Rule::when(
                function ($input) {
                    // Check if existing tag translation is selected
                    return $this->translationVisible === true && $this->translateRadioButton == 'select';
                },
                ['required', 'int'],
            ),
            'inputTagTranslation' => 'array',
            'inputTagTranslation.name' => Rule::when(
                fn ($input) => $this->translationVisible === true && $this->translateRadioButton === 'input',
                array_merge(
                    timebank_config('tags.name_rule'),
                    timebank_config('tags.exists_in_current_locale_rule'),
                    [
                        'sometimes',
                        function ($attribute, $value, $fail) {
                            if (!$this->transLanguageOk && !$this->transLanguageIgnored) {
                                $baseLocale = $this->selectTranslationLanguage;
                                $locale = \Locale::getDisplayName($baseLocale, $baseLocale);
                                $fail(__('Is this :locale? Please confirm here below', [
                                    'locale' => $locale
                                ]));
                            }
                        },
                        function ($attribute, $value, $fail) {
                            $existsInTransLationLanguage = DB::table('taggable_tags')
                                ->join('taggable_locales', 'taggable_tags.tag_id', '=', 'taggable_locales.taggable_tag_id')
                                ->where('taggable_locales.locale', $this->selectTranslationLanguage)
                                ->where(function ($query) use ($value) {
                                    $query->where('taggable_tags.name', $value)
                                        ->orWhere('taggable_tags.normalized', $value);
                                })
                                ->exists();

                            if ($existsInTransLationLanguage) {
                                $fail(__('This tag already exists.'));
                            }
                        },
                    ]
                ),
                []
            ),
        ];
    }

    public function mount($label = null)
    {
        if ($label === null) {
            $label = __('Activities or skills you offer to other ' . platform_users());
        }
        $this->label = $label;

        $owner = getActiveProfile();

        if (!$owner) {
            abort(403, 'No active profile');
        }

        // CRITICAL SECURITY: Validate user has ownership/access to this profile
        \App\Helpers\ProfileAuthorizationHelper::authorize($owner);

        $owner->cleanTaggables();

        $this->checkTranslationAllowed();
        $this->checkTranslationPossible();

        $this->getSuggestions();
        $this->getInitialTags();
        $this->getLanguageDetector();
        $this->dispatch('load');
    }



    protected function getSuggestions()
    {
        $suggestions = (new Tag())->localTagArray(app()->getLocale());

        $this->suggestions = collect($suggestions)->map(function ($value) {
            return app()->getLocale() == 'de' ? $value : StringHelper::DutchTitleCase($value);
        });
    }


    protected function getInitialTags()
    {
        $this->initTagIds = getActiveProfile()->tags()->get()->pluck('tag_id');

        $this->initTagsArray = TaggableLocale::whereIn('taggable_tag_id', $this->initTagIds)
            ->select('taggable_tag_id', 'locale', 'updated_by_user')
            ->get()
            ->toArray();

        $translatedTags = collect((new Tag())->translateTagIdsWithContexts($this->initTagIds));

        $tags = $translatedTags->map(function ($item, $key) {
            return [
                'original_tag_id' => $item['original_tag_id'],
                'tag_id' => $item['tag_id'],
                'value' => $item['locale'] == App::getLocale() ? $item['tag'] : $item['tag'] . ' (' . strtoupper($item['locale']) . ')',
                'readonly' => false, // Tags are never readonly so the remove button is always visible
                'class' => $item['locale'] == App::getLocale() ? '' : 'tag-foreign-locale', // Mark foreign-locale tags with a class for diagonal stripe styling
                'locale' => $item['locale'],
                'category' => $item['category'],
                'category_path' => $item['category_path'],
                'category_color' => $item['category_color'],
                'title' => $item['category_path'], // 'title' is used by Tagify script for text that shows on hover
                'style' =>
                    '--tag-bg:' .
                    tailwindColorToHex($item['category_color'] . '-400') .
                    '; --tag-text-color:#000' . // #111827 is gray-900
                    '; --tag-hover:' .
                    tailwindColorToHex($item['category_color'] . '-200'), // 'style' is used by Tagify script for background color, tailwindColorToHex is a helper function in app/Helpers/StyleHelper.php
            ];
        });

        $tags = $tags->sortBy('category_color')->values();
        $this->initTagsArrayTranslated = $tags->toArray();
        $this->tagsArray = json_encode($tags->toArray());
    }


    public function checkSessionLanguage()
    {
        // Ensure the language detector is initialized
        $this->getLanguageDetector();

        $detectedLanguage = $this->langDetector->detectSimple($this->newTag['name']);
        if ($detectedLanguage === session('locale')) {
            $this->sessionLanguageOk = true;
            // No need to ignore language detection when session locale is detected
            $this->sessionLanguageIgnored = false;
        } else {
            $this->sessionLanguageOk = false;
        }

        $this->validateOnly('newTag.name');
    }


    public function checkTransLanguage()
    {
        // Ensure the language detector is initialized
        $this->getLanguageDetector();
        $detectedLanguage = $this->langDetector->detectSimple($this->inputTagTranslation['name']);

        if ($detectedLanguage === $this->selectTranslationLanguage) {
            $this->transLanguageOk = true;
            // No need to ignore language detection when base locale is detected
            $this->transLanguageIgnored = false;
        } else {
            $this->transLanguageOk = false;
        }
        $this->validateOnly('inputTagTranslation.name');
    }


    public function checkTranslationAllowed()
    {
        // Check if translations are allowed based on config and profile type
        $allowTranslations = timebank_config('tags.allow_tag_transations_for_non_admins', false);
        $profileType = getActiveProfileType();

        // If config is false, only admins can add translations
        if (!$allowTranslations) {
            $this->translationAllowed = ($profileType === 'admin');
        } else {
            // If config is true, all profile types can add translations
            $this->translationAllowed = true;
        }
    }

    public function checkTranslationPossible()
    {
        // Check if profile is capable to do any translations
        $countNonBaseLanguages = getActiveProfile()->languages()->where('lang_code', '!=', timebank_config('base_language'))->count();
        if ($countNonBaseLanguages === 0 && app()->getLocale() === timebank_config('base_language')) {
            $this->translationPossible = false;
        }
    }

    protected function getLanguageDetector()
    {
        if (!$this->langDetector) {
            $this->langDetector = new \Text_LanguageDetect();
            $this->langDetector->setNameMode(2); // iso language code with 2 characters
        }
        return $this->langDetector;
    }


    public function updatedNewTagName()
    {
        $this->resetErrorBag('newTag.name');

        // Check if name is the profiles's session's locale
        $this->checkSessionLanguage();
        // Only fall back to initTagsArray if newTagsArray has not been set yet,
        // to preserve any tags the user already added before opening the create modal
        if ($this->newTagsArray === null) {
            $this->newTagsArray = collect($this->initTagsArray);
        }
    }

    public function updatedSessionLanguageIgnored()
    {
        if (!$this->sessionLanguageIgnored) {
            $this->checkSessionLanguage();
        }

        // Revalidate the newTag.name field
        $this->validateOnly('newTag.name');
    }


    public function updatedTransLanguageIgnored()
    {
        if (!$this->transLanguageIgnored) {
            $this->checkTransLanguage();
        } else {
            $this->resetErrorBag('inputTagTranslation.name');
        }
    }


    public function updatedSelectTranslationLanguage()
    {
        $this->selectTagTranslation = [];
        // Suggest related tags in the selected translation language
        $this->translationOptions = $this->getTranslationOptions($this->selectTranslationLanguage);
    }


    public function updatedNewTagCategory()
    {
        $this->categoryColor = collect($this->categoryOptions)
            ->firstWhere('category_id', $this->newTagCategory)['color'] ?? 'gray';
        $this->selectTagTranslation = [];
        // Suggest related tags in the selected translation language
        $this->translationOptions = $this->getTranslationOptions($this->selectTranslationLanguage);
        $this->resetErrorBag('inputTagTranslationCategory');
    }


    public function updatedInputTagTranslationName()
    {
        $this->validateOnly('inputTagTranslation.name');
    }


    public function updatedTagsArray()
    {
        // Prevent save during updating cycle of the tagsArray
        $this->saveDisabled = true;
        $this->newTagsArray = collect(json_decode($this->tagsArray, true));

        $localesToCheck = [app()->getLocale(), '']; // Only current locale and tags without locale should be checked for any new tag keywords
        $newTagsArrayLocal = $this->newTagsArray->whereIn('locale', $localesToCheck);
        // map suggestion to lower case for search normalization of the $newEntries
        $suggestions = collect($this->suggestions)->map(function ($value) {
            return strtolower($value);
        });
        // Retrieve new tag entries not present in suggestions
        $newEntries = $newTagsArrayLocal->filter(function ($newItem) use ($suggestions) {
            return !$suggestions->contains(strtolower($newItem['value']));
        });
        // Add a new tag modal if there are new entries
        if (count($newEntries) > 0) {

            $this->newTag['name'] = app()->getLocale() == 'de' ? $newEntries->flatten()->first() : ucfirst($newEntries->flatten()->first());
            $this->categoryOptions = Category::where('type', Tag::class)
                ->get()
                ->map(function ($category) {
                    // Include all attributes, including appended ones
                    return [
                        'category_id' => $category->id,
                        'name' => ucfirst($category->translation->name ?? ''), // Use the appended 'translation' attribute
                        'description' => $category->relatedPathExSelfTranslation ?? '', // Appended attribute
                        'color' => $category->relatedColor ?? 'gray',
                    ];
                })
                ->sortBy('name')
                ->values();

            // Open the create tag modal
            $this->modalVisible = true;

            // For proper validation, this needs to be done after the netTag.name input of the modal is visible
            $this->checkSessionLanguage();

        } else {
            $newEntries = false;

            // Enable save button as updating cycle tagsArray is finished by now
            $this->saveDisabled = false;
        }
        $this->checkChangesTagsArray();
    }


    public function checkChangesTagsArray()
    {
        // Check if tagsArray has been changed, to display 'unsaved changes' message next to save button
        $initTagIds = collect($this->initTagIds);
        $newTagIds = $this->newTagsArray->pluck('tag_id');
        $diffFromNew = $newTagIds->diff($initTagIds);
        $diffFromInit = $initTagIds->diff($newTagIds);

        if ($diffFromNew->isNotEmpty() || $diffFromInit->isNotEmpty()) {
            $this->tagsArrayChanged = true;
        } else {
            $this->tagsArrayChanged = false;
        }
    }


    // When tagify raises focus, disable the save button
    public function tagifyFocus()
    {
        $this->saveDisabled = true;
    }

    // When tagify looses focus, enable the save button
    public function tagifyBlur()
    {
        $this->saveDisabled = false;
    }



    public function renderedModalVisible()
    {
        // Enable save button as updating cycle tagsArray is finished by now
        $this->saveDisabled = false;
    }


    public function updatedTranslationVisible()
    {
        if ($this->translationVisible && $this->translationAllowed) {
            $this->updatedNewTagCategory();

            $profile = getActiveProfile();

            if (!$profile) {
                abort(403, 'No active profile');
            }

            // CRITICAL SECURITY: Validate user has ownership/access to this profile
            \App\Helpers\ProfileAuthorizationHelper::authorize($profile);

            // Get all languages of the profile with good competence
            $this->translationLanguages = $profile
                ->languages()
                ->wherePivot('competence', 1)
                ->where('lang_code', '!=', app()->getLocale())
                ->get()
                ->map(function ($language) {
                    $language->name = trans($language->name); // Map the name property to a translation key
                    return $language;
                });

            // Make sure that always the base language is included even if the profile does not has it as a competence 1
            if (!$this->translationLanguages->contains('lang_code', 'en')) {
                $transLanguage = Language::where('lang_code', timebank_config('base_language'))->first();
                if ($transLanguage) {
                    $transLanguage->name = trans($transLanguage->name); // Map the name property to a translation key
                    // Add the base language to the translationLanguages collection
                    $this->translationLanguages = collect($this->translationLanguages)
                        ->push($transLanguage);
                }

                // Set the default selection to base language
                if (app()->getLocale() != timebank_config('base_language')) {
                    $this->selectTranslationLanguage = timebank_config('base_language');
                }
            }
        }
    }



    public function updatedTranslateRadioButton()
    {
        if ($this->translateRadioButton === 'select') {
            $this->inputDisabled = true;
            $this->dispatch('disableInput');
        } elseif ($this->translateRadioButton === 'input') {
            $this->inputDisabled = false;
            // $this->dispatch('disableSelect'); // Script inside view skills-form.blade.php
        }
        $this->resetErrorBag('selectTagTranslation');
        $this->resetErrorBag('inputTagTranslation.name');
        $this->resetErrorBag('newTagCategory');
    }


    public function updatedSelectTagTranslation()
    {
        if ($this->selectTagTranslation) {
            $this->categoryColor = Tag::find($this->selectTagTranslation)->categories->first()->relatedColor ?? 'gray';
            $this->translateRadioButton = 'select';
            $this->dispatch('disableInput');
        }
    }


    public function updatedInputTagTranslation()
    {
        $this->translateRadioButton = 'input';
        $this->inputDisabled = false;
        // $this->dispatch('disableSelect'); // Script inside view skills-form.blade.php
        $this->checkTransLanguage();
    }


    /**
     * Updates the visibility of the modal. If the modal becomes invisible, dispatches the 'remove' event to remove the last value of the tags array on the front-end.
     */
    public function updatedModalVisible()
    {
        if ($this->modalVisible == false) {
            $this->dispatch('remove'); // Removes last value of the tagsArray on front-end only
            $this->dispatch('reinitializeComponent');
        }
    }


    /**
     * Retrieves a list of related tags based on the specified category and locale.
     * Get all translation options in the choosen locale,
     * but exclude all tags already have a similar context in the current $appLocal.
     *
     * @param int|null $category The ID of the category to filter related tags. If null, all tags in the locale are suggested.
     * @param string|null $locale The locale to use for tag names. If not provided, the application's current locale is used.
     *
     * @return \Illuminate\Support\Collection A collection of tags containing 'tag_id' and 'name' keys, sorted by name.
     */

    public function getTranslationOptions($locale)
    {
        $appLocale = app()->getLocale();

        // 1) Get all context_ids used by tags that match app()->getLocale()
        $contextIdsInAppLocale = DB::table('taggable_locale_context')
            ->whereIn('tag_id', function ($query) use ($appLocale) {
                $query->select('taggable_tag_id')
                    ->from('taggable_locales')
                    ->where('locale', $appLocale);
            })
            ->pluck('context_id');

        // 2) Exclude tags that share these context_ids
        $tags = Tag::with(['locale', 'contexts.category'])
            ->whereHas('locale', function ($query) use ($locale) {
                $query->where('locale', $locale);
            })
            ->whereNotIn('tag_id', function ($subquery) use ($contextIdsInAppLocale) {
                $subquery->select('tag_id')
                    ->from('taggable_locale_context')
                    ->whereIn('context_id', $contextIdsInAppLocale);
            })
            ->get();

        // 3) Build the options array. Adjust the name logic to your preference.
        $options = $tags->map(function ($tag) use ($locale) {
            $category = optional($tag->contexts->first())->category;
            $description = optional(optional($category)->translation)->name ?? '';

            return [
                'tag_id'      => $tag->tag_id,
                'name'        => $locale == 'de' ? $tag->name : StringHelper::DutchTitleCase($tag->normalized),
                'description' => $description,
            ];
        })->sortBy('name')->values();

        return $options;
    }


    /**
     * Cancels the creation of a new tag by resetting error messages,
     * clearing input fields, hiding translation and modal visibility,
     * and resetting tag arrays to their initial state.
     */
    public function cancelCreateTag()
    {
        $this->resetErrorBag();
        $this->newTag = [];
        $this->newTagCategory = null;
        $this->translationVisible = false;
        $this->translateRadioButton = null;
        $this->sessionLanguageOk = false;
        $this->sessionLanguageIgnored = false;
        $this->transLanguageOk = false;
        $this->transLanguageIgnored = false;
        $this->categoryColor = 'gray';
        $this->selectTagTranslation = null;
        $this->inputTagTranslation = [];
        $this->inputDisabled = true;
        // $this->newTagsArray = collect($this->initTagsArray);
        // $this->tagsArray = json_encode($this->initTagsArray);

        // Remove last value of the tagsArray
        $tagsArray = is_string($this->tagsArray) ? json_decode($this->tagsArray, true) : $this->tagsArray;
        array_pop($tagsArray);
        $this->tagsArray = json_encode($tagsArray);

        // Check of there were also other unsaved new tags in the tagsArray
        $hasNoTagId = false;
        if (is_array($tagsArray)) {
            $this->tagsArrayChanged = count(array_filter($tagsArray, function ($tag) {
                return !array_key_exists('tag_id', $tag) || $tag['tag_id'] === null;
            })) > 0;
        } else {
            $this->tagsArrayChanged = false;
        }

            $this->modalVisible = false;
        $this->updatedModalVisible();
    }



    /**
     * Handles the save button of the create tag modal.
     *
     * Creates a new tag for the currently active profile and optionally
     * associates it with a category or base-language translation.
     *
     * @return void
     */
    public function createTag()
    {
        // TODO: MAKE A TRANSACTION

        $this->validate();
        $this->resetErrorBag();

        // Format strings to correct case
        $this->newTag['name'] = app()->getLocale() == 'de' ? $this->newTag['name'] : StringHelper::DutchTitleCase($this->newTag['name']);

        $name = $this->newTag['name'];
        $normalized = call_user_func(config('taggable.normalizer'), $name);

        // Create the tag and attach the owner and context
        $tag = Tag::create([
            'name' => $name,
            'normalized' => $normalized,
            ]);
        $owner = getActiveProfile();

        if (!$owner) {
            abort(403, 'No active profile');
        }

        // CRITICAL SECURITY: Validate user has ownership/access to this profile
        \App\Helpers\ProfileAuthorizationHelper::authorize($owner);
        $owner->tagById($tag->tag_id);
        $context = [
            'category_id' => $this->newTagCategory,
            'updated_by_user' => Auth::guard('web')->user()->id,    // use the logged user, not the active profile
        ];

        if ($this->translationVisible) {
            if ($this->translateRadioButton === 'select') {
                // Attach an existing context in the base language to the new tag. See timebank_config('base_language')
                // Note that the category_id and updated_by_user is not updated when selecting an existing context
                $tagContext = Tag::find($this->selectTagTranslation)
                    ->contexts()
                    ->first();
                $tag->contexts()->attach($tagContext->id);
            } elseif ($this->translateRadioButton === 'input') {
                // Create a new context for the new tag
                $tagContext = $tag->contexts()->create($context);

                // Create a new translation of the tag
                $this->inputTagTranslation['name'] = $this->selectTranslationLanguage == 'de' ? $this->inputTagTranslation['name'] : StringHelper::DutchTitleCase($this->inputTagTranslation['name']);
                // $owner->tag($this->inputTagTranslation['name']);
                $nameTranslation = $this->inputTagTranslation['name'];
                $normalizedTranslation = call_user_func(config('taggable.normalizer'), $nameTranslation);
                $locale = ['locale' => $this->selectTranslationLanguage ];

                // Create the translation tag with the locale and attach the context
                $tagTranslation = Tag::create([
                            'name' => $nameTranslation,
                            'normalized' => $normalizedTranslation,
                            ]);
                $tagTranslation->locale()->create($locale);
                $tagTranslation->contexts()->attach($tagContext->id);

                // The translation now has been recorded. Next, detach owner from this translation as only the locale tag should be attached to the owner
                $owner->untagById([$tagTranslation->tag_id]);
                // Also clean up owner's tags that have similar context but have different locale. Only the tag in owner's app()->getLocale() should remain in db.
                $owner->cleanTaggables();   // In TaggableWithLocale trait

            }
        } else {
            // Create a new context for the new tag without translation
            $tagContext = $tag->contexts()->create($context);
        }

        $this->modalVisible = false;
        $this->saveDisabled = false;
        // Attach the new collection of tags to the active profile
        $this->save();
        $this->tagsArrayChanged = false;

        // Dispatch the SendEmailNewTag job
        SendEmailNewTag::dispatch($tag->tag_id);
    }


    /**
     * Saves the newTagsArray: attaches the current tags to the profile model.
     * Ignores the tags that are marked read-only (no app locale and no base language locale).
     * Dispatches notification on success or error.
     *
     * @return void
     */
    public function save()
    {
        if ($this->saveDisabled === false) {

            if ($this->newTagsArray) {
                try {
                    // Use a transaction for saving skill tags
                    DB::transaction(function () {
                        // Make sure we can count newTag for conditional validation rules
                        if ($this->newTag === null) {
                            $this->newTag = [];
                        }

                        $owner = getActiveProfile();

                        if (!$owner) {
                            abort(403, 'No active profile');
                        }

                        // CRITICAL SECURITY: Validate user has ownership/access to this profile
                        \App\Helpers\ProfileAuthorizationHelper::authorize($owner);

                        $this->validate();
                        // try {
                        //     $this->validate();
                        // } catch (ValidationException $e) {
                        //     // Dump all validation errors to the log or screen
                        //     logger()->error('Validation failed', $e->errors());
                        //     dd($e->errors()); // or use dump() if you prefer
                        // }
                        $this->resetErrorBag();

                        $initTags = collect($this->initTagsArray)->pluck('taggable_tag_id');

                        $newTagsArray = collect($this->newTagsArray);

                        $newTags = $newTagsArray
                            ->where('tag_id', null)
                            ->pluck('value')->toArray();
                        $owner->tag($newTags);

                        $remainingTags = $this->newTagsArray
                            ->where('tag_id')
                            ->pluck('tag_id')->toArray();

                        $removedTags = $initTags->diff($remainingTags)->toArray();
                        $owner->untagById($removedTags);

                        // Finaly clean up taggables table: remove duplicate contexts and any orphaned taggables
                        // In TaggableWithLocale trait
                        $owner->cleanTaggables();

                        $owner->touch(); // Observer catches this and reindexes search index

                        // WireUI notification
                        $this->notification()->success($title = __('Your have updated your profile successfully!'));
                    });
                    // end of transaction
                } catch (Throwable $e) {
                    // WireUI notification
                    // TODO!: create event to send error notification to admin
                    $this->notification([
                        'title' => __('Update failed!'),
                        'description' => __('Sorry, your data could not be saved!') . '<br /><br />' . __('Our team has ben notified about this error. Please try again later.') . '<br /><br />' . $e->getMessage(),
                        'icon' => 'error',
                        'timeout' => 100000,
                    ]);
                }

                $this->tagsArrayChanged = false;
                $this->dispatch('saved');
                $this->forgetCachedSkills();
                $this->cacheSkills();
                $this->initTagsArray = [];
                $this->newTag = null;
                $this->newTagsArray = null;
                $this->newTagCategory = null;
                $this->dispatch('refreshComponent');
                $this->dispatch('reinitializeTagify');
                $this->dispatch('reloadPage');
            }
        }
    }


    public function forgetCachedSkills()
    {
        // Get the profile type (user / organization) from the session and convert to lowercase
        $profileType = strtolower(basename(str_replace('\\', '/', session('activeProfileType'))));
        // Get the supported locales from the config
        $locales = config('app.supported_locales', [app()->getLocale()]);
        // Iterate over each locale and forget the cache
        foreach ($locales as $locale) {
            Cache::forget('skills-' . $profileType . '-' . session('activeProfileId') . '-lang-' . $locale);
        }
    }


    public function cacheSkills()
    {
        $profileType = strtolower(basename(str_replace('\\', '/', session('activeProfileType')))); // Get the profile type (user / organization) from the session and convert to lowercase

        $skillsCache = Cache::remember('skills-' . $profileType . '-' . session('activeProfileId') . '-lang-' . app()->getLocale(), now()->addDays(7), function () {
            // remember cache for 7 days
            $tagIds = session('activeProfileType')::find(session('activeProfileId'))->tags->pluck('tag_id');
            $translatedTags = collect((new Tag())->translateTagIdsWithContexts($tagIds, App::getLocale(), App::getFallbackLocale())); // Translate to app locale, if not available to fallback locale, if not available do not translate
            $skills = $translatedTags->map(function ($item, $key) {
                return [
                    'original_tag_id' => $item['original_tag_id'],
                    'tag_id' => $item['tag_id'],
                    'name' => $item['tag'],
                    'foreign' => $item['locale'] == App::getLocale() ? false : true, // Mark all tags in a foreign language read-only, as users need to switch locale to edit/update/etc foreign tags
                    'locale' => $item['locale'],
                    'category' => $item['category'],
                    'category_path' => $item['category_path'],
                    'category_color' => $item['category_color'],
                ];
            });
            $skills = collect($skills);

            return $skills;
        });

        $this->tagsArray = json_encode($skillsCache->toArray());
    }


    public function render()
    {
        return view('livewire.main-page.skills-card-full');
    }
}
