<?php

namespace App\Http\Controllers;

use App\Jobs\SyncTaxonomyToTenants;
use App\Master\Models\BodyStyleType;
use App\Master\Models\DrivetrainType;
use App\Master\Models\FuelType;
use App\Master\Models\TransmissionType;
use App\Master\Models\VehicleMake;
use App\Reporting\TaxonomyCountTile;
use App\Reporting\UnmappedTaxonomyCountTile;
use App\TaxonomyMap;
use App\Traits\ResolvesTaxonomyMapping;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\App;
use App\Master\Models\VehicleModel;

class TaxonomyController extends Controller
{
    use ResolvesTaxonomyMapping;

    public function __construct()
    {
        $this->middleware([
            'permission:manage-taxonomies',
        ]);
    }

    /**
     * List dashboard
     *
     * @return array[]
     */
    public function index()
    {
        return [
            'tiles' => [
                (new UnmappedTaxonomyCountTile())->toArray(),
                (new TaxonomyCountTile('make'))->toArray(),
                (new TaxonomyCountTile('model'))->toArray(),
                (new TaxonomyCountTile('fuel_type'))->toArray(),
                (new TaxonomyCountTile('body_type'))->toArray(),
                (new TaxonomyCountTile('transmission'))->toArray(),
                (new TaxonomyCountTile('drivetrain'))->toArray(),
            ],
        ];
    }

    public function providers()
    {
        return TaxonomyMap::query()
            ->distinct()
            ->whereNull('taxonomy_id')
            ->pluck('provider')
            ->map(fn($entry) => [
                'name' => $entry,
                'value' => $entry,
            ])
            ->prepend([
                'name' => 'All',
                'value' => ''
            ]);
    }

    /**
     * List unmapped taxonomies from 3rd parties
     *
     * @param Request $request
     * @return array
     */
    public function unmapped(Request $request): array
    {
        $activeFilter = str_replace('-', '_', $request->input('filters', ''));
        $taxonomies = TaxonomyMap::query()
            ->whereNull('taxonomy_id')
            ->when($request->filled('filters'), fn($query) => $query->where('taxonomy_type', 'master-' . $activeFilter))
            ->when($request->filled('provider'), fn($query) => $query->where('provider', $request->input('provider')))
            ->orderBy('term')
            ->paginate()
            ->through(fn(TaxonomyMap $taxonomyMap) => [
                'id' => $taxonomyMap->id,
                'name' => $taxonomyMap->term,
                'taxonomy_id' => null,
                'updated' => false,
                'type' => $taxonomyMap->taxonomy_type,
                'details' => $taxonomyMap->details,
                'metaItems' => [
                    __('taxonomies.' . $taxonomyMap->taxonomy_type),
                    $taxonomyMap->provider,
                ]
            ])->toArray();

        return $this->getFilters($activeFilter, $taxonomies);
    }

    /**
     * List mapped taxonomies from 3rd parties
     *
     * @param Request $request
     * @return array
     */
    public function mapped(Request $request): array
    {
        $activeFilter = str_replace('-', '_', $request->input('filters', ''));
        $taxonomies = TaxonomyMap::query()
            ->whereNotNull('taxonomy_id')
            ->with('taxonomy')
            ->when($request->filled('filters'), fn($query) => $query->where('taxonomy_type', 'master-' . $activeFilter))
            ->when($request->filled('provider'), fn($query) => $query->where('provider', $request->input('provider')))
            ->orderBy('term')
            ->paginate()
            ->through(fn(TaxonomyMap $taxonomyMap) => [
                'id' => $taxonomyMap->id,
                'name' => $taxonomyMap->term,
                'mapped' => $taxonomyMap->taxonomy?->name,
                'type' => $taxonomyMap->taxonomy_type,
                'cleared' => false,
                'metaItems' => [
                    __('taxonomies.' . $taxonomyMap->taxonomy_type),
                    $taxonomyMap->provider,
                ]
            ])->toArray();

        return $this->getFilters($activeFilter, $taxonomies);
    }

    /**
     * View taxonomies of a type
     *
     * @param Request $request
     * @param string $type
     * @return mixed
     */
    public function show(Request $request, string $type)
    {
        if ($type === 'model') {
            return $this->viewModels($request);
        }

        return App::make($this->getMorphedModel($type))
            ->newQuery()
            ->when(
                $request->filled('search'),
                fn($query) => $query->where('name', 'like', '%' . $request->input('search') . '%')
            )
            ->setSortBy($request->input('order_by', 'name'))
            ->paginate(50);
    }

