<?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\CodeStructuralElement\Php\Reflection\PropertyDeclaration;

use Magento\Mray\CodeStructuralElement\Php\NodeVisitor\PhpDocAst;
use Magento\Mray\CodeStructuralElement\Php\Reflection\ClassLikeDeclaration\Finder;
use Magento\Mray\CodeStructuralElement\Php\Reflection\ClassLikeMemberPhpDocInheritance;
use Magento\Mray\CodeStructuralElement\Php\Reflection\InCodeDeclaration;
use Magento\Mray\CodeStructuralElement\Php\Reflection\PropertyDeclaration;
use Magento\Mray\CodeStructuralElement\Php\Reflection\TypeFactory;
use phpDocumentor\Reflection\Type;
use PhpParser\Node;
use PhpParser\NodeDumper;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocNode;
use function array_values;
use function sha1;
use function sprintf;

class Property implements PropertyDeclaration, InCodeDeclaration
{
    use ClassLikeMemberPhpDocInheritance;

    /** @var Node\Stmt\Property  */
    private $pGrp;
    /** @var Node\Stmt\PropertyProperty  */
    private $p;
    /** @var string|null  */
    private $classLikeName;

    /**
     * @param Node\Stmt\Property $pGrp
     * @param Node\Stmt\PropertyProperty $p
     * @param string|null $classLikeName
     * @param Finder|null $inheritedDeclarationsFinder
     */
    public function __construct(
        Node\Stmt\Property $pGrp,
        Node\Stmt\PropertyProperty $p,
        ?string $classLikeName,
        ?Finder $inheritedDeclarationsFinder = null
    ) {
        $this->pGrp = $pGrp;
        $this->p = $p;
        $this->classLikeName = $classLikeName;

        $this->inheritedDeclarationsFinder = $inheritedDeclarationsFinder;
    }

    /**
     * @return string|null
     */
    public function getName(): ?string
    {
        return $this->p->name instanceof Node\Expr ? null : (string)$this->p->name;
    }

    /**
     * @return string|null
     */
    public function getClassLikeName(): ?string
    {
        return $this->classLikeName;
    }

    /**
     * @return Type
     */
    public function getType(): Type
    {
        $typeFromAnnotation = null;

        $phpDoc = $this->getPhpDoc();
        if ($phpDoc) {
            foreach ($phpDoc->getVarTagValues() as $varTag) {
                $typeFromAnnotation = $varTag->type;
            }
        }

        return TypeFactory::preciseType(
            TypeFactory::cast($this->pGrp->type),
            TypeFactory::cast($typeFromAnnotation)
        );
    }

    /**
     * @return Type
     */
    public function getValueType(): Type
    {
        return $this->getType();
    }

    /**
     * @return bool
     */
    public function isPublic(): bool
    {
        return $this->pGrp->isPublic();
    }

    /**
     * @return bool
     */
    public function isProtected(): bool
    {
        return $this->pGrp->isProtected();
    }

    /**
     * @return bool
     */
    public function isPrivate(): bool
    {
        return $this->pGrp->isPrivate();
    }

    /**
     * @return bool
     */
    public function isStatic(): bool
    {
        return $this->pGrp->isStatic();
    }

    /**
     * @return string|null
     */
    public function getImplementationChecksum(): ?string
    {
//        return null;
        $p = clone $this->pGrp;
        foreach ($p->props as $key => $prop) {
            if ($prop !== $this->p) {
                unset($p->props[$key]);
            }
        }
        $p->props = array_values($p->props);

        $dumper = new NodeDumper();
        return sprintf('v1:sha1:%s', sha1($dumper->dump($p)));
    }

    /**
     * @inheritDoc
     */
    public function getDeclarationNodes(): array
    {
        return [
            $this->pGrp,
            $this->p
        ];
    }

    /**
     * @return PhpDocNode|null
     */
    public function getPhpDoc(): ?PhpDocNode
    {
        return $this->getPhpDocWithResolvedInheritance(
            $this->pGrp->getAttribute(PhpDocAst::PHP_DOC_COMMENT)
        );
    }
}
