<?php
/**
 * Copyright 2020 Adobe
 * All Rights Reserved.
 *
 * NOTICE: Adobe permits you to use, modify, and distribute this file in
 * accordance with the terms of the Adobe license agreement accompanying
 * it.
 */
declare(strict_types=1);

namespace Magento\Mray\Index\Traverser;

use Magento\Mray\Package\AbstractTree\Component\MagentoLibrary;
use Magento\Mray\Package\AbstractTree\Component\MagentoModule;
use Magento\Mray\Package\AbstractTree\Component\MagentoTheme;
use Magento\Mray\Package\AbstractTree\Identifier\Unidentifiable;
use Magento\Mray\Package\AbstractTree\Identifier\Versioned;
use Magento\Mray\Package\AbstractTree\Node\Identifier;
use Magento\Mray\Package\AbstractTree\Node\Node;

class Formatter implements NodeVisitor
{
    /**
     * @var array[]
     */
    private $output = [
        'modules' => [],
        'libraries' => [],
        'themes' => [],
    ];

    /**
     * @return array[]
     */
    public function getOutput(): array
    {
        return $this->output;
    }

    /**
     * @param Node $node
     * @return null
     */
    public function enterNode(Node $node)
    {
        return null;
    }

    /**
     * @param Node $node
     * @return null
     */
    public function leaveNode(Node $node)
    {
        $data = $this->formatNode($node);
        if (!$data) {
            return null;
        }

        if ($node instanceof MagentoModule) {
            $this->output['modules'][] = $data;
        } elseif ($node instanceof MagentoLibrary) {
            $this->output['libraries'][] = $data;
        } elseif ($node instanceof MagentoTheme) {
            $this->output['themes'][] = $data;
        }

        return null;
    }

    /**
     * @param Node $node
     * @return array|null
     */
    private function formatNode(Node $node): ?array
    {
        if (!$node instanceof MagentoModule &&
            !$node instanceof MagentoLibrary &&
            !$node instanceof MagentoTheme
        ) {
            return null;
        }

        return [
            'name' => $this->unversionedIdentifier($node->identifier),
            'package' => $this->unversionedIdentifier($node->getAttribute('distributionPackage')),
            'version' => $node->identifier instanceof Versioned ? (string)$node->identifier->version : null,
            'path' => $node->getAttribute('path'),
            'dependencies' => $node->getAttribute('dependencies') ?: [],
        ];
    }

    /**
     * @param Identifier|null $identifier
     * @return string|null
     */
    private function unversionedIdentifier(?Identifier $identifier): ?string
    {
        if (!$identifier || $identifier instanceof Unidentifiable) {
            return null;
        }
        if ($identifier instanceof Versioned) {
            return (string)$identifier->identifier;
        }
        return (string)$identifier;
    }
}
