<?php
namespace nur\config;

use PHPUnit\Framework\TestCase;

class ConfigManagerTest extends TestCase {
  private static $static;
  static function inc_static() { self::$static++; }
  private $member;
  function incMember() { $this->member++; }
  function reset() {
    self::$static = 0;
    $this->member = 0;
  }

  function testConfigurators() {
    $cm = new ConfigManager();
    $cm->addConfigurator(new class($this) {
      private static $self;
      function __construct($self) {
        self::$self = $self;
      }
      static function configure_static() {
        self::$self::inc_static();
      }
      function configureMember() {
        self::$self->incMember();
      }
    });

    $this->reset();
    $cm->resetConfiguration();
    $cm->configure();
    self::assertSame(1, self::$static);
    self::assertSame(1, $this->member);
    $cm->configure();
    self::assertSame(1, self::$static);
    self::assertSame(1, $this->member);

    $this->reset();
    $cm->resetConfiguration();
    $cm->configure(["exclude" => "static"]);
    self::assertSame(0, self::$static);
    self::assertSame(1, $this->member);
    $cm->configure();
    self::assertSame(1, self::$static);
    self::assertSame(1, $this->member);
  }

  function testIsObject() {
    $cm = new class extends ConfigManager {
      const OBJECT_PKEY_PREFIXES = [
        "x.y" => null,
      ];
    };

    self::assertFalse($cm->isObject(false, ""));
    self::assertFalse($cm->isObject(false, "x"));
    self::assertFalse($cm->isObject(false, "x.y"));
    self::assertTrue($cm->isObject(false, "x.y.z"));
    self::assertFalse($cm->isObject(false, "x.y.z.t"));
    self::assertFalse($cm->isObject(false, "x.y.z.t.w"));
  }

  function testNewObject() {
    $cm = new ConfigManager();

    $def0 = [[MyClass0::class]];
    $obj0 = $cm->newObject($def0);
    self::assertInstanceOf(MyClass0::class, $obj0);

    $def1 = [[MyClass1::class], "first", "second"];
    $obj1 = $cm->newObject($def1);
    self::assertInstanceOf(MyClass1::class, $obj1);
    self::assertSame("first", $obj1->first);
    self::assertSame("second", $obj1->second);

    $def2 = [[MyClass2::class], "first" => "a", "second" => "b"];
    $obj2 = $cm->newObject($def2);
    self::assertInstanceOf(MyClass2::class, $obj2);
    self::assertSame(["first" => "a", "second" => "b"], $obj2->options);
  }

  function testGetValue() {
    $cm = new ConfigManager();

    $cm->addConfig(DynConfigTest::class);
    $cm->addConfig(new DynConfigTest());
    self::assertSame("dyn", $cm->getValue("app.ov"));

    $cm->addConfig(StaticConfigTest::class);
    self::assertSame(1, $cm->getValue("app.dc.first"));
    self::assertSame(2, $cm->getValue("app.dc.second"));
    self::assertSame("a", $cm->getValue("app.sc.first"));
    self::assertSame("b", $cm->getValue("app.sc.second"));
    self::assertSame("static", $cm->getValue("app.ov"));
    self::assertSame(1, $cm->getValue("app.dfirst_abs"));
    self::assertSame(1, $cm->getValue("app.dfirst_rel"));
    self::assertSame(2, $cm->getValue("app.dsecond"));
    self::assertSame([5, 6, 7], $cm->getValue("app.darr567"));
    self::assertSame([3, 4, 5], $cm->getValue("app.darr345"));
    self::assertSame([1, 2], $cm->getValue("app.d12"));

    putenv('CONFIG_ALL_app__ec__first=Z');
    putenv('JSON_CONFIG_ALL_app__ec__array={"a":1,"b":"2"}');
    $cm->addConfig(EnvConfig::class);
    self::assertSame("Z", $cm->getValue("app.ec.first"));
    self::assertSame(["a" => 1, "b" => "2"], $cm->getValue("app.ec.array"));
  }
}

class MyClass0 {
  function __construct() {
  }
}
class MyClass1 {
  public $first;
  public $second;

  function __construct($first, $second) {
    $this->first = $first;
    $this->second = $second;
  }
}
class MyClass2 {
  public $options;

  function __construct(array $options) {
    $this->options = $options;
  }
}

class DynConfigTest extends ArrayConfig {
  function APP(): array {
    return [
      "dc" => [
        "first" => 1,
        "second" => 2,
      ],
      "ov" => "dyn",
      "arr" => [5],
      "dfirst_abs" => [["ref"], "app.dc.first"],
      "dfirst_rel" => [["ref"], ".dc.first"],
      "darr567" => [["aref"], ".arr", [6, 7]],
    ];
  }
}
class StaticConfigTest {
  const APP = [
    "sc" => [
      "first" => "a",
      "second" => "b",
    ],
    "ov" => "static",
    "dsecond" => [["ref"], ".dc.second"],
    "darr345" => [["pref"], ".arr", [3, 4]],
    "d12" => [["refs"], ".dc", "first", "second"],
  ];
}