1:   2:   3:   4:   5:   6:   7:   8:   9:  10:  11:  12:  13:  14:  15:  16:  17:  18:  19:  20:  21:  22:  23:  24:  25:  26:  27:  28:  29:  30:  31:  32:  33:  34:  35:  36:  37:  38:  39:  40:  41:  42:  43:  44:  45:  46:  47:  48:  49:  50:  51:  52:  53:  54:  55:  56:  57:  58:  59:  60:  61:  62:  63:  64:  65:  66:  67:  68:  69:  70:  71:  72:  73:  74:  75:  76:  77:  78:  79:  80:  81:  82:  83:  84:  85:  86:  87:  88:  89:  90:  91:  92:  93:  94:  95:  96:  97:  98:  99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 
<?php declare(strict_types=1);

/**
 * Copyright (C) Apis Networks, Inc - All Rights Reserved.
 *
 * Unauthorized copying of this file, via any medium, is
 * strictly prohibited without consent. Any dissemination of
 * material herein is prohibited.
 *
 * For licensing inquiries email <licensing@apisnetworks.com>
 *
 * Written by Matt Saladna <matt@apisnetworks.com>, July 2018
 */

namespace Module\Support;
use Module_Skeleton;
use Opcenter\Dns\Record;

abstract class Dns extends Module_Skeleton implements \Opcenter\Contracts\Hookable, \Module\Skeleton\Contracts\Proxied
{
    const DEPENDENCY_MAP = [
        'siteinfo'
    ];
    // @var Record
    protected $zoneCache = [];

    /**
     * Add record to cache
     *
     * @param \Opcenter\Dns\Record $r
     */
    protected function addCache(\Opcenter\Dns\Record $r): void
    {
        $key = $this->getCacheKey($r);
        array_set(
            $this->zoneCache[$r->getZone()],
            $key,
            array_merge(array_get($this->zoneCache[$r->getZone()], $key, []), [$r])
        );
    }

    protected function removeCache(\Opcenter\Dns\Record $r): void
    {
        $key = $this->getCacheKey($r);
        if (!array_has($this->zoneCache[$r->getZone()], $key)) {
            return;
        }
        $records = array_get($this->zoneCache[$r->getZone()], $key);
        if (!$records) {
            // empty cache
            array_forget(
                $this->zoneCache[$r->getZone()],
                $key
            );
            return;
        }
        array_walk_recursive($records, static function (Record $v) use ($r) {
            // soft delete record forcing cache update next modification
            if ($v->is($r)) {
                $v->setMeta('id', null);
            }
        });
        array_set(
            $this->zoneCache[$r->getZone()],
            $key,
            $records
        );
    }

    protected function getCacheKey(\Opcenter\Dns\Record $r): string
    {
        if (!isset($this->zoneCache[$r->getZone()])) {
            $this->zoneAxfr($r->getZone());
        }

        return 'records.' . strtoupper($r['rr']) . '.' . ($r['name'] ?: '@');
    }

    /**
     * Get internal CF zone ID
     *
     * @param string $domain
     * @return null|string
     */
    protected function getZoneId(string $domain): ?string
    {
        return $domain;
    }

    protected function getRecordId(\Opcenter\Dns\Record $r): ?string
    {
        // @var \Opcenter\Dns\Record[] $cache
        $idx = $this->getCacheKey($r);
        if (null === ($cache = array_get($this->zoneCache[$r->getZone()], $idx))) {
            return null;
        }

        $id = false;

        foreach ($cache as $record => $c) {
            // @var \Opcenter\Dns\Record $r
            if (\is_array($c)) {
                // nested records e.g. foo.bar.baz.com while examining bar.baz.com
                continue;
            }
            if ($c->is($r)) {
                $id = $c->getMeta('id');
                break;
            }
        }
        if ($id === null) {
            // soft deletion
            unset($this->zoneCache[$r->getZone()]);
            return $this->getRecordId($r);
        }
        return (string)$id ?: null;
    }

    protected function getRecordFromCache(Record $r): ?Record {
        $key = $this->getCacheKey($r);
        if (!isset($this->zoneCache[$r->getZone()])) {
            return null;
        }
        return collect(array_get($this->zoneCache[$r->getZone()], $key, null))->flatten()->first();
    }
}