<?php

class ComplianceImport
{
    private $rulesModel;
    private $reportModel;
    private $userName;
    private $inventoryVars;
    private $duplicateItems = ['conditions' => [], 'reports' => []];

    public function __construct(rules_model $rulesModel, Search_save_model $reportModel, array $inventoryVars, string $userName)
    {
        $this->rulesModel = $rulesModel;
        $this->reportModel = $reportModel;
        $this->userName = $userName;
        $this->inventoryVars = $inventoryVars;
    }

    public function import(array $data, bool $overwrite, bool $public, string $source = '')
    {
        if (!isset($data['reports']) || empty($data['reports']) || !array($data['reports'])) {
            throw new Exception('Import data missing reports');
        }

        if (!isset($data['conditions']) || empty($data['conditions']) || !array($data['conditions'])) {
            throw new Exception('Import data missing conditions');
        }

        $reportConditions = array_reduce($data['reports'], function($acc, $r) { return array_merge($acc, $r['conditions']); }, []);
        $filteredConditions = array_filter($data['conditions'], function($cond) use($reportConditions) { return in_array($cond['id'], $reportConditions); });

        $processedConditions = $this->processConditions($filteredConditions, $overwrite) ?? [];
        $processedReports = $this->processReports($data['reports'], $processedConditions, $overwrite, $public, $source) ?? [];
        $return = [
            'processed-conditions' => $processedConditions,
            'processed-reports' => $processedReports
        ];
        if (!empty($this->duplicateItems['conditions']) || !empty($this->duplicateItems['reports'])) {
            $return['duplicates'] = $this->duplicateItems;
        }
        return $return;
    }

    private function processConditions(array $conditions, $overwrite)
    {
        return array_reduce($conditions, function ($reducer, $condition) use ($overwrite) {
            $this->validateCondition($condition);
            $ruleToSave = [
                'username' => $this->userName,
                'name' => $condition['name'],
                'description' => $condition['description'],
                'type' => $condition['type'],
                'category' => $condition['category'],
                'conditionMustBeMet' => $condition['condition_for'] == 'passing' ? 'true' : 'false',
                'severity' => $condition['severity'],
                'hostcontexts' => json_encode(
                    $condition['host_filter'] ?
                        ['name' => $condition['host_filter'], 'includes' => [$condition['host_filter']]] :
                        ['name' => 'All hosts', 'includes' => []]
                ),
                'export_id' => $condition['id'],
                'rules' => $condition['rules']
            ];

            $setter = $this->getConditionSetter($condition['type']);
            $setter->setCondition($ruleToSave, $this->inventoryVars);
            unset($ruleToSave['rules']);
            try {
                if ($savedRule = $this->rulesModel->saverule($ruleToSave, $overwrite)) {
                    $reducer[$condition['id']] = $savedRule->id;
                    return $reducer;
                } else {
                    $this->duplicateItems['conditions'][] = $ruleToSave['name'];
                }
            } catch (Exception $e) {
                // 23505 error - unique_violation
                if (strstr($e->getMessage(), 'SQLSTATE[23505]')) {
                    $this->duplicateItems['conditions'][] = $ruleToSave['name'];
                } else {
                    throw $e;
                }
            }

        }, []);
    }

    private function validateCondition($condition)
    {
        $required = ['name', 'type', 'category', 'condition_for', 'severity', 'id', 'rules'];
        $missing = array_diff($required, array_keys($condition));
        if (sizeof($missing) > 0) {
            throw new Exception('Required condition fields are missing: ' . implode(', ', $missing));
        }
    }

    private function getConditionSetter(string $type)
    {
        $class = ucfirst($type) . 'ConditionSetter';
        $file = __DIR__ . '/implementations/' . $class . '.php';
        if (file_exists($file) && include_once $file) {
            return new $class();
        } else {
            throw new Exception('Class ' . $class . ' not found.');
        }
    }

    private function processReports(array $reports, array $processedConditions, $overwrite, $public, $source)
    {
        return array_reduce($reports, function ($reducer, $report) use ($processedConditions, $overwrite, $public, $source) {
            $this->validateReport($report);
            $reportToSave = [
                'username' => $this->userName,
                'export_id' => $report['id'],
                'type' => $report['type'],
                'label' => $report['title'],
                'url' => 'advancedreports',
                'reportType' => 'Advanced reports',
                'reportCategory' => 'compliance_report',
                'params' => '',
                'date' => time(),
                'is_public' => $public,
                'advancedreportsdata' => [
                    'SQL' => [
                        'SQLSTRING' => '',
                        'conditions' => array_reduce($report['conditions'], function ($reducer, $condition) use ($processedConditions) {
                            if (isset($processedConditions[$condition])) {
                                $reducer[] = $processedConditions[$condition];
                            }
                            return $reducer;
                        }, [])
                    ]
                ],
                'meta_data' => '{"source": "'. addslashes($source) .'"}'
            ];

            try {
                if ($savedReport = $this->reportModel->insert($reportToSave, $overwrite)) {
                    $reducer[$report['id']] = $savedReport->id;
                    return $reducer;
                } else {
                    $this->duplicateItems['reports'][] = $reportToSave['label'];
                }
            } catch (Exception $e) {
                // 23505 error - unique_violation
                if (strstr($e->getMessage(), 'SQLSTATE[23505]')) {
                    $this->duplicateItems['reports'][] = $reportToSave['label'];
                } else {
                    throw $e;
                }
            }
        }, []);
    }

    private function validateReport($report)
    {
        $required = ['id', 'type', 'title', 'conditions'];
        $missing = array_diff($required, array_keys($report));
        if (sizeof($missing) > 0) {
            throw new Exception('Required report fields are missing: ' . implode(', ', $missing));
        }
    }
}
