<?php

namespace App\Services;

use App\Enums\DocumentTypeEnum;
use App\Models\Music;
use App\Models\MusicAlbum;
use App\Models\Product;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\DB;

trait MusicService
{
    /**
     * Store new record
     *
     * @param array $data
     * @return Builder|Model
     */
    public static function store(array $data): Model|Builder
    {
        return Music::query()->create($data);
    }

    /**
     * Update existing record
     *
     * @param array $data
     * @return mixed
     */
    public function updateService(array $data): mixed
    {
        return tap($this)->update($data);
    }

    /**
     * Find by id
     *
     * @param int $id
     * @return Builder|Builder[]|Collection|Model|null
     */
    public static function findById(int $id): Model|Collection|Builder|array|null
    {
        return Music::query()->find($id);
    }

    /**
     * Try to add or update Music poster
     *
     * @param UploadedFile $uploadedFile
     * @param string $basePath
     * @return Music
     */
    public function addMusicPoster(UploadedFile $uploadedFile, string $basePath): Music
    {
        // Try to upload ...
        $resultPath = upload_file_system(DocumentTypeEnum::poster->value, $basePath, $uploadedFile);

        if(filled($resultPath)) {
            $this->updateService(array(
                "cover" => $resultPath,
            ));
        }

        return $this->refresh();
    }

    /**
     * Try to add or update associated Music file
     *
     * @param UploadedFile $uploadedFile
     * @param string $basePath
     * @return Music
     */
    public function addMusicFile(UploadedFile $uploadedFile, string $basePath): Music
    {
        // Try to upload ...
        $resultPath = upload_file_system(DocumentTypeEnum::musicFile->value, $basePath, $uploadedFile);

        if(filled($resultPath)) {
            $this->updateService(array(
                "file_path" => $resultPath,
            ));
        }

        return $this->refresh();
    }

    /**
     * Add new music
     *
     * @param array $data
     * @param MusicAlbum|null $album
     * @param string|null $filePath
     * @param string|null $cover
     * @return Model|Builder|null
     */
    public static function createNewMusic(array $data, MusicAlbum $album = null, string $filePath = null, string $cover = null): Model|Builder|null
    {
        return self::store(array(
            'designation' => $data['designation'] ?? null,
            'description' => $data['description'] ?? null,
            'occurrence' => isset($data['occurrence']) && $data['occurrence'] > config("torryme.constants.default_zero_number") ? $data['occurrence'] : config("torryme.constants.default_one_number"),
            'file_path' => $filePath,
            'cover' => $cover,
            'music_album_id' => $album?->{'id'}
        ));
    }

    /**
     * Add new music single into album
     *
     * @param MusicAlbum $album
     * @param UploadedFile $musicFile
     * @param array $data
     * @return Builder|Model|null
     * @throws \Exception
     */
    public static function createNewMusicSingleForAlbum(MusicAlbum $album, UploadedFile $musicFile, array $data): Model|Builder|null
    {
        DB::beginTransaction();

        try {
            $product = $album->{'product'};
            $business = $product->{'business'};

            // And add music to album ...
            $basePath = sprintf(
                config('torryme.paths.docs'),
                sprintf(
                    "%s/%s",
                    $business->buildProductDir($product->{'id'}),
                    MusicAlbum::$prefixDir,
                ),
            );

            // Try to store music single ...
            $resultPath = upload_file_system(
                DocumentTypeEnum::albumMusicFile->value,
                $basePath,
                $musicFile,
            );

            if(filled($resultPath)) {
                $single = self::createNewMusic($data, $album, $resultPath);

                // Try to update product status
                if(! $product->isComplete()) {
                    $product->updateStatusToComplete();
                }

                DB::commit();
                return $single->refresh();
            }

            DB::rollBack();
        } catch (\Exception $exception) {
            DB::rollBack();
            log_debug(exception: $exception,  prefix: 'MusicAlbumService::createNewMusicSingleForAlbum');

            throw $exception;
        }

        return null;
    }

    /**
     * Create new Music with cover file
     *
     * @param Product $product
     * @param string $basePath
     * @param UploadedFile $cover
     * @return Model|Builder|null
     */
    public static function createMusicWithCover(Product $product, string $basePath, UploadedFile $cover): Model|Builder|null
    {
        $music = null;
        DB::beginTransaction();

        try {
            // Try to upload ...
            $resultPath = upload_file_system(DocumentTypeEnum::poster->value, $basePath, $cover);
            if(filled($resultPath)) {
                $music = self::createNewMusic(
                    array(
                        'designation' => $product->{'designation'},
                        'description' => $product->{'description'},
                    ),
                    null, null, $resultPath
                );
            }

            if($music !== null) {
                $product->updateService([
                    "music_id" => $music->{'id'},
                ]);

                DB::commit();
                return $music->refresh();
            }

            DB::rollBack();
        } catch (\Exception $exception) {
            log_debug(exception: $exception,  prefix: 'MusicService::createMusicWithCover');

            DB::rollBack();
        }

        return $music;
    }

    /**
     * Create new Music with file
     *
     * @param Product $product
     * @param string $basePath
     * @param UploadedFile $musicFile
     * @return Model|Builder|null
     */
    public static function createMusicWithFile(Product $product, string $basePath, UploadedFile $musicFile): Model|Builder|null
    {
        $music = null;
        DB::beginTransaction();

        try {
            // Try to upload ...
            $resultPath = upload_file_system(DocumentTypeEnum::musicFile->value, $basePath, $musicFile);
            if(filled($resultPath)) {
                $music = self::createNewMusic(
                    array(
                        'designation' => $product->{'designation'},
                        'description' => $product->{'description'},
                    ),
                    null, $resultPath, null
                );
            }

            if($music !== null) {
                $product->updateService([
                    "music_id" => $music->{'id'},
                ]);

                DB::commit();
                return $music->refresh();
            }

            DB::rollBack();
        } catch (\Exception $exception) {
            log_debug(exception: $exception,  prefix: 'MusicService::createMusicWithFile');

            DB::rollBack();
        }

        return $music;
    }

    /**
     * Total number of music single into the album
     *
     * @param MusicAlbum $album
     * @return int
     */
    public static function totalAlbumMusic(MusicAlbum $album): int
    {
        return Music::query()
            ->whereHas('musicAlbum', function(Builder $builder) use($album) {
                $builder->where('id', $album->{'id'});
            })
            ->count();
    }

    /**
     * Compute sample music attributes
     *
     * @param Collection $musics
     * @return array
     */
    public static function computeSampleDetails(Collection $musics): array
    {
        $result = [];

        foreach ($musics as $music) {
            $result[] = array(
                'cover_url' => $music->{'cover_url'},
                'music_url' => $music->{'music_url'},
                "music_type" => $music->musicFileType(),
                "designation" => $music->{'designation'},
                "occurrence" => $music->{'occurrence'},
                "size" => $music->musicSize(),
                "duration" => $music->musicDuration(),
            );
        }

        return $result;
    }
}
