<?php

namespace App\Master\Http\Controllers;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Validation\ValidationException;
use Mtc\MercuryDataModels\Tenant;
use Symfony\Component\Console\Command\Command as ConsoleCommand;

class CommandController extends Controller
{
    public function index(Request $request): array
    {
        return [
            'commands' => collect(Artisan::all())
                ->map(fn (ConsoleCommand $command, $key) => [
                    'name' => $key,
                    'id' => $key,
                    'description' => $command->getDescription(),
                    'signature' => trim($key . ' ' . $command->getNativeDefinition()->getSynopsis()),
                ])
                ->sortBy(fn ($row) => $row['name'])
                ->values(),
            'tenants' => Tenant::query()
                ->whereNull('suspended_at')
                ->select(['name', 'id'])
                ->get()
                ->prepend(['name' => 'All', 'id' => 'all'])
                ->prepend(['name' => 'System command (not run for tenants)', 'id' => 'none']),
        ];
    }

    public function store(Request $request): array
    {
        $parameters = $this->parseParameters($request->input('options'));

        if ($request->input('tenant') == 'none') {
            $exitCode = Artisan::call($request->input('command'), $this->buildCallParameters($parameters));

            return [
                'exit_code' => $exitCode,
                'output' => Artisan::output(),
            ];
        }

        $params = [
            'commandname' => $request->input('command'),
            '--argument' => $this->buildTenantRunPairs($parameters, false),
            '--option' => $this->buildTenantRunPairs($parameters, true),
        ];
        if ($request->input('tenant') !== 'all') {
            $params['--tenants'] = $request->input('tenant');
        }

        $exitCode = Artisan::call('tenants:run', $params);

        return [
            'exit_code' => $exitCode,
            'output' => Artisan::output(),
        ];
    }

    /**
     * Parse textarea input (one parameter per line) into structured pairs.
     * Accepted line formats: "--flag", "--option=value" and "argument=value".
     *
     * @return array<int, array{option: bool, key: string, value: string|null}>
     */
    private function parseParameters(?string $raw): array
    {
        return collect(preg_split('/\R/', (string) $raw))
            ->map(fn ($line) => trim($line))
            ->filter()
            ->map(function ($line) {
                $isOption = str_starts_with($line, '--');
                $parts = explode('=', $isOption ? substr($line, 2) : $line, 2);

                if ($parts[0] === '' || (! $isOption && ! isset($parts[1]))) {
                    throw ValidationException::withMessages([
                        'options' => "Invalid line \"{$line}\". Use --flag, --option=value or argument=value.",
                    ]);
                }

                return ['option' => $isOption, 'key' => $parts[0], 'value' => $parts[1] ?? null];
            })
            ->values()
            ->all();
    }

    /**
     * Build the parameter array for a direct Artisan::call. Repeated keys become arrays
     * so array options (--tag=a, --tag=b) pass through correctly.
     */
    private function buildCallParameters(array $parameters): array
    {
        $params = [];
        foreach ($parameters as $parameter) {
            $key = ($parameter['option'] ? '--' : '') . $parameter['key'];
            $value = $parameter['option'] ? ($parameter['value'] ?? true) : $parameter['value'];
            $params[$key] = array_key_exists($key, $params)
                ? array_merge((array) $params[$key], [$value])
                : $value;
        }

        return $params;
    }

    /**
     * Build "key=value" pairs for the tenants:run --argument / --option inputs.
     */
    private function buildTenantRunPairs(array $parameters, bool $options): array
    {
        return collect($parameters)
            ->filter(fn ($parameter) => $parameter['option'] === $options)
            ->map(fn ($parameter) => $parameter['key'] . '=' . ($parameter['value'] ?? '1'))
            ->values()
            ->all();
    }
}