    /**
     * View taxonomy entry of specific type
     *
     * @param Request $request
     * @param string $type
     * @param int $id
     * @return mixed
     */
    public function showEntry(Request $request, string $type, int $id)
    {
        $record = App::make($this->getMorphedModel($type))
            ->newQuery()
            ->findOrFail($id);

        if ($request->filled('include')) {
            $record->extra = collect(explode(',', $request->input('include')))
                ->keyBy(fn($extra) => $extra)
                ->filter()
                ->map(fn($extra) => App::make($this->getMorphedModel($extra))
                    ->newQuery()
                    ->select(['id', 'name'])
                    ->get());
        }
        return $record;
    }

    /**
     * Update taxonomy
     *
     * @param Request $request
     * @param string $type
     * @param int $id
     * @return Response
     */
    public function updateEntry(Request $request, string $type, int $id)
    {
        $record = App::make($this->getMorphedModel($type))
            ->newQuery()
            ->findOrFail($id);
        if ($type == 'model' && !empty($record->autotrader_id)) {
            $record->do_not_sync = true;
        }
        $record->update($request->input());
        $this->dispatch(new SyncTaxonomyToTenants($type, $record));
        return response('ok');
    }

    /**
     * Store a new taxonomy
     *
     * @param Request $request
     * @param string $type
     * @return mixed
     */
    public function store(Request $request, string $type)
    {
        return App::make($this->getMorphedModel($type))
            ->newQuery()
            ->create([
                'make_id' => 0, // For Models
                'name' => $request->input('name')
            ]);
    }

    /**
     * Create a new taxonomy from unmapped taxonomy
     *
     * @param Request $request
     * @return mixed
     */
    public function storeUnmapped(Request $request)
    {
        $map = TaxonomyMap::query()->findOrFail($request->input('id'));
        $model = App::make($this->getMorphedModel($map->taxonomy_type))
            ->newQuery()
            ->create([
                'make_id' => 0, // For Models
                'name' => $map->term,
            ]);

        $map->update([
            'taxonomy_id' => $model->id,
        ]);
        return $model;
    }

    /**
     * Clear mapped taxonomy to re-map with other value
     *
     * @param Request $request
     * @return TaxonomyMap
     */
    public function clearMapped(TaxonomyMap $taxonomyMap): TaxonomyMap
    {
        $taxonomyMap->update([
            'taxonomy_id' => null,
        ]);

        return $taxonomyMap;
    }

    /**
     * Update taxonomy map with taxonomy id (map unmapped taxonomy)
     *
     * @param Request $request
     * @param TaxonomyMap $map
     * @return Response
     */
    public function update(Request $request, TaxonomyMap $map): Response
    {
        $map->update([
            'taxonomy_id' => $request->input('taxonomy_id'),
        ]);

        $map->mapables()
            ->get()
            ->groupBy('tenant')
            ->each(fn($mapables, $tenant) => $this->resolveTaxonomyMapping($mapables, $tenant, $map));

        return response([
            'status' => 'ok',
        ]);
    }

    public function destroy(Request $request, string $type, int $id)
    {
        $record = App::make($this->getMorphedModel($type))
            ->newQuery()
            ->findOrFail($id);
        $record->delete();
        return $this->show($request, $type);
    }

    /**
     * View Vehicle model section
     *
     * @param Request $request
     * @return mixed
     */
    private function viewModels(Request $request)
    {
        return VehicleModel::query()
            ->with('make')
            ->when(
                $request->filled('search'),
                fn($query) => $query->where('name', 'like', '%' . $request->input('search') . '%')
            )
            ->setSortBy($request->input('order_by', 'name'))
            ->paginate(50)
            ->through(fn(VehicleModel $model) => [
                'id' => $model->id,
                'name' => $model->name,
                'metaItems' => [
                    $model->make?->name,
                ],
            ]);
    }

    private function getMorphedModel(string $type): string
    {
        $map = [
            'make' => VehicleMake::class,
            'model' => VehicleModel::class,
            'fuel_type' => FuelType::class,
            'transmission' => TransmissionType::class,
            'body_type' => BodyStyleType::class,
            'drivetrain' => DrivetrainType::class,
        ];

        return $map[str_replace('-', '_', $type)];
    }
}
