<?php

namespace CleantalkSP\SpbctWP\Scanner\DBTrigger;

class DBTriggerModel
{
    /**
     * Runs the DBTrigger model to find malicious database triggers.
     * @return string|true The result of the operation or an error message.
     */
    public function run()
    {
        try {
            DBTriggerService::removeNotExistsTriggers();

            $db_triggers = $this->getDataBaseTriggers();
            $db_triggers = $this->removeSentTriggers($db_triggers);

            $signatures = $this->getSignaturesForTriggers();

            $bad_triggers = $this->analyzeDBTriggers($db_triggers, $signatures);

            DBTriggerService::saveTriggersStorage($bad_triggers);
        } catch (\Exception $error) {
            return $error->getMessage();
        }

        return true;
    }

    /**
     * Remove the triggers that have already been sent.
     *
     * @param array $db_triggers The list of triggers.
     * @return array The list of triggers without the sent ones.
     */
    private function removeSentTriggers($db_triggers)
    {
        $scanned_triggers = DBTriggerService::loadTriggersStorage();
        $scanned_triggers = array_map(function ($trigger) {
            return $trigger['name'];
        }, $scanned_triggers);
        $db_triggers = array_filter($db_triggers, function ($trigger) use ($scanned_triggers) {
            if (in_array($trigger->TRIGGER_NAME, $scanned_triggers)) {
                return false;
            }
            return true;
        });
        return $db_triggers;
    }

    /**
     * Analyze the triggers and return the bad ones.
     *
     * @param array $db_triggers The list of triggers.
     * @return array The list of bad triggers.
     */
    private function analyzeDBTriggers($db_triggers, $signatures)
    {
        global $spbc;

        $bad_triggers = array();
        $triggered_signatures = [];

        foreach ($db_triggers as $trigger) {
            $trigger_code = $trigger->ACTION_STATEMENT;
            $signature = self::isTriggerBad($trigger_code, $signatures);

            if ($signature) {
                $bad_triggers[] = array(
                    'name' => $trigger->TRIGGER_NAME,
                    'table' => $trigger->EVENT_OBJECT_TABLE,
                    'time' => $trigger->ACTION_TIMING,
                    'action' => $trigger->EVENT_MANIPULATION,
                    'code' => $trigger_code,
                    'signature' => current($signature)[0],
                    'status' => 'Vulnerable trigger',
                    'sent' => true,
                );
                $triggered_signatures[key($signature)] = current($signature);
            }
        }

        if ( count($triggered_signatures) > 0 ) {
            $signature_idx = $spbc->data['scanner']['signatures_found'];
            foreach ( $triggered_signatures as $signature_id => $_signature ) {
                $signature_idx[$signature_id] = ! empty($signature_idx[$signature_id])
                    ? (int)$signature_idx[$signature_id] + 1
                    : 1;
            }

            $spbc->data['scanner']['signatures_found'] = $signature_idx;
            $spbc->save('data');
        }

        return $bad_triggers;
    }

    /**
     * Check if the trigger is bad.
     *
     * @param string $trigger_code The code of the trigger.
     * @return bool|array The signature name if the trigger is bad, false otherwise.
     */
    private static function isTriggerBad($trigger_code, $signatures)
    {
        foreach ($signatures as $signature_id => $signature) {
            if (preg_match('/' . $signature[1] . '/is', $trigger_code)) {
                return [$signature_id => $signature];
            }
        }

        return false;
    }

    public function getDataBaseTriggers()
    {
        return DBTriggerService::getDataBaseTriggers();
    }

    public function getSignaturesForTriggers()
    {
        return DBTriggerService::getSignaturesForTriggers();
    }
}
