<?php

namespace App\Services;

use App\Enums\ApprovalStatusEnum;
use App\Models\Client;
use App\Models\Media;
use App\Models\Provider;
use App\Traits\RemoveArchiveS3;
use App\Traits\UploadsFile;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Http\UploadedFile;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

class MediaService
{
    use RemoveArchiveS3, UploadsFile;

    public function getAll(): Collection
    {
        return Media::query()
            ->with(['user'])
            ->orderBy('created_at', 'desc')
            ->get();
    }

    public function findById(int $id): ?Media
    {
        return Media::with(['user'])->find($id);
    }

    public function create(array $data): Media
    {
        return Media::create($data);
    }

    public function update(int $id, array $data): ?Media
    {
        $model = $this->findById($id);

        if (! $model) {
            return null;
        }

        $model->update($data);

        return $model->fresh(['user']);
    }

    public function delete(int $id): bool
    {
        $model = $this->findById($id);

        if (! $model) {
            return false;
        }

        return $model->delete();
    }

    //

    public function getPendingProfileMedia(int $page = 1, int $perPage = 10): LengthAwarePaginator
    {
        $paginated = Media::query()
            ->where(function ($query) {
                $query->where(function ($query) {
                    $query->where('media.source', 'provider')
                        ->whereExists(function ($query) {
                            $query->selectRaw('1')
                                ->from('providers')
                                ->whereColumn('providers.id', 'media.source_id')
                                ->whereColumn('providers.profile_media_id', 'media.id')
                                ->where('providers.approval_status', ApprovalStatusEnum::ACCEPTED->value)
                                ->where('providers.selfie_verified', false)
                                ->whereNull('providers.deleted_at');
                        });
                })->orWhere(function ($query) {
                    $query->where('media.source', 'client')
                        ->whereExists(function ($query) {
                            $query->selectRaw('1')
                                ->from('clients')
                                ->whereColumn('clients.id', 'media.source_id')
                                ->whereColumn('clients.profile_media_id', 'media.id')
                                ->where('clients.selfie_verified', false)
                                ->whereNull('clients.deleted_at');
                        });
                });
            })
            ->orderBy('media.created_at')
            ->paginate($perPage, ['media.*'], 'page', $page);

        $media       = $paginated->getCollection();
        $providerIds = $media->where('source', 'provider')->pluck('source_id');
        $clientIds   = $media->where('source', 'client')->pluck('source_id');

        $providers = $providerIds->isEmpty()
            ? collect()
            : Provider::query()
                ->with('user:id,name,email')
                ->whereIn('id', $providerIds)
                ->get()
                ->keyBy('id');

        $clients = $clientIds->isEmpty()
            ? collect()
            : Client::query()
                ->with('user:id,name,email')
                ->whereIn('id', $clientIds)
                ->get()
                ->keyBy('id');

        $media->each(function (Media $item) use ($clients, $providers) {
            $profile = match ($item->source) {
                'client'   => $clients->get($item->source_id),
                'provider' => $providers->get($item->source_id),
            };

            $item->setRelation('user', $profile?->user);
        });

        return $paginated;
    }

    public function approveProfileMedia(string $source, int $sourceId): Media
    {
        return DB::transaction(function () use ($source, $sourceId) {
            $profile = match ($source) {
                'client'   => Client::query()->lockForUpdate()->findOrFail($sourceId),
                'provider' => Provider::query()->lockForUpdate()->findOrFail($sourceId),
            };

            $profile->load('profileMedia');

            $media = $profile->profileMedia;

            if ($media === null) {
                throw (new ModelNotFoundException)->setModel(Media::class);
            }

            $profile->update(['selfie_verified' => true]);

            return $media;
        });
    }

    public function createFromFile(UploadedFile $file, string $folder, string $source, int $sourceId, ?string $filename = null): Media
    {
        Log::warning('[avatar-upload] MediaService::createFromFile — iniciando upload para S3', [
            'folder'             => $folder,
            'source'             => $source,
            'source_id'          => $sourceId,
            'file_original_name' => $file->getClientOriginalName(),
            'file_size'          => $file->getSize(),
            'file_mime'          => $file->getMimeType(),
            'file_extension'     => $file->getClientOriginalExtension(),
            'file_is_valid'      => $file->isValid(),
        ]);

        $path = $this->uploadFile($file, $folder, $filename);

        Log::warning('[avatar-upload] MediaService::createFromFile — upload concluído, criando registro Media', [
            'path_retornado' => $path,
            'source'         => $source,
            'source_id'      => $sourceId,
        ]);

        $media = Media::create([
            'source'    => $source,
            'source_id' => $sourceId,
            'name'      => $file->getClientOriginalName(),
            'path'      => $path,
        ]);

        Log::warning('[avatar-upload] MediaService::createFromFile — Media criado no banco', [
            'media_id'   => $media->id,
            'media_path' => $media->path,
            'media_url'  => $media->url ?? null,
        ]);

        return $media;
    }

    public function replaceFile(UploadedFile $newFile, string $folder, string $source, int $sourceId, ?Media $old = null, ?string $filename = null): Media
    {
        Log::warning('[avatar-upload] MediaService::replaceFile iniciado', [
            'folder'        => $folder,
            'source'        => $source,
            'source_id'     => $sourceId,
            'has_old_media' => $old !== null,
            'old_media_id'  => $old?->id,
            'old_path'      => $old?->path,
        ]);

        if ($old) {
            Log::warning('[avatar-upload] Removendo arquivo antigo do S3', ['old_path' => $old->path]);

            $this->removeArchiveByPath($old->path);

            $old->delete();

            Log::warning('[avatar-upload] Arquivo antigo removido do S3 e Media deletado do banco');
        }

        return $this->createFromFile($newFile, $folder, $source, $sourceId, $filename);
    }
}
