<?php

declare(strict_types=1);

namespace FiloBlu\Refilo\Console\Command;

use FiloBlu\Refilo\Model\Deleter\ItemCleaner;
use FiloBlu\Refilo\Remote\AbstractIndexer;
use Magento\Framework\App\Area;
use Magento\Framework\App\ObjectManager;
use Magento\Framework\App\State;
use Magento\Indexer\Model\IndexerFactory;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class CleanIndexerCommand extends Command
{
    const ARG_INDEXER = 'indexer';

    /**
     * @var ItemCleaner
     */
    private $itemCleaner;

    /**
     * @var IndexerFactory
     */
    private $indexerFactory;

    /**
     * @var State
     */
    private $appState;

    /**
     * CleanIndexerCommand constructor.
     *
     * @param ItemCleaner $itemCleaner
     * @param IndexerFactory $indexerFactory
     * @param State $appState
     */
    public function __construct(
        ItemCleaner $itemCleaner,
        IndexerFactory $indexerFactory,
        State $appState
    ) {
        parent::__construct();
        $this->itemCleaner = $itemCleaner;
        $this->indexerFactory = $indexerFactory;
        $this->appState = $appState;
    }

    /**
     * Configure the command settings
     */
    protected function configure()
    {
        $this->setName('refilo:indexer:clean')
            ->setDescription('Clean all indexers or a specific one by indexer name')
            ->addArgument(
                self::ARG_INDEXER,
                InputArgument::OPTIONAL,
                'Indexer name to clean (e.g. beehive_product)'
            );
    }

    /**
     * Executes the clean indexer command.
     *
     * This method sets the application area code, determines if a specific indexer code
     * was provided, and either cleans a specific indexer or all indexers accordingly.
     * Outputs success or error messages to the console.
     *
     * @param InputInterface $input The input interface for command arguments and options.
     * @param OutputInterface $output The output interface for console messages.
     * @return int Returns 0 on success, 1 on failure.
     */
    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        try {
            $this->appState->setAreaCode(Area::AREA_CRONTAB);
        } catch (\Exception $e) {
            // Area code is already set
        }

        $indexerCode = $input->getArgument(self::ARG_INDEXER);

        if ($indexerCode) {
            $indexer = $this->indexerFactory->create()->load($indexerCode);
            $actionClass = $indexer->getActionClass();
            return $this->cleanIndexer($actionClass, $indexerCode, $output);
        }

        try {
            $this->itemCleaner->clean();
            $output->writeln('<info>All indexers cleaned successfully</info>');
        }catch (\Exception $e){
            $output->writeln("<error>Error cleaning indexer: {$e->getMessage()}</error>");
            return 1;
        }

        return  0;
    }

    /**
     * Cleans a specific indexer by its action class.
     *
     * @param string $actionClass The fully qualified class name of the indexer action.
     * @param string $indexerCode The code of the indexer to clean.
     * @param OutputInterface $output The output interface for console messages.
     * @return int Returns 0 on success, 1 on failure.
     */
    private function cleanIndexer(string $actionClass, string $indexerCode, OutputInterface $output): int
    {
        if (!is_a($actionClass, AbstractIndexer::class, true)) {
            $output->writeln("<error>Action class '$actionClass' is not a Refilo AbstractIndexer</error>");
            return 1;
        }

        /** @var AbstractIndexer $indexerModel */
        $indexerModel = ObjectManager::getInstance()->create($actionClass);

        try {
            $this->itemCleaner->cleanByIndexer($indexerModel);
            $output->writeln("<info>Indexer '{$indexerCode}' cleaned successfully</info>");
            return 0;
        } catch (\Exception $e) {
            $output->writeln("<error>Error cleaning indexer '{$indexerCode}': {$e->getMessage()}</error>");
            return 1;
        }
    }
}
