<?php

namespace App;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphTo;
use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Support\Facades\App;
use Stancl\Tenancy\Database\Concerns\CentralConnection;

class TaxonomyMap extends Model
{
    use CentralConnection;
    use HasFactory;

    public const MAKE = 'make';
    public const MODEL = 'model';
    public const BODY_STYLE = 'body_style';
    public const TRANSMISSION = 'transmission';
    public const FUEL_TYPE = 'fuel_type';
    public const DRIVETRAIN = 'drivetrain';

    protected $table = 'taxonomy_map';

    protected $fillable = [
        'taxonomy_type',
        'taxonomy_id',
        'term',
        'provider',
        'parent_id',
        'details',
    ];

    protected $casts = [
        'details' => 'array',
    ];

    public static function getTaxonomy(
        string $provider,
        string $type,
        string $term,
        array $details = [],
        ?int $make_id = null
    ): Model {
        if (!str_contains($type, 'master-')) {
            $type = 'master-' . $type;
        }

        if (in_array($type, ['model', 'master-model'])) {
            return self::query()
                ->firstOrCreate([
                    'provider' => $provider,
                    'taxonomy_type' => $type,
                    'term' => $term,
                    'parent_id' => $make_id,
                ], [
                    'details' => $details
                ]);
        }
        return self::query()
            ->firstOrCreate([
                'provider' => $provider,
                'taxonomy_type' => $type,
                'term' => $term,
            ], [
                'details' => $details
            ]);
    }

    /**
     * Relationship with morphed taxonomy
     *
     * @return MorphTo
     */
    public function taxonomy(): MorphTo
    {
        return $this->morphTo('taxonomy');
    }

    /**
     * Search and find a taxonomy id for term
     *
     * @param string $provider
     * @param string $type
     * @param string $term
     * @param array $details
     * @return int|null
     */
    public static function search(
        string $provider,
        string $type,
        string $term,
        array $details = [],
        ?int $make_id = null
    ): ?int {
        if (empty($term)) {
            return null;
        }
        if (strpos($type, 'master-') === false) {
            $type = 'master-' . $type;
        }

        /** @var self $taxonomy */
        $taxonomy = self::getTaxonomy($provider, $type, $term, $details, $make_id);

        if ($taxonomy->taxonomy_id) {
            return $taxonomy->taxonomy_id;
        }

        $model = Relation::getMorphedModel($type);
        if ($model) {
            $entry = App::make($model)->newQuery()
                ->when(
                    in_array($type, ['master-model', 'model']) && $make_id,
                    fn($query) => $query->where('make_id', $make_id)
                )
                ->where('name', $term)
                ->first();
            if ($entry) {
                $taxonomy->update([
                    'taxonomy_id' => $entry->id,
                ]);
            } else {
                // We couldn't find the taxonomy term for an existing model (make, fuel etc).
                // Check if we have this taxonomy term mapped for a different provider.
                $mapped_taxonomy = self::query()
                    ->whereNotNull('taxonomy_id')
                    ->where('provider', '!=', $provider)
                    ->where('taxonomy_type', '=', $type)
                    ->where('term', '=', $term)
                    ->when(
                        in_array($type, ['model', 'master-model']),
                        fn($query) => $query->where('parent_id', $make_id)
                    )
                    ->first();

                if ($mapped_taxonomy) {
                    $taxonomy->taxonomy_id = $mapped_taxonomy->taxonomy_id;
                    $taxonomy->save();

                    return $taxonomy->taxonomy_id;
                }
            }
        }

        return $taxonomy->taxonomy_id;
    }

    public function mapables()
    {
        return $this->hasMany(TaxonomyMapable::class);
    }
}
