<?php

namespace App\Traits;

use App\TaxonomyMap;
use App\TaxonomyMapable;
use Illuminate\Support\Collection;

trait MapsTaxonomies
{
    private bool $mappingLoaded = false;
    private array $unmappedTaxonomy = [];

    /**
     * @return string
     */
    abstract protected function getProviderName(): string;

    /**
     * Get the array of details to be saved.
     * Can be overridden to define the array to be saved.
     *
     * @param array|null $record
     * @return array
     */
    protected function getDetailsForTaxonomyMap(?array $record): array
    {
        return [];
    }

    /**
     * @param string $taxonomy
     * @param $value
     * @param array $vehicle_data
     * @param ?int $make_id
     * @return mixed
     */
    private function getMappedTaxonomy(string $taxonomy, $value, array $vehicle_data = null, ?int $make_id = null)
    {
        if (empty(trim($value))) {
            return null;
        }

        if (!$this->mappingLoaded) {
            $this->eagerLoadTaxonomyMapping();
        }

        if ($taxonomy === 'model') {
            return $this->getModelMappedTaxonomy($taxonomy, $value, $vehicle_data, $make_id);
        }

        if (!isset($this->{$taxonomy}[$value])) {
            $this->{$taxonomy}[$value] = TaxonomyMap::search(
                $this->getProviderName(),
                $taxonomy,
                $value,
                $this->getDetailsForTaxonomyMap($vehicle_data),
            );
        }

        if (!$this->{$taxonomy}[$value]) {
            $this->unmappedTaxonomy[] = TaxonomyMap::getTaxonomy(
                $this->getProviderName(),
                $taxonomy,
                $value,
                $this->getDetailsForTaxonomyMap($vehicle_data),
            );
        }

        return $this->{$taxonomy}[$value];
    }

    private function getModelMappedTaxonomy(string $taxonomy, $value, array $vehicle_data = null, ?int $make_id = null)
    {
        if (empty($make_id)) {
            $make_id = 0;
        }
        if (!isset($this->{$taxonomy}[$make_id][$value])) {
            $this->{$taxonomy}[$make_id][$value] = TaxonomyMap::search(
                $this->getProviderName(),
                $taxonomy,
                $value,
                $this->getDetailsForTaxonomyMap($vehicle_data),
                $make_id
            );
        }
        if (!$this->{$taxonomy}[$make_id][$value]) {
            $this->unmappedTaxonomy[] = TaxonomyMap::getTaxonomy(
                $this->getProviderName(),
                $taxonomy,
                $value,
                $this->getDetailsForTaxonomyMap($vehicle_data),
                $make_id
            );
        }
        return $this->{$taxonomy}[$make_id][$value];
    }

    private function eagerLoadTaxonomyMapping()
    {
        TaxonomyMap::query()
            ->where('provider', $this->getProviderName())
            ->whereNotIn('taxonomy_type', ['model', 'master-model'])
            ->get()
            ->groupBy('taxonomy_type')
            ->each(function (Collection $group, $taxonomy) {
                $this->{$taxonomy} = $group->keyBy('term')
                    ->map(fn($entry) => $entry->taxonomy_id)
                    ->toArray();
            });

        $this->mappingLoaded = true;
    }

    public function storeUnmappedTaxonomy($vehicle): void
    {
        foreach ($this->unmappedTaxonomy as $unmappedTaxonomy) {
            $existingRecord = TaxonomyMapable::where('taxonomy_map_id', $unmappedTaxonomy->id)
                ->where('mappable_type', $vehicle->getMorphClass())
                ->where('mappable_id', $vehicle->id)
                ->where('tenant', tenant('id'))
                ->first();
            if (!$existingRecord) {
                TaxonomyMapable::create([
                    'tenant' => tenant('id'),
                    'taxonomy_map_id' => $unmappedTaxonomy->id,
                    'mappable_type' => $vehicle->getMorphClass(),
                    'mappable_id' => $vehicle->id,
                ]);
            }
        }

        $this->unmappedTaxonomy = [];
    }
}
