<?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\CodeStructuralElement\Php\Reflection\ClassLikeDeclaration;
use Magento\Mray\CodeStructuralElement\Php\Reflection\PropertyDeclaration;
use Magento\Mray\Index\Collection\PhpCode as PhpCodeIndex;
use Magento\Mray\Package\AbstractTree\Node\Node;
use Magento\Mray\Package\AbstractTree\Node\Package;
use Magento\Mray\Package\AbstractTree\Node\PhpCode;

use function get_object_vars;
use function sprintf;

class PhpCodeDiscovery implements NodeVisitor
{
    /** @var PhpCodeIndex|null  */
    private $phpCode;

    /**
     * @param PhpCodeIndex|null $phpCode
     */
    public function __construct(?PhpCodeIndex $phpCode = null)
    {
        $this->phpCode = $phpCode;
    }

    /**
     * @inheritDoc
     */
    public function enterNode(Node $node)
    {
        if (!$node instanceof Package) {
            return null;
        }

        foreach ($this->getPhpCode($node) as $phpCode) {
            if (!$phpCode->getDeclarations()) {
                continue;
            }

            foreach ($phpCode->getDeclarations() as $ds) {
                foreach ($ds as $d) {
                    if (!$d->getName()) {
                        continue;
                    }

                    $this->phpCode->registerImplementation($d->getName(), $d);
                    if (!$d instanceof ClassLikeDeclaration) {
                        continue;
                    }

                    foreach ($d->getMembers() as $md) {
                        if (!$md->getName()) {
                            continue;
                        }

                        $mName = sprintf(
                            '%s%s',
                            $md instanceof PropertyDeclaration ? '$' : '',
                            $md->getName()
                        );
                        $fqsen = sprintf(
                            '%s::%s',
                            $d->getName(),
                            $mName
                        );
                        $this->phpCode->registerImplementation($fqsen, $md);
                    }
                }
            }
        }

        return null;
    }

    /**
     * @param Package $pkg
     * @return array
     */
    private function getPhpCode(Package $pkg): array
    {
        return array_filter(get_object_vars($pkg), function ($node) {
            return $node instanceof PhpCode;
        });
    }

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