<?php

namespace Snaju\Inception\Console;

use Snaju\Inception\Exception\InvalidArgumentException;

class ConsoleInput implements InputInterface
{

    protected array $argv = [];

    protected string $namespace = 'default';

    protected string $command = '';

    protected string $subcommand ='';

    protected string $resourceResultName = '';

    protected object $options;

    /**
     * Returns the proper type for a value given as a string.
     * @param $value
     * @return bool|float|string
     */
    private function getTypedValue($value)
    {
        if (is_numeric($value)) {
            return (double)$value;
        }
        else if ($value === 'true' || $value === 'false') {
            return (bool)$value;
        }
        else {
            return $value;
        }
    }

    public function __construct()
    {
        $this->argv = $_SERVER['argv'];
        $this->options = new \stdClass();
        $this->parse();
    }

    public function parse()
    {
        // If no command detected, set "help" as the default command
        if (!isset($this->argv[1])) {
            $this->command = 'help';
        }
        else {
            $argsShiftOffset = 0;

            // If the command string should have a namespace, it's expected to be in index position 1 and contain a slash
            // If contains a namespace, "unshift" the offset to account for it
            if (! str_contains($this->argv[1], '/')) {
                $argsShiftOffset = 1;
            }
            else {
                // Set the namespace
                $this->namespace = $this->argv[1];
            }

            // Separate command and subcommands
            if (str_contains($this->argv[2 - $argsShiftOffset], ':')) {
                $parts = explode(':', $this->argv[2 - $argsShiftOffset]);
                $this->command = $parts[0];
                $this->subcommand = $parts[1];
            }
            else {
                $this->command = $this->argv[2 - $argsShiftOffset];
            }

            // Check and set command resource result name if it exists
            if (isset($this->argv[3 - $argsShiftOffset])) {
                $this->resourceResultName = $this->argv[3 - $argsShiftOffset];
            }
        }
        
        // Get all the options for the command
        foreach ($this->getArgs() as $argument) {
            if (str_contains($argument, '--') && str_contains($argument, '=')) {
                $parts = explode('=', $argument);
                $property = str_replace('--', '', $parts[0]);
                $value = $this->getTypedValue($parts[1]);
                $this->options->{$property} = $value;
            }
        }

    }

    /**
     * @return array
     */
    public function getArgs(): array
    {
        return $this->argv;
    }

    /**
     * @return string
     */
    public function getNamespace(): string
    {
        return $this->namespace;
    }

    /**
     * @return string
     */
    public function getCommand(): string
    {
        return $this->command;
    }

    /**
     * @return string
     */
    public function getSubcommand(): string
    {
        return $this->subcommand;
    }

    public function getFullCommand(): string
    {
        return !empty($this->subcommand) ? $this->command .':'. $this->subcommand : $this->command;
    }

    /**
     * @return string
     */
    public function getResourceResultName(): string
    {
        return $this->resourceResultName;
    }

    /**
     * @return object
     */
    public function getOptions(): object
    {
        return $this->options;
    }

    public function hasDefaultNamespace(): bool
    {
        return !! $this->namespace === 'default';
    }

    /**
     * @return bool
     */
    public function hasSubcommand(): bool
    {
        return (!empty($this->subcommand));
    }

    public function hasOptions(): bool
    {
        return count((array)$this->options) > 0;
    }

}