<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\URL;
use Illuminate\Support\Facades\Mail;

use Illuminate\Support\Arr;
use App\Models\User;
use App\Models\Tag;
use App\Models\LivestreamRegistration;

use App\Mail\LivestreamRegistered;
use App\Mail\LivestreamModeratorAdded;
use App\Mail\LivestreamTimeChanged;

use App\Traits\TagsTrait;
use App\Traits\HasPermissionsTrait;
use App\Traits\AppendAttributesTrait;
use App\Traits\PhotosTrait;
use App\Traits\FindByTagsTrait;

use App\Contracts\SearchResultContract;
use App\Utilities\SearchResult;
use App\Utilities\PageLink;

class Livestream extends Model implements SearchResultContract
{
    use HasFactory;
    use TagsTrait;
    use HasPermissionsTrait;
    use AppendAttributesTrait;
    use PhotosTrait;
    use FindByTagsTrait;

    protected $with = ['tags', 'photos'];

    protected $appends = ['chat_room', 'roles', 'actions', 'has_permissions', 'moderators', 'banner', 'has_completed'];

    protected $casts = [
        'unlisted' => 'boolean',
        'start_date' => 'datetime',
        'will_be_recorded' => 'boolean',
    ];

    public function loadCollectionAttributes($items)
    {
        return $items;
    }

    public function getFullSlugAttribute()
    {
        return 'livestreams/'.$this->id;
    }

    public function saveLivestream($input, $id = null)
    {
        if ($id) {
            $livestream = Livestream::findOrFail($id);
        } else {
            $livestream = new Livestream();
        }

        $old_start_date = $livestream->start_date;

        $livestream->name = Arr::get($input, 'name');
        $livestream->description = Arr::get($input, 'description');
        $livestream->video_id = Arr::get($input, 'video_id');
        $livestream->start_date = Arr::get($input, 'start_date');
        $livestream->length = Arr::get($input, 'length'); // in minutes
        $livestream->chat_mode = Arr::get($input, 'chat_mode');
        $livestream->unlisted = Arr::get($input, 'unlisted');
	$livestream->can_register = Arr::get($input, 'can_register');
	$livestream->will_be_recorded = Arr::get($input, 'will_be_recorded', true);
        $livestream->save();

        $livestream->saveTags($input);
        $livestream->saveRoles($input, 'view');
        $livestream->saveModerators($input);
        $livestream->saveSinglePhoto($input);

        if ($id > 0 && $livestream->start_date->notEqualTo($old_start_date)) {
            $livestream_registrations = $livestream->livestreamRegistrations()->get();
            foreach ($livestream_registrations as $livestream_registration) {
                Mail::to($livestream_registration->user->email)
                    ->queue(new LivestreamTimeChanged($livestream, $old_start_date, $livestream_registration->user, $livestream_registration->url, $livestream_registration->unregister_url));
            }
        }

        cache()->tags([cache_name($livestream)])->flush();
        $livestream->refresh();

        return $livestream;
    }

    public function saveModerators($input)
    {
        $old_mods = $this->moderators;
        $users = collect();

        if (Arr::get($input, 'moderators')) {
            foreach (Arr::get($input, 'moderators') as $user_data) {
                $user = User::find(Arr::get($user_data, 'id'));
                if ($user instanceof User) {
                    $users->push($user);
                }
            }

            foreach ($old_mods as $old_mod) {
                if (!$users->contains('id', $old_mod->id)) {
                    $this->removePermission('moderate', $old_mod);
                }
            }

            foreach ($users as $user) {
                if (!$user->can('moderate', $this)) {
                    $this->createPermission('moderate', $user);
                    Mail::to($user->email)
                        ->queue(new LivestreamModeratorAdded($this, $user));
                }
            }
        }
    }

    public function getDescriptionAttribute($value)
    {
        return PageLink::convertLinkText($value);
    }

    public function getBannerAttribute()
    {
        return $this->photos->first();
    }

    public function getModeratorsAttribute()
    {
        return $this->permissions()->where('action', 'moderate')->get()->map->accessable;
    }

    public function livestreamRegistrations()
    {
        return $this->hasMany(LivestreamRegistration::class);
    }

    public function users()
    {
        return $this->hasManyThrough(User::class, LivestreamRegistration::class, 'livestream_id', 'id', 'id', 'user_id');
    }

    public function inquiryUsers()
    {
        return $this->users()->whereHas('inquiries');
    }

    public function getDateAttribute()
    {
        return $this->start_date->timezone('America/Vancouver')->format('l F j, Y g:i a');
    }

    /*
    public function getInquiryUsersAttribute()
    {
        return $this->inquiries->map(function ($inquiry) {
            $user = $inquiry->user;
            $user->pivot = $inquiry->pivot;
            return $user;
        });
    }
    */

    public function getChatRoomAttribute()
    {
        return 'livestream.'.$this->id;
    }

    public function getSearchFieldsAttribute()
    {
        return ['name'];
    }

