<?php

namespace App\Modules;

use Illuminate\Support\Collection;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Str;

class ModuleRepository
{
    public function __construct(
        /** @var string */
        protected string $modules_path = '',

        /** @var Collection<array-key, BaseModule> */
        protected Collection $modules = new Collection([]),

        /** @var Collection<array-key, BaseModule> */
        protected Collection $allModules = new Collection([]),
    ) {
        $this->prepareModules();
    }

    /**
     * Prepare modules
     */
    public function prepareModules(): void
    {
        $this->setPath();
        $this->setModules();
    }

    /**
     * Set path
     */
    public function setPath(): void
    {
        $this->modules_path = __DIR__;
    }

    /**
     * Set modules
     */
    public function setModules(bool $withDisabled = false): void
    {
        /** @var array<int, string> */
        $modulesDir = File::directories($this->path());

        foreach ($modulesDir as $modulePath) {
            if (Str::contains($modulePath, 'Core')) {
                continue;
            }

            $moduleName = Str::afterLast($modulePath, '/');

            $module = new BaseModule(
                path: $modulePath,
                name: $moduleName,
                app: $this->module($moduleName),
            );

            $this->allModules->push($module);

            // Include all
            if ($withDisabled) {
                $this->modules->push($module);

                continue;
            }

            if ($module->app->isEnabled()) {
                $this->modules->push($module);
            }
        }
    }

    /**
     * Modules path
     */
    public function path(): string
    {
        return $this->modules_path;
    }

    /**
     * Instantiate new module class if exists
     */
    public function module(string $moduleName): ModuleContract
    {
        $moduleClass = 'App\\Modules\\'.$moduleName.'\\'.$moduleName.'Module';

        if (class_exists($moduleClass)) {
            /** @var ModuleContract $module */
            $module = new $moduleClass;

            return $module;
        }

        return $this->emptyModule();
    }

    /**
     * Modules
     *
     * @return Collection<array-key, BaseModule>
     */
    public function modules(): Collection
    {
        return $this->modules;
    }

    /**
     * Empty module
     */
    public function emptyModule(): ModuleContract
    {
        return new EmptyModule;
    }

    /**
     * Get all modules regardless if disabled
     *
     * @return Collection<array-key, BaseModule>
     */
    public function allModules(): Collection
    {
        return $this->allModules;
    }

    /**
     * Check if module exists
     */
    public function moduleExists(string $moduleName): bool
    {
        return $this->modules->contains(fn (BaseModule $module) => $module->name === $moduleName);
    }
}
