package com.airlock.iam.system.config;

import java.util.Map;
import java.util.Optional;
import java.util.regex.Pattern;
import org.snakeyaml.engine.v2.api.ConstructNode;
import org.snakeyaml.engine.v2.constructor.ConstructScalar;
import org.snakeyaml.engine.v2.constructor.ConstructYamlNull;
import org.snakeyaml.engine.v2.exceptions.ConstructorException;
import org.snakeyaml.engine.v2.nodes.Node;
import org.snakeyaml.engine.v2.nodes.Tag;
import org.snakeyaml.engine.v2.resolver.CoreScalarResolver;
import org.snakeyaml.engine.v2.resolver.ScalarResolver;
import org.snakeyaml.engine.v2.schema.Schema;

public class IamYamlSchema implements Schema {
   public ScalarResolver getScalarResolver() {
      return new CoreScalarResolver(false);
   }

   public Map<Tag, ConstructNode> getSchemaTagConstructors() {
      return Map.of(
         Tag.NULL,
         new ConstructYamlNull(),
         Tag.INT,
         new ConstructScalar() {
            private static final Pattern SIMPLE_INT_PATTERN = Pattern.compile("-?[1-9][0-9]*|0");

            public Object construct(Node node) {
               String stringValue = this.constructScalar(node);
               if (!SIMPLE_INT_PATTERN.matcher(stringValue).matches()) {
                  throw new ConstructorException(
                     null,
                     Optional.empty(),
                     "integer value '%s' not accepted since it might be confused with a string, please quote the value".formatted(stringValue),
                     node.getStartMark()
                  );
               } else {
                  return stringValue;
               }
            }
         },
         Tag.FLOAT,
         new ConstructScalar() {
            private static final Pattern SIMPLE_FLOAT_PATTERN = Pattern.compile("-?(?:[1-9][0-9]*|0)\\.[0-9]+");

            public Object construct(Node node) {
               String stringValue = this.constructScalar(node);
               if (!SIMPLE_FLOAT_PATTERN.matcher(stringValue).matches()) {
                  throw new ConstructorException(
                     null,
                     Optional.empty(),
                     "float value '%s' not accepted since it might be confused with a string, please quote the value".formatted(stringValue),
                     node.getStartMark()
                  );
               } else {
                  return stringValue;
               }
            }
         },
         Tag.BOOL,
         new ConstructScalar() {
            private static final Pattern SIMPLE_BOOLEAN_PATTERN = Pattern.compile("true|false");

            public Object construct(Node node) {
               String stringValue = this.constructScalar(node);
               if (!SIMPLE_BOOLEAN_PATTERN.matcher(stringValue).matches()) {
                  throw new ConstructorException(
                     null,
                     Optional.empty(),
                     "boolean value '%s' not accepted since it might be confused with a string, please quote the value".formatted(stringValue),
                     node.getStartMark()
                  );
               } else {
                  return stringValue;
               }
            }
         },
         Tag.SET,
         new ConstructNode() {
            public Object construct(Node node) {
               throw new ConstructorException(null, Optional.empty(), "unordered set is not supported", node.getStartMark());
            }
         },
         Tag.ENV_TAG,
         new ConstructScalar() {
            public Object construct(Node node) {
               throw new ConstructorException(null, Optional.empty(), "!ENV_VARIABLE is not supported", node.getStartMark());
            }
         }
      );
   }
}