    public static function searchResults($terms, $collection = null)
    {
        $input = request()->only(['descending', 'sort_by']);
        $livestreams = Livestream::with('permissions')
            ->where(function ($query) use ($terms) {
                $first = true;
                foreach ($terms as $term) {
                    if ($first) {
                        $query->where('name', 'LIKE', '%'.$term.'%');
                        $first = false;
                    } else {
                        $query->orWhere('name', 'LIKE', '%'.$term.'%');
                    }
                }
                $query->orWhereHas('tags', function ($query) use ($terms) {
                    $first = true;
                    foreach ($terms as $term) {
                        if ($first) {
                            $query->where('name', 'LIKE', '%'.$term.'%');
                            $first = false;
                        } else {
                            $query->orWhere('name', 'LIKE', '%'.$term.'%');
                        }
                    }
                });
            })
        ->orderBy(Arr::get($input, 'sort_by', 'start_date'), Arr::get($input, 'descending') ? 'desc' : 'asc')
        ->get()
        ->filter(function ($livestream) {
            if ($livestream->roles->count()) {
                if (!auth()->check()) {
                    return false;
                }

                return auth()->user()->can('view', $livestream);
            }
            return true;
        });

        if ($collection) {
            return $livestreams;
        }

        $results = collect();

        foreach ($livestreams as $livestream) {
            $searh_result = new SearchResult(
                'livestream',
                $livestream->id,
                route('livestreams.view', ['id' => $livestream->id]),
                $livestream->getSearchResultTitle(),
                $livestream->getSearchResultPreview($terms),
                $livestream->getSearchResultRank($terms),
                'Livestreams > '.$livestream->name,
                $livestream->start_date,
            );
            $results->push($searh_result);
        }

        return $results;
    }

    public function getSearchResultTitle($default = null)
    {
        return $this->name;
    }

    public function getSearchResultPreview($terms)
    {
        if ($this->start_date->isFuture()) {
            return 'Upcoming livestream: '.$this->description;
        } else {
            return 'Previously Broadcast: '.$this->description;
        }
    }

    public function getSearchResultRank($terms)
    {
        $rank = 0;
        if ($this->start_date->isFuture()) {
            $multiplier = 5;
        } else {
            $multiplier = 3;
        }

        foreach ($terms as $term) {
            $rank += mb_substr_count($this->name, $term) * $multiplier;
            $rank += mb_substr_count($this->description, $term) * $multiplier;
        }
        return $rank;
    }

    public function registerUser($input, $skip_email_confirmation = false)
    {
        if ($input instanceof User) {
            $user = $input;
        } elseif (Arr::get($input, 'user_id')) {
            $user = User::findOrFail(Arr::get($input, 'user_id'));
        } else {
            $user = User::findOrCreateByEmail($input);
        }

        if (!$user->livestreamRegistrations->contains('livestream_id', $this->id)) {
            $livestream_registration = (new LivestreamRegistration())->saveLivestreamRegistration($this, $user);
        } else {
            $livestream_registration = $user->livestreamRegistrations->where('livestream_id', $this->id)->first();
        }

        $url = $livestream_registration->url;

        cache()->tags([cache_name($this), cache_name($user)])->flush();

        if (!$skip_email_confirmation) {
            Mail::to($user->email)
                ->queue(new LivestreamRegistered($this, $user, $url));
        }
    }

    public function getHasCompletedAttribute()
    {
        return $this->start_date->addMinutes((int) $this->length)->isPast();
    }

    public static function getLivestreams($input = null)
    {
        $livestreams = (new Livestream())->getByTags(request());

        $livestreams = $livestreams->filter(function ($livestream) use ($input) {
            if (!$livestream->unlisted) {
                return true;
            }

            if (!auth()->check()) {
                return false;
            } else {
                return auth()->user()->can('view', $livestream);
            }
        })
            ->filter(function ($livestream) use ($input) {
                if (Arr::get($input, 'show_future') && Arr::get($input, 'show_past')) {
                    return true;
                } else {
                    if (Arr::get($input, 'show_future')) {
                        return $livestream->start_date->addMinutes((int) $livestream->length)->isFuture();
                    }
                    if (Arr::get($input, 'show_past')) {
                        return $livestream->start_date->addMinutes((int) $livestream->length)->isPast();
                    }
                    return true;
                }
            })
            ->sortBy(function ($livestream) {
                return $livestream->start_date;
            })
            ->load('livestreamRegistrations', 'livestreamRegistrations.user', 'livestreamRegistrations.location', 'permissions');

        if (Arr::get($input, 'show_past')) {
            $livestreams = $livestreams->reverse();
        }

        $livestreams = $livestreams->values();

        /*
        if ($tags) {
            if ($tags instanceof Tag) {
                $tags = collect([$tags]);
            }

            if ($tags->count()) {
                $livestreams = $livestreams->filter(function ($livestream) use ($tags) {
                    foreach ($tags as $tag) {
                        if ($livestream->tags->contains('id', $tag->id)) {
                            return true;
                        }
                    }
                    return false;
                });
            }
        }
         */

        return $livestreams;
    }

    public function markUserAttended(User $user = null)
    {
        if (!$user) {
            $user = auth()->user();
        }

        if ($user) {
            $livestream_registration = $this->livestreamRegistrations()->where('user_id', $user->id)->first();

            if ($livestream_registration) {
                $livestream_registration->attended_at = now();
                $livestream_registration->save();
            }
        }

        return $this;
    }
}
