<?php

declare(strict_types=1);

namespace apexl\Io\modules\file\traits;

use apexl\entityCore\helpers\Condition;
use apexl\Io\dto\Relationship\HasMany;
use apexl\Io\includes\System;
use apexl\Io\modules\file\entities\fileEntity;
use apexl\Vault\interfaces\driver;
use apexl\Vault\Vault;
use app\module\newtonsTrailers\Collection\FilesCollection;
use function apexl\Io\container;

trait HasFiles
{
    private const string TABLE__ENTITY_FILES = 'entity__files';

    public function addFile(string|int $key, int|fileEntity $file): void
    {
        $vault = container()->get(Vault::class);

        if (is_int($file)) {
            $fileId = $file;
            $file = new fileEntity();
            $file->load($fileId);
        }

        if ($this->hasFile($key)) {
            $this->removeLink($key);
        }

        $vault->insert(self::TABLE__ENTITY_FILES)
            ->fields([
                'entity' => $this::class,
                'entity_id' => $this->{$this->getPrimaryKey()},
                'file_id' => $file->id,
                'file_key' => $key,
            ])
            ->execute();
    }

    public function hasFile(string|int $key): bool
    {
        return !!$this->fileQuery($key)
            ->execute()
            ->fetch();
    }

    public function fileQuery(string|int $key): driver
    {
        $vault = container()->get(Vault::class);

        return $vault->select(self::TABLE__ENTITY_FILES)
            ->fields(['file_id'])
            ->where('entity', $this::class)
            ->where('entity_id', $this->{$this->getPrimaryKey()})
            ->where('file_key', $key, str_contains((string) $key, '%') ? 'LIKE' : '=');
    }

    private function removeLink(string|int $key): void
    {
        $vault = container()->get(Vault::class);

        $vault->delete(self::TABLE__ENTITY_FILES)
            ->where('entity', $this::class)
            ->where('entity_id', $this->{$this->getPrimaryKey()})
            ->where('file_key', $key)
            ->execute();
    }

    public function removeFile(string|int $key): void
    {
        $this->file($key)?->delete();
        $this->removeLink($key);
    }

    public function file(string|int $key): ?fileEntity
    {
        if ($this->hasFile($key)) {
            $fileId = $this->fileQuery($key)
                ->execute()
                ->fetchColumn();

            $file = new fileEntity();
            $file->load($fileId);

            return $file->id ? $file : null;
        }

        return null;
    }

    public function removeFiles(string $keyPattern): void
    {
        foreach ($this->files($keyPattern) as $file) {
            $file->delete();
        }

        $vault = container()->get(Vault::class);

        $vault->delete(self::TABLE__ENTITY_FILES)
            ->where('entity', $this::class)
            ->where('entity_id', $this->{$this->getPrimaryKey()})
            ->where('file_key', $keyPattern, 'LIKE')
            ->execute();
    }

    public function files(?string $key): FilesCollection
    {
        $ids = array_values(array_map(
            fn(object $row) => $row->file,
            $this->fileQuery($key)->execute()->fetchAll()
        ));

        return $ids ? FilesCollection::createFromArray(
            (new fileEntity())->loadMultiple([
                Condition::in('id', $ids),
            ])
        ) : new FilesCollection();
    }

    protected function filesRelation(string $key, string $name = 'files'): HasMany
    {
        return $this->hasMany(
            $name,
            fileEntity::class,
            pivotTable: self::TABLE__ENTITY_FILES,
            pivotLocal: 'entity_id',
            pivotRemote: 'file_id',
            pivotFields: [
                'entity' => $this::class,
                'file_key' => $key,
            ],
            hasWeight: false,
        );
    }
}
