private function generateProxyClass(): string
    {
        $class = $this->reflectionClass;

        if ($class->isFinal()) {
            throw new \RuntimeException(sprintf(
                'Can not generate proxy for final class "%s".',
                $class->getName()
            ));
        }

        if ($class->isAbstract()) {
            throw new \RuntimeException(sprintf(
                'Can not generate proxy for abstract class "%s".',
                $class->getName()
            ));
        }

        $methodCodes = [];

        $uses = $this->getAllUseStatementsMarkedAsAnnotation($class);
        $uses[] = 'FiveLab\Component\Transactional\Proxy\ProxyInterface as FiveLabTransactionalProxyInterface';

        $uses = \array_map(function ($use) {
            return 'use '.$use.';';
        }, $uses);

        $interfaces = [
            'FiveLabTransactionalProxyInterface',
        ];

        foreach ($this->getProxyMethods() as $method) {
            $methodCodes[] = $this->generateProxyMethod($method);
        }

        $docComment = $class->getDocComment();

        $templateVariables = [
            'uses'           => \implode("\n", $uses),
            'namespace'      => $class->getNamespaceName(),
            'docComment'     => $docComment,
            'proxyClassName' => $class->getShortName().'Proxy',
            'className'      => '\\'.$class->getName(),
            'proxyMethods'   => \implode("\n\n", $methodCodes),
            'interfaces'     => \implode(', ', $interfaces),
            'realClassName'  => $class->getName(),
        ];

        $template = $this->getTemplateForProxyClass();
        $code = $this->replaceVariables($template, $templateVariables);

        $lines = \explode("\n", $code);
        $lines = \array_map('rtrim', $lines);

        return \implode("\n", $lines);
    }