<?php

namespace App;

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\ApiUsageEndpoint;
use Mtc\MercuryDataModels\Contracts\HasMediaChecksums;
use Mtc\MercuryDataModels\Vehicle;
use Illuminate\Support\Facades\DB;

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
     */
    #[\Override]
    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
     */
    #[\Override]
    public function storagePrefix(): string
    {
        if (tenancy()->initialized && !Settings::get('filesystem-custom-s3-bucket-enabled')) {
            return tenant('id') . '/';
        }
        return parent::storagePrefix();
    }

    #[\Override]
    public function createSize(Media $media, ImageSize $size, bool $overwrite = false)
    {
        $this->setStorage();
        try {
            return parent::createSize($media, $size, $overwrite);
        } catch (\Intervention\Image\Exception\NotReadableException $e) {
            if ($overwrite) {
                throw $e;
            }
            return parent::createSize($media, $size, true);
        }
    }

    #[\Override]
    public function removeFile($path): bool
    {
        $this->setStorage();
        return parent::removeFile($path);
    }

    #[\Override]
    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();
        }
    }

    #[\Override]
    public function upload(MediaUploadRequest $request, ?Media $mediaToUpdate = null): Media
    {
        if ($mediaToUpdate) {
            $usage = ApiUsage::query()->updateOrCreate([
                'tenant_id' => tenant('id'),
                'endpoint_id' => ApiUsageEndpoint::resolveId('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']);
    }

    #[\Override]
    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())));
    }

    public function getStats(): array
    {
        return [
            'total_records' => $this->getTotalRecords(),
            'records_without_uses' => $this->getRecordsWithoutUses(),
            'uses_by_owner_type' => $this->getUsesByOwnerType(),
            'vehicle_breakdown' => $this->getVehicleBreakdown(),
        ];
    }

    private function getTotalRecords(): int
    {
        return $this->model->newQuery()->count();
    }

    private function getRecordsWithoutUses(): int
    {
        return $this->model->newQuery()
            ->whereDoesntHave('uses')
            ->count();
    }

    private function getUsesByOwnerType(): array
    {
        return $this->mediaUse->newQuery()
            ->select('owner_type', DB::raw('COUNT(*) as total_count'))
            ->groupBy('owner_type')
            ->orderByDesc('total_count')
            ->get()
            ->map(fn ($item) => [
                'owner_type' => __('labels.morph-models.' . $item->owner_type),
                'total_count' => $item->total_count,
            ])
            ->toArray();
    }

    private function getVehicleBreakdown(): array
    {
        $vehicleUses = $this->mediaUse->newQuery()
            ->where('owner_type', 'vehicle')
            ->select('owner_id')
            ->distinct()
            ->pluck('owner_id');

        $totalUses = $this->mediaUse->newQuery()
            ->where('owner_type', 'vehicle')
            ->count();

        $allVehiclesCount = Vehicle::query()
            ->withTrashed()
            ->whereIn('id', $vehicleUses)
            ->count();

        $publishedVehiclesCount = Vehicle::query()
            ->where('is_published', true)
            ->whereIn('id', $vehicleUses)
            ->count();

        $nonDeletedVehiclesCount = Vehicle::query()
            ->whereIn('id', $vehicleUses)
            ->count();

        $deletedOnlyCount = Vehicle::query()
            ->onlyTrashed()
            ->whereNull('purged_at')
            ->whereIn('id', $vehicleUses)
            ->count();

        $purgedCount = Vehicle::query()
            ->onlyTrashed()
            ->whereNotNull('purged_at')
            ->whereIn('id', $vehicleUses)
            ->count();

        $publishedVehicleUses = $this->mediaUse->newQuery()
            ->where('owner_type', 'vehicle')
            ->whereIn('owner_id', Vehicle::query()->where('is_published', true)->pluck('id'))
            ->count();

        $nonDeletedVehicleUses = $this->mediaUse->newQuery()
            ->where('owner_type', 'vehicle')
            ->whereIn('owner_id', Vehicle::query()->pluck('id'))
            ->count();

        $deletedOnlyVehicleUses = $this->mediaUse->newQuery()
            ->where('owner_type', 'vehicle')
            ->whereIn('owner_id', Vehicle::query()->onlyTrashed()->whereNull('purged_at')->pluck('id'))
            ->count();

        $purgedVehicleUses = $this->mediaUse->newQuery()
            ->where('owner_type', 'vehicle')
            ->whereIn('owner_id', Vehicle::query()->onlyTrashed()->whereNotNull('purged_at')->pluck('id'))
            ->count();

        $avgPerVehicle = $allVehiclesCount > 0 ? round($totalUses / $allVehiclesCount, 2) : 0;

        return [
            'summary' => [
                'total_uses' => $totalUses,
                'total_vehicles_with_media' => $allVehiclesCount,
                'average_per_vehicle' => $avgPerVehicle,
            ],
            'categories' => [
                [
                    'type' => 'all',
                    'label' => 'All Records (including deleted & purged)',
                    'total_uses' => $totalUses,
                    'vehicle_count' => $allVehiclesCount,
                    'avg_per_vehicle' => $avgPerVehicle,
                ],
                [
                    'type' => 'non_deleted',
                    'label' => 'Vehicles (not deleted)',
                    'total_uses' => $nonDeletedVehicleUses,
                    'vehicle_count' => $nonDeletedVehiclesCount,
                    'avg_per_vehicle' => $nonDeletedVehiclesCount > 0
                        ? round($nonDeletedVehicleUses / $nonDeletedVehiclesCount, 2)
                        : 0,
                ],
                [
                    'type' => 'published',
                    'label' => 'Published Vehicles',
                    'total_uses' => $publishedVehicleUses,
                    'vehicle_count' => $publishedVehiclesCount,
                    'avg_per_vehicle' => $publishedVehiclesCount > 0
                        ? round($publishedVehicleUses / $publishedVehiclesCount, 2)
                        : 0,
                ],
                [
                    'type' => 'deleted_only',
                    'label' => 'Recently deleted',
                    'total_uses' => $deletedOnlyVehicleUses,
                    'vehicle_count' => $deletedOnlyCount,
                    'avg_per_vehicle' => $deletedOnlyCount > 0
                        ? round($deletedOnlyVehicleUses / $deletedOnlyCount, 2)
                        : 0,
                ],
                [
                    'type' => 'purged',
                    'label' => 'Deleted & purged',
                    'total_uses' => $purgedVehicleUses,
                    'vehicle_count' => $purgedCount,
                    'avg_per_vehicle' => $purgedCount > 0 ? round($purgedVehicleUses / $purgedCount, 2) : 0,
                ],
            ],
        ];
    }
}
