<?php

namespace PriaxSync\Sync;

use PriaxSync\Priax;
use PriaxSync\OpMon;
use PriaxSync\Reducers\Reducer;
use PriaxSync\Change;
use PriaxSync\Sync\Sync;

/**
 * Class Incremental
 */
final class Incremental
{
    private $priax;

    private $opmon;

    private $reducer;

    private $logger;

    private $changesApplied;

    /**
     * Constructor
     */
    public function __construct(Priax $priax, OpMon $opmon, Reducer $reducer)
    {
        $this->priax = $priax;
        $this->opmon = $opmon;
        $this->reducer = $reducer;
        $this->logger = $this->opmon->getLogger();
        $this->begintime =new \DateTime("now");
        $this->logger->info("Incremental sync starting");
        $this->changesApplied = 0;
    }

    /**
     * Constructor
     */
    public static function newInstance($priax, $opmon, $reducer)
    {
        return new self($priax, $opmon, $reducer);
    }

    public function sync($lastLog)
    {
        $this->logger->info("Running Incremental Process ...");
        $changes = array();
        $lastChangeLogProcessed = $this->getChanges($lastLog, $changes);
        $this->processChanges($changes);
        $this->opmon->apply();

        $this->begintime;
        $timeend = new \DateTime("now");
        $interval = $this->begintime->diff($timeend);
        $this->logger->info(sprintf("Incremental sync: took %s and applied %d changes", $interval->format("%H:%I:%S"), $this->changesApplied));

        return $lastChangeLogProcessed;
    }

    protected function processChanges($changes)
    {
        $addedCmdbOpMonIds = array();
        foreach ($changes as $change) {
            $this->changesApplied++;
            $this->processChange($change, $addedCmdbOpMonIds);
        }

        /* Do the parent links if new ICs were added */
        if (count($addedCmdbOpMonIds) > 0) {
            $full = Full::newInstance($this->priax, $this->opmon, $this->reducer, true);
            $full->addRelationships($addedCmdbOpMonIds);
            unset($full);
        }
    }

    protected function processChange($change, &$addedCmdbOpMonIds)
    {
        if ($change->isDelete()) {
            return $this->opmon->deleteByCmdbId($change->getItemId(), $change->isBeingType());
        }

        if ($change->isBeingType()) {
            $ic = $this->reducer->reduceItem($this->priax->getIcById($change->getItemId()));
            $opmonid = $this->opmon->saveIC($ic);
            if ($opmonid === false) {
                $this->logger->error('IC: Missing host_id - probably cloud not add IC to OpMon\n'.var_export($ic, true));
                return false;
            }
            $this->priax->setICOpmonID($ic['cmdb_id'], $opmonid);
            $addedCmdbOpMonIds[] = $opmonid;
            return $opmonid;
        }
        $icOpMonId = $this->priax->getIcOpMonIdbyKpiId($change->getItemId());
        return $this->savePriaxKpi($icOpMonId, $change);
    }

    protected function savePriaxKpi($icOpMonId, $change)
    {
        $kpi = $this->reducer->reduceItem($this->priax->getKpiById($change->getItemId()));
        $this->opmon->saveKPI($icOpMonId, $kpi);
    }

    protected function getChanges($lastSyncedLog, &$changesCollection)
    {
        $lastChangedLog = $this->priax->getLastChangeLogId();
        do {
            $this->logger->info("Getting incremental changelog block ... $lastLog");
            $processedId = $this->filterChanges(
                $this->priax->getChangesBlockById($lastSyncedLog),
                $changesCollection
            );
            $lastSyncedLog = $processedId;
        } while ($processedId < $lastChangedLog);

        return $processedId;
    }

    protected function filterChanges($block, &$changesCollection)
    {
        foreach ($block as $change) {
            //print "type => ".$change->type." action => ".$change->action." idChangeLog => ".$change->idChangeLog."\n";
            if (Change::isRelevant($change)) {
                //$this->logger->debug("Change info -> \n" . print_r($change, true));
                $changesCollection[$change->type . $change->id] = new Change($change);
                //var_dump($changesCollection[$change->type . $change->id]);
            }
        }
        return $change->idChangeLog;
    }
}
