#!/usr/bin/env php
<?php

use Symfony\Component\Yaml\Yaml;

define('ROOT', dirname(__DIR__));

require ROOT . '/vendor/autoload.php';

const types = [
    'blob'         => '',
    'bool'         => 'bool',
    'char'         => 'string',
    'datetime'     => '\DateTimeInterface',
    'timestamp'    => '\DateTimeInterface',
    'html'         => 'string',
    'int'          => 'int',
    'numeric'      => 'float',
    'serial'       => 'int',
    'text'         => 'string',
    'varchar'      => 'string',
    'phone_number' => 'PhoneNumber',
];

$files = array_merge(glob(ROOT . '/src/Entity/*.php'),
                     array_merge(glob(ROOT . '/components/*/Entity/*.php'),
                                 glob(ROOT . '/plugins/*/Entity/*.php')));

$classes = [];
$nullable_no_defaults_warning = [];

foreach ($files as $file) {

    require_once $file;

    $declared = get_declared_classes();
    foreach ($declared as $dc) {
        if (preg_match('/(App|(Component|Plugin)\\\\[^\\\\]+)\\\\Entity/', $dc) && !in_array($dc, $classes)) {
            $class = $dc;
            $classes[] = $class;
            break;
        }
    }

    $no_ns_class  = preg_replace('/.*?\\\\/', '', $class);
    $schema       = $class::schemaDef();
    $fields       = array_keys($schema['fields']);
    $fields_code  = [];
    $methods_code = [];
    foreach ($fields as $field) {
        $field_schema   = $schema['fields'][$field];
        $nullable       = ($field_schema['not null'] ?? false) ? '' : '?';
        $type           = types[$field_schema['type']];
        $type           = $type !== '' ? $nullable . $type : $type;
        $method_name    = str_replace([' ', 'actor'], ['', 'Actor'], ucwords(str_replace('_', ' ', $field)));
        $length         = $field_schema['length'] ?? null;

        $field_setter = "\${$field}";
        if (\is_int($length)) {
            if ($nullable === '?') {
                $field_setter = "\is_null(\${$field}) ? null : \mb_substr(\${$field}, 0, $length)";
            } else {
                $field_setter = "\mb_substr(\${$field}, 0, $length)";
            }
        }

        if (($nullable === '?' || \array_key_exists('default', $field_schema)) && $type != '\DateTimeInterface') {
            if (!\array_key_exists('default', $field_schema)) {
                $nullable_no_defaults_warning[] = "{$class}::{$field}";
            }
            $default = $field_schema['default'] ?? null;
            if (\is_string($default)) {
                $default = "'{$default}'";
            } elseif (\is_null($default)) {
                $default = "null";
            } elseif ($type === 'bool' || $type === '?bool') {
                $default = $default ? 'true' : 'false';
            }

            $fields_code[]  = "    private {$type} \${$field} = {$default};";
        } else {
            $fields_code[]  = "    private {$type} \${$field};";
        }

        $methods_code[] = "    public function set{$method_name}({$type} \${$field}): self" .
                        "\n    {\n        \$this->{$field} = {$field_setter};\n        return \$this;\n    }" . "\n\n" .
                        "    public function get{$method_name}()" . ($type !== '' ? ": {$type}" : '') .
                        "\n    {\n        return \$this->{$field};\n    }" . "\n";
    }

    $fields_code  = implode("\n", $fields_code);
    $methods_code = implode("\n", $methods_code);

    $begin = '// {{{ Autocode';
    $end   = '// }}} Autocode';
    $code  = "
    {$begin}
    // @codeCoverageIgnoreStart
{$fields_code}

{$methods_code}
    // @codeCoverageIgnoreEnd
    {$end}";

    foreach (['/\\//' => '\\/', '/ /' => '\\ '] as $from => $to) {
        $begin = preg_replace($from, $to, $begin);
        $end   = preg_replace($from, $to, $end);
    }

    $in_file  = file_get_contents($file);
    $out_file = preg_replace("%\\s*{$begin}.*{$end}%smu", $code, $in_file);
    file_put_contents($file, $out_file);
}

if (!empty($nullable_no_defaults_warning)) {
    echo "Warning: The following don't have a default value, but we're assigning it `null`. Doctrine might not like this, so update it\n";
    foreach ($nullable_no_defaults_warning as $n) {
        echo "    {$n}\n";
    }
}
