<?php

namespace idoit\Module\Forms\Model\Validators\Category;

use idoit\Component\Helper\Ip;
use idoit\Component\Property\Property;
use idoit\Module\Forms\Model\Validators\GenericValidator;
use idoit\Module\Forms\Model\Validators\ValidatorInterface;
use isys_cmdb_dao_category;
use isys_tenantsettings;

class Ipv4Validator extends AbstractCategoryValidator implements ValidatorInterface
{
    public const CATEGORY_CONST = 'C__CATG__IP__IPV4';

  /**
   * @param array                  $formData
   * @param isys_cmdb_dao_category $categoryDao
   *
   * @return array|null
   */
    public function validate(array $formData, isys_cmdb_dao_category $categoryDao): ?array
    {
        $validationIssues = (GenericValidator::factory())->validate($formData, $categoryDao);

        if ($validationIssues === null) {
            $validationIssues = [];
        }

      // Unset hostaddress because either ipv4_address or ipv6_address will be used
        if (is_array($validationIssues) && isset($validationIssues['hostaddress'])) {
            unset($validationIssues['hostaddress']);
        }

      // Specific rules for net objects.
        if (isset($formData['net']) && $formData['net'] !== '' && is_numeric($formData['net'])) {
            $categoryDao->validateNetObject($formData['net'], $validationIssues);
        }

      // Specific rules for IPv4 addresses.
        if (isset($formData['ipv4_address']) && $formData['ipv4_address'] !== '') {
            $this->validateIpAddress('ipv4_address', $formData, $validationIssues, $categoryDao);
        }

        if (isset($formData['net_type'])) {
            if ($formData['net_type'] == defined_or_default('C__CATS_NET_TYPE__IPV6')) {
                unset($validationIssues['ipv4_address']);
            } else {
                unset($validationIssues['ipv6_address']);
            }
        }

        if (!empty($validationIssues)) {
            return $validationIssues;
        }

        return null;
    }

