<?php

namespace App;

use App\Events\ModelMediaUsesAssigned;
use App\Facades\Settings;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Config;
use Mtc\ContentManager\Contracts\Media;
use Mtc\ContentManager\Contracts\MediaFolder;
use Mtc\ContentManager\Contracts\MediaTag;
use Mtc\ContentManager\Contracts\MediaUse;
use Mtc\ContentManager\Http\Requests\MediaUploadRequest;
use Mtc\ContentManager\ImageSize;
use Mtc\ContentManager\MediaRepository as BaseMediaRepository;
use Mtc\ContentManager\Models\MediaSize;
use Mtc\MercuryDataModels\ApiUsage;
use Mtc\MercuryDataModels\Contracts\HasMediaChecksums;

class MediaRepository extends BaseMediaRepository
{
    public function __construct(Media $model, MediaUse $mediaUse, MediaTag $tag, MediaFolder $folder)
    {
        parent::__construct($model, $mediaUse, $tag, $folder);
        $this->setStorage();
    }

    /**
     * List of media files
     *
     * @param Request $request
     * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator
     */
    public function getList(Request $request): \Illuminate\Contracts\Pagination\LengthAwarePaginator
    {
        return $this->model->newQuery()
            ->when(
                !empty($request->has('name')),
                fn($query) => $query->where('title', 'like', '%' . $request->input('name') . '%')
            )
            ->when(
                !empty($request->has('mediaTypes')),
                fn($query) => $query->where('type', $request->input('mediaTypes'))
            )
            ->when($request->has('model'), fn($query) => $query->where('parent_type', $request->input('model')))
            ->when($request->has('model_id'), fn($query) => $query->where('parent_id', $request->input('model_id')))
            ->when(!$request->input('include_vehicle_images') && !$request->has('folder'), fn($query) => $query
                ->whereDoesntHave('folder', fn($folderQuery) => $folderQuery->where('name', 'Vehicles')))
            ->when($request->input('exclude_external_images'), fn($query) => $query
                ->whereNull('image_provider'))
            ->when($request->has('folder'), function ($query) use ($request) {
                if ($request->input('folder') == 'uncategorised') {
                    $query->whereNull('folder_id');
                } else {
                    $query->where('folder_id', $request->input('folder'));
                }
            })
            ->when(
                $request->filled('term'),
                fn ($query) => $query->where(fn($search) => $search
                    ->where('title', 'like', '%' . $request->input('term') . '%')
                    ->orWhere('src', 'like', '%' . $request->input('term') . '%')
                    ->orWhere('caption', 'like', '%' . $request->input('term') . '%')
                    ->orWhere('alt_text', 'like', '%' . $request->input('term') . '%')
                    ->orWhere('description', 'like', '%' . $request->input('term') . '%'))
            )
            ->setSortBy($request->input('sort_by', 'id_desc'))
            ->setFilters($request->input('filters', []))
            ->paginate($request->input('per_page'));
    }

    /**
     * File storage prefix for multi-tenancy
     *
     * @return string
     */
    public function storagePrefix(): string
    {
        if (tenancy()->initialized && !Settings::get('filesystem-custom-s3-bucket-enabled')) {
            return tenant('id') . '/';
        }
        return parent::storagePrefix();
    }

    public function createSize(Media $media, ImageSize $size, bool $overwrite = false)
    {
        $this->setStorage();
        return parent::createSize($media, $size, $overwrite);
    }

    public function removeFile($path): bool
    {
        $this->setStorage();
        return parent::removeFile($path);
    }

    public function setUsesForModel(array $mediaIds, Model $model, array $meta = [], bool $removeOthers = true): void
    {
        parent::setUsesForModel($mediaIds, $model, $meta, $removeOthers);
        if ($model instanceof HasMediaChecksums) {
            $model[$model->imageChecksumAttribute()] = $this->calculateMediaChecksum($model);
            $model->save();
        }
    }

    public function upload(MediaUploadRequest $request, Media $mediaToUpdate = null): Media
    {
        if ($mediaToUpdate) {
            $usage = ApiUsage::query()->updateOrCreate([
                'tenant_id' => tenant('id'),
                'endpoint' => 're-upload media file',
                'method' => 'post',
                'time_window' => Carbon::now()->format('Y-m-d H')
            ]);
            $usage->increment('hits');
        }
        $this->setStorage();
        return parent::upload($request, $mediaToUpdate);
    }

    /**
     * Update media use order for model
     *
     * @param int[] $media
     * @param Model $model
     * @return void
     */
    public function setPrimaryUse(array $media, $primary, Model $model)
    {
        $data = collect($media)
            ->map(fn($id) => [
                'media_id' => $id,
                'primary' => $id == $primary,
                'owner_id' => $model->id,
                'owner_type' => $model->getMorphClass()
            ])
            ->toArray();
        $this->mediaUse->newQuery()
            ->upsert($data, ['media_id', 'owner_id', 'owner_type'], ['primary']);
    }

    public function getAllowedSizes(Model $model, array $meta): array
    {
        $additions = match ($model->getMorphClass()) {
            'new-car-content', 'new-car-version', 'offer-content', 'offer-version' => $this->sizes('content'),
            'vehicle-trim' => $this->sizes('new-car'),
            default => [],
        };
        return array_merge(parent::getAllowedSizes($model, $meta), $additions);
    }

    private function setStorage(): void
    {
        if (Settings::get('filesystem-custom-s3-bucket-enabled')) {
            $this->setCustomStorage();
        } else {
            Config::set('filesystems.default_media', Config::get('filesystems.default_media_original'));
        }
    }

    private function setCustomStorage(): void
    {
        $config = Config::get('filesystems.disks.' . Config::get('filesystems.default_media'));
        $config['key'] = Settings::get('filesystem-custom-s3-bucket-access-key-id');
        $config['secret'] = Settings::get('filesystem-custom-s3-bucket-access-key');
        $config['region'] = Settings::get('filesystem-custom-s3-bucket-region');
        $config['bucket'] = Settings::get('filesystem-custom-s3-bucket-bucket');
        $config['endpoint'] = Settings::get('filesystem-custom-s3-bucket-endpoint');
        $config['cdn_endpoint'] = Settings::get('filesystem-custom-s3-bucket-cnd-endpoint');
        Config::set('filesystems.disks.' . tenant('id'), $config);
        Config::set('filesystems.default_media', tenant('id'));
    }

    private function sizes($model): array
    {
        return MediaSize::query()
            ->where('model', $model)
            ->get()
            ->mapWithKeys(fn($size) => [
                $size->label => "{$size->width}x{$size->height}"
            ])
            ->toArray();
    }

    private function calculateMediaChecksum(HasMediaChecksums $model): string
    {
        $media = $model->mediaUses()->with('media')->get()->map(fn($entry) => $entry->media?->source_filename);
        $media->prepend($media->count());
        return dechex(crc32(json_encode($media->toArray())));
    }
}
