<?php

namespace BookStack\Activity\Tools;

use BookStack\Activity\Models\Watch;
use BookStack\Activity\WatchLevels;
use BookStack\Entities\Models\BookChild;
use BookStack\Entities\Models\Entity;
use BookStack\Entities\Models\Page;
use BookStack\Users\Models\User;
use Illuminate\Database\Eloquent\Builder;

class UserEntityWatchOptions
{
    protected ?array $watchMap = null;

    public function __construct(
        protected User $user,
        protected Entity $entity,
    ) {
    }

    public function canWatch(): bool
    {
        return $this->user->can('receive-notifications') && !$this->user->isGuest();
    }

    public function getWatchLevel(): string
    {
        return WatchLevels::levelValueToName($this->getWatchLevelValue());
    }

    public function isWatching(): bool
    {
        return $this->getWatchLevelValue() !== WatchLevels::DEFAULT;
    }

    public function getWatchedParent(): ?WatchedParentDetails
    {
        $watchMap = $this->getWatchMap();
        unset($watchMap[$this->entity->getMorphClass()]);

        if (isset($watchMap['chapter'])) {
            return new WatchedParentDetails('chapter', $watchMap['chapter']);
        }

        if (isset($watchMap['book'])) {
            return new WatchedParentDetails('book', $watchMap['book']);
        }

        return null;
    }

    public function updateLevelByName(string $level): void
    {
        $levelValue = WatchLevels::levelNameToValue($level);
        $this->updateLevelByValue($levelValue);
    }

    public function updateLevelByValue(int $level): void
    {
        if ($level < 0) {
            $this->remove();
            return;
        }

        $this->updateLevel($level);
    }

    public function getWatchMap(): array
    {
        if (!is_null($this->watchMap)) {
            return $this->watchMap;
        }

        $entities = [$this->entity];
        if ($this->entity instanceof BookChild) {
            $entities[] = $this->entity->book;
        }
        if ($this->entity instanceof Page && $this->entity->chapter) {
            $entities[] = $this->entity->chapter;
        }

        $query = Watch::query()
            ->where('user_id', '=', $this->user->id)
            ->where(function (Builder $subQuery) use ($entities) {
                foreach ($entities as $entity) {
                    $subQuery->orWhere(function (Builder $whereQuery) use ($entity) {
                        $whereQuery->where('watchable_type', '=', $entity->getMorphClass())
                        ->where('watchable_id', '=', $entity->id);
                    });
                }
            });

        $this->watchMap = $query->get(['watchable_type', 'level'])
            ->pluck('level', 'watchable_type')
            ->toArray();

        return $this->watchMap;
    }

    protected function getWatchLevelValue()
    {
        return $this->getWatchMap()[$this->entity->getMorphClass()] ?? WatchLevels::DEFAULT;
    }

    protected function updateLevel(int $levelValue): void
    {
        Watch::query()->updateOrCreate([
            'watchable_id' => $this->entity->id,
            'watchable_type' => $this->entity->getMorphClass(),
            'user_id' => $this->user->id,
        ], [
            'level' => $levelValue,
        ]);
        $this->watchMap = null;
    }

    protected function remove(): void
    {
        $this->entityQuery()->delete();
        $this->watchMap = null;
    }

    protected function entityQuery(): Builder
    {
        return Watch::query()->where('watchable_id', '=', $this->entity->id)
            ->where('watchable_type', '=', $this->entity->getMorphClass())
            ->where('user_id', '=', $this->user->id);
    }
}