    /**
     * @param                        $ipType
     * @param                        $ipData
     * @param                        $errors
     * @param isys_cmdb_dao_category $categoryDao
     *
     * @return void
     * @throws \isys_exception_database
     *
   */
    private function validateIpAddress($ipType, &$ipData, &$errors, isys_cmdb_dao_category $categoryDao): void
    {
        unset($ipData['ipv6_address'], $errors['ipv6_address']);
        $globalNetObject = defined_or_default('C__OBJ__NET_GLOBAL_IPV4');

        $ipAddress = $ipData[$ipType];
        $netObject = $globalNetObject;
        $message = null;
        $sqlConditions = null;

        if (isset($ipData['net'])) {
            $netObject = $ipData['net'];
        }

        if (!empty($ipAddress)) {
            if (!Ip::validate_ip($ipAddress)) {
                $errors[$ipType] = $this->language->get('LC__CMDB__CATS__NET_IP_ADDRESSES__IP_INVALID');
            } elseif (isys_tenantsettings::get('cmdb.unique.ip-address', 1)) {
                $l_catLevel = $categoryDao->in_use($netObject, $ipAddress, 0);

                if ($l_catLevel) {
                    $l_row = $categoryDao->get_data($l_catLevel)
                    ->get_row();

                    $errors[$ipType] = $this->language->get('LC__CATG__IP__UNIQUE_IP_WARNING',
                            [$l_row['isys_obj__title'], $l_row['isys_obj__id']]);
                }
            }

          // @see  ID-6570  Fixing the validation for IPv4 and IPv6.
            if (!isset($errors[$ipType])) {
                $properties = $categoryDao->get_properties(C__PROPERTY__WITH__VALIDATION);
                $property = $properties[$ipType];

                if (is_array($property)) {
                    $property = Property::createInstanceFromArray($property);
                }

                $ipCheck = $property->getCheck();

                $sqlBase = 'SELECT isys_catg_ip_list__id AS id, isys_obj__id AS objId, isys_obj__title AS objTitle, isys_obj_type__title AS objTypeTitle
                    FROM isys_cats_net_ip_addresses_list
                    INNER JOIN isys_catg_ip_list ON isys_catg_ip_list__isys_cats_net_ip_addresses_list__id = isys_cats_net_ip_addresses_list__id
                    INNER JOIN isys_obj ON isys_obj__id = isys_catg_ip_list__isys_obj__id
                    INNER JOIN isys_obj_type ON isys_obj_type__id = isys_obj__isys_obj_type__id
                    WHERE isys_cats_net_ip_addresses_list__title = ' . $categoryDao->convert_sql_text($ipAddress) . '
                    AND isys_obj__status = ' . $categoryDao->convert_sql_int(C__RECORD_STATUS__NORMAL) . '
                    AND isys_catg_ip_list__status = ' . $categoryDao->convert_sql_int(C__RECORD_STATUS__NORMAL) . '
            AND isys_cats_net_ip_addresses_list__status = ' . $categoryDao->convert_sql_int(C__RECORD_STATUS__NORMAL) .
                    ' ';

                if ($ipCheck->getUniqueObjectType() && $categoryDao->get_object_type_id()) {
                    $message = 'LC__SETTINGS__CMDB__VALIDATION_MESSAGE__UNIQUE_OBJTYPE';
                    $sqlConditions = "AND isys_obj__isys_obj_type__id = {$categoryDao->convert_sql_id($categoryDao->get_object_type_id())};";
                }

                if ($ipCheck->getUniqueObject() && $categoryDao->get_object_id()) {
                    $message = 'LC__SETTINGS__CMDB__VALIDATION_MESSAGE__UNIQUE_OBJ';
                    $sqlConditions = "AND isys_obj__id = {$categoryDao->convert_sql_id($categoryDao->get_object_id())};";
                }

                if ($ipCheck->getUniqueObjectType() && $categoryDao->get_object_type_id()) {
                    $message = 'LC__SETTINGS__CMDB__VALIDATION_MESSAGE__UNIQUE_OBJTYPE';
                    $sqlConditions = "AND isys_obj__isys_obj_type__id = {$categoryDao->convert_sql_id($categoryDao->get_object_type_id())};";
                }

                if ($ipCheck->getUniqueObject() && $categoryDao->get_object_id()) {
                    $message = 'LC__SETTINGS__CMDB__VALIDATION_MESSAGE__UNIQUE_OBJ';
                    $sqlConditions = "AND isys_obj__id = {$categoryDao->convert_sql_id($categoryDao->get_object_id())};";
                }

                if ($ipCheck->getUniqueGlobal()) {
                    $message = 'LC__SETTINGS__CMDB__VALIDATION_MESSAGE__UNIQUE_GLOBAL';
                    $sqlConditions = ';';
                }

                if ($sqlConditions !== null && $message !== null) {
                    $result = $categoryDao->retrieve($sqlBase . $sqlConditions);

                    if ($result !== false && is_countable($result) && count($result)) {
                        $objects = [];

                        while ($objectRow = $result->get_row()) {
                            $objects[] = $this->language->get($objectRow['objTypeTitle']) . ' > ' .
                                $objectRow['objTitle'];
                        }

                        // Remove duplicates
                        $objects = array_unique($objects);

                        if ($objectCount = count($objects)) {
                            if ($objectCount > $categoryDao::UNIQUE_VALIDATION_OBJECT_COUNT) {
                                $objects = array_slice($objects, 0, $categoryDao::UNIQUE_VALIDATION_OBJECT_COUNT);
                                $objects[] = $this->language
                                ->get(
                                    'LC__SETTINGS__CMDB__VALIDATION_MESSAGE__UNIQUE_AND_MORE',
                                    ($objectCount - $categoryDao::UNIQUE_VALIDATION_OBJECT_COUNT)
                                );
                            }

                            $errors[$ipType] = $this->language->get($message) . implode(', ', $objects);
                        }
                    }
                }
            }
        }
    }
}
