<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;

use App\Utilities\Paginate;

use App\Traits\ContentElementTrait;
use App\Traits\TagsTrait;
use App\Traits\AppendAttributesTrait;
use App\Traits\PhotosTrait;

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

use App\Utilities\PageLink;

class Calendar extends Model implements SearchResultContract
{
    use HasFactory;
    use ContentElementTrait;
    use TagsTrait;
    use PhotosTrait;
    use AppendAttributesTrait;

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

    public function saveContent(array $input, $id = null)
    {
        if ($id >= 1) {
            $calendar = Calendar::findOrFail($id);
        } else {
            $calendar = new Calendar();
        }

        $calendar->header = Arr::get($input, 'header');
        $calendar->body = Arr::get($input, 'body');
        $calendar->layout = Arr::get($input, 'layout');
        $calendar->count = Arr::get($input, 'count');
        $calendar->save();

        $calendar->saveGoogleCalendars($input, $id ? false : true); // if id is null, create new google calendars

        $calendar->saveTags($input);

        cache()->tags([cache_name($calendar)])->flush();
        return $calendar;
    }

    public function getBodyAttribute($value)
    {
        return $this->convertAttributeLinks($value, 'body');
    }

    public function googleCalendars()
    {
        return $this->hasMany(GoogleCalendar::class);
    }

    public function saveGoogleCalendars($input, $new_version)
    {
        $calendars = collect();

        if (is_array(Arr::get($input, 'google_calendars'))) {
            foreach (Arr::get($input, 'google_calendars') as $google_calendar_data) {
                $google_calendar_data['calendar_id'] = $this->id;
                $google_calendar = (new GoogleCalendar())->saveGoogleCalendar($google_calendar_data, !$new_version ? Arr::get($google_calendar_data, 'id') : null);
                $calendars->push($google_calendar);
            }
        }

        $this->googleCalendars()->get()->each(function ($gc) use ($calendars) {
            if (!$calendars->contains('id', $gc->id)) {
                $gc->delete();
            }
        });

        return $this;
    }

    public function getCalendarEvents($include_calendars = null, $input = null)
    {
        $google_calendars = $this->googleCalendars;

        if (!$include_calendars) {
            $include_calendars = request('google_calendars');
        }

        if ($include_calendars) {
            $ids = collect($include_calendars)->pluck('id');
            $google_calendars = $google_calendars->filter(function ($gc) use ($ids) {
                return $ids->contains($gc->id);
            });
        }

        $calendar_events = $google_calendars->map(function ($google_calendar) use ($input) {
            return $google_calendar->getCalendarEvents($input);
        })
        ->flatten();

        if (request('terms') && $calendar_events->count()) {
            // TODO we may want to use SearchResult::collectTerms here
            $terms = request('terms');

            if (mb_strlen($terms) > 2) {
                $terms = explode(' ', Str::lower($terms));

                $calendar_events = $calendar_events->filter(function ($event) use ($terms) {
                    return Str::containsAll(Str::lower($event->title), $terms);
                });
            }
        }

        return $calendar_events->sortBy(function ($event) {
            return $event->start_date;
        })
        ->values();
    }

    public function getSearchFieldsAttribute()
    {
        return [
            'header',
            'body',
        ];
    }

    public function getSearchResultTitle($default = null)
    {
        if (mb_strlen($this->header) > 0) {
            return $this->header;
        }
        return $default;
    }

    public function getSearchResultPreview($terms)
    {
        return SearchResult::truncatePreview(strip_tags($this->body) ? $this->body : $this->header, $terms);
    }

    public function getSearchResultRank($terms)
    {
        $rank = 0;
        foreach ($terms as $term) {
            $rank += mb_substr_count($this->header, $term);
            $rank += mb_substr_count($this->body, $term) * 0.5;
        }
        return $rank;
    }
}
