<?php

namespace App\Models;

use App\Models\Downloads;
use App\Models\PopularMonthly;
use App\Models\PopularWeekly;
use App\Models\PopularDaily;

use Conner\Tagging\Taggable;
use Laravelista\Comments\Commentable;
use Maize\Markable\Markable;
use Maize\Markable\Models\Like;

use Spatie\Sitemap\Contracts\Sitemapable;
use Spatie\Sitemap\Tags\Url;

use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Cache;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;

class Episode extends Model implements Sitemapable
{
    use Commentable, Markable, Taggable;
    use HasFactory;

    protected static $marks = [
        Like::class
    ];

    /**
     * Get the studio for the Hentai.
     */
    public function studio(): BelongsTo
    {
        return $this->belongsTo(Studios::class, 'studios_id');
    }

    /**
     * Get the hentai for the episode.
     */
    public function hentai(): BelongsTo
    {
        return $this->belongsTo(Hentai::class, 'hentai_id');
    }

    /**
     * Get the subtitles for the episode.
     */
    public function subtitles(): HasMany
    {
        return $this->hasMany(EpisodeSubtitle::class);
    }

    /**
     * Has a Gallery.
     */
    public function gallery(): HasMany
    {
        return $this->hasMany(Gallery::class);
    }

    /**
     * Has many Downloads.
     */
    public function downloads(): HasMany
    {
        return $this->hasMany(Downloads::class);
    }

    /**
     * Increment View Count.
     */
    public function incrementViewCount(): bool
    {
        $this->view_count++;
        return $this->save();
    }

    /**
     * Increment Popular Count.
     */
    public function incrementPopularCount(): void
    {
        PopularDaily::create(['episode_id' => $this->id]);
        PopularWeekly::create(['episode_id' => $this->id]);
        PopularMonthly::create(['episode_id' => $this->id]);
    }

    /**
     * Get cached view count
     */
    public function viewCount(): int
    {
        return cache()->remember('episodeViews' . $this->id, 300, fn() => $this->view_count);
    }

    /**
     * Get view count in a human readable way
     */
    public function viewCountFormatted(): string
    {
        if ($this->viewCount() < 1000) {
            return $this->viewCount();
        }

        $units = ['k', 'M'];
        $index = floor(log($this->viewCount(), 1000));
        $shortNumber = $this->viewCount() / pow(1000, $index);

        return round($shortNumber, 0) . $units[$index - 1];
    }

    /**
     * Get cached like count
     */
    public function likeCount(): int
    {
        return cache()->remember('episodeLikes' . $this->id, 300, fn() => $this->likes->count());
    }

    /**
     * Get cached comment count
     */
    public function commentCount(): int
    {
        return cache()->remember('episodeComments' . $this->id, 300, fn() => $this->comments->count());
    }

    public function getProblematicTags(): string
    {
        $problematicTags = ['Gore', 'Scat', 'Horror'];
        $problematicResults = '';
        foreach ($problematicTags as $pTag) {
            if (! $this->tags->contains('name', $pTag)) {
                continue;
            }

            if (! empty($problematicResults)) {
                $problematicResults .= ' + ';
            }

            $problematicResults .= $pTag;
        }
        return $problematicResults;
    }

    /**
     * Check if episode contains loli / shota tag
     */
    public function isLoliOrShota(): bool
    {
        $problematicTags = ['Loli', 'Shota'];

        return Cache::remember(
            "episode:{$this->id}:has_problematic_tags",
            now()->addMinutes(1440),
            fn () => $this->tags->pluck('name')->intersect($problematicTags)->isNotEmpty()
        );
    }

    /**
     * If episode has machine translated subtitles
     */
    public function hasAutoTrans(): bool
    {
        return cache()->remember('mt' . $this->id, 900, fn() => $this->subtitles()->exists());
    }

    public function is48Fps(): bool
    {
        return cache()->remember('48fps' . $this->id, 900, fn() => $this->interpolated);
    }

    public function isUHD48Fps(): bool
    {
        return cache()->remember('48fpsUHD' . $this->id, 900, fn() => $this->interpolated_uhd);
    }

    public function getResolution(): string
    {
        if ($this->isUHD48Fps()) {
            return '4k | 4k 48fps | FHD 48fps';
        }

        return $this->is48Fps() ? '4k | FHD 48fps' : '4k';
    }

    public function userWatched(int $user_id): bool
    {
        return cache()->remember('user' . $user_id . 'watched' . $this->id, 300, fn() => Watched::where('user_id', $user_id)->where('episode_id', $this->id)->exists());
    }

    public function watched(): HasMany
    {
        return $this->hasMany(Watched::class);
    }

    public function getDownloadByType(string $type): Downloads | null
    {
        $cacheKey = "episode_{$this->id}_download_{$type}";
        return Cache::remember($cacheKey, now()->addMinutes(10), function () use ($type) {
            return $this->downloads()->where('type', $type)->first();
        });
    }

    public function toSitemapTag(): Url | string | array
    {
        return Url::create(route('hentai.index', $this->slug))
            ->setLastModificationDate(Carbon::create($this->created_at));
    }
}
