package com.airlock.iam.servicecontainer.app.application.service.radius;

import com.airlock.iam.servicecontainer.app.application.service.radius.wrapper.lib.IamRadiusServerAttribute;
import com.airlock.iam.servicecontainer.app.application.service.radius.wrapper.lib.NameValueResolved;
import com.google.common.base.Preconditions;
import com.google.common.io.BaseEncoding;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.time.DateFormatUtils;

public enum RadiusAttributeValueSetter {
   STRING_UTF8 {
      @Override
      public boolean trySetAttributeValue(IamRadiusServerAttribute attribute, Object obj, Function<String, NameValueResolved> toNamedValue) {
         Preconditions.checkNotNull(attribute);
         if (obj == null) {
            return false;
         }

         attribute.value(Objects.toString(obj, ""));
         return true;
      }

      @Override
      public String asReadableString(byte[] bytes, Function<Long, String> toNameFromValue) {
         return bytes == null ? "" : new String(bytes, StandardCharsets.UTF_8);
      }
   },
   INTEGER {
      @Override
      public boolean trySetAttributeValue(IamRadiusServerAttribute attribute, Object obj, Function<String, NameValueResolved> toNamedValue) {
         Preconditions.checkNotNull(attribute);
         if (obj == null) {
            return false;
         }

         if (obj instanceof Number) {
            attribute.value(((Number)obj).longValue());
            return true;
         }

         try {
            attribute.value(Long.parseLong(Objects.toString(obj, "")));
            return true;
         } catch (NumberFormatException e) {
            return false;
         }
      }

      @Override
      public String asReadableString(byte[] bytes, Function<Long, String> toNameFromValue) {
         if (bytes != null && bytes.length != 0) {
            ByteBuffer buffer = ByteBuffer.allocate(bytes.length).put(bytes).flip();
            return String.valueOf(buffer.getInt());
         } else {
            return "";
         }
      }
   },
   OCTETS {
      @Override
      public boolean trySetAttributeValue(IamRadiusServerAttribute attribute, Object obj, Function<String, NameValueResolved> toNamedValue) {
         Preconditions.checkNotNull(attribute);
         if (obj == null) {
            return false;
         } else if (obj instanceof byte[]) {
            attribute.value(obj);
            return true;
         } else {
            String stringVal = Objects.toString(obj, "");
            if (Base64.isBase64(stringVal.getBytes(StandardCharsets.UTF_8))) {
               attribute.value(Base64.decodeBase64(stringVal));
               return true;
            } else {
               return false;
            }
         }
      }

      @Override
      public String asReadableString(byte[] bytes, Function<Long, String> toNameFromValue) {
         return bytes != null && bytes.length != 0 ? "0x" + BaseEncoding.base16().encode(bytes) : "";
      }
   },
   NAMED_VALUE {
      @Override
      public boolean trySetAttributeValue(IamRadiusServerAttribute attribute, Object obj, Function<String, NameValueResolved> toNamedValue) {
         Preconditions.checkNotNull(attribute);
         if (obj == null) {
            return false;
         }

         String name = Objects.toString(obj, "");
         NameValueResolved resolved = toNamedValue.apply(name);
         if (!resolved.didSomeWork()) {
            attribute.value(name);
         } else {
            Optional<Long> resolvedValue = resolved.resolvedValue();
            if (resolvedValue.isEmpty()) {
               return false;
            }

            attribute.value(resolvedValue.get());
         }

         return attribute.value().bytes().length != 0;
      }

      @Override
      public String asReadableString(byte[] bytes, Function<Long, String> toNameFromValue) {
         if (bytes != null && bytes.length != 0) {
            ByteBuffer buffer = ByteBuffer.allocate(bytes.length).put(bytes).flip();
            long value = buffer.getInt();
            String name = toNameFromValue.apply(value);
            return name + " (" + value + ")";
         } else {
            return "";
         }
      }
   },
   DATE {
      @Override
      public boolean trySetAttributeValue(IamRadiusServerAttribute attribute, Object obj, Function<String, NameValueResolved> toNamedValue) {
         Preconditions.checkNotNull(attribute);
         if (obj == null) {
            return false;
         } else if (obj instanceof Date) {
            attribute.value(((Date)obj).getTime() / 1000L);
            return true;
         } else if (obj instanceof Number) {
            attribute.value(((Number)obj).longValue());
            return true;
         } else {
            return false;
         }
      }

      @Override
      public String asReadableString(byte[] bytes, Function<Long, String> toNameFromValue) {
         if (bytes != null && bytes.length != 0) {
            ByteBuffer buffer = ByteBuffer.allocate(bytes.length).put(bytes).flip();
            return DateFormatUtils.format(buffer.getInt() * 1000L, "yyyy.MM.dd HH:mm:ss");
         } else {
            return "";
         }
      }
   },
   IPV4_ADDRESS {
      @Override
      public boolean trySetAttributeValue(IamRadiusServerAttribute attribute, Object obj, Function<String, NameValueResolved> toNamedValue) {
         Preconditions.checkNotNull(attribute);
         if (obj == null) {
            return false;
         }

         if (obj instanceof InetAddress) {
            byte[] result = ((InetAddress)obj).getAddress();
            if (result.length == 4) {
               attribute.value(result);
               return true;
            }
         } else if (obj instanceof byte[] bVal) {
            if (bVal.length == 4) {
               attribute.value(bVal);
               return true;
            }
         } else {
            String name = Objects.toString(obj, "");

            try {
               InetAddress address = InetAddress.getByName(name);
               byte[] addrBytes = address.getAddress();
               if (addrBytes.length == 4) {
                  attribute.value(addrBytes);
                  return true;
               }
            } catch (UnknownHostException e) {
               return false;
            }
         }

         return false;
      }

      @Override
      public String asReadableString(byte[] bytes, Function<Long, String> toNameFromValue) {
         if (bytes != null && bytes.length == 4) {
            try {
               return InetAddress.getByAddress(bytes).getHostAddress();
            } catch (UnknownHostException e) {
               return "";
            }
         } else {
            return "";
         }
      }
   },
   IPV6_ADDRESS {
      @Override
      public boolean trySetAttributeValue(IamRadiusServerAttribute attribute, Object obj, Function<String, NameValueResolved> toNamedValue) {
         Preconditions.checkNotNull(attribute);
         if (obj == null) {
            return false;
         }

         if (obj instanceof InetAddress) {
            byte[] result = ((InetAddress)obj).getAddress();
            if (result.length == 16) {
               attribute.value(result);
               return true;
            }
         } else if (obj instanceof byte[] bVal) {
            if (bVal.length == 16) {
               attribute.value(bVal);
               return true;
            }
         } else {
            String name = Objects.toString(obj, "");

            try {
               InetAddress address = InetAddress.getByName(name);
               byte[] addrBytes = address.getAddress();
               if (addrBytes.length == 16) {
                  attribute.value(addrBytes);
                  return true;
               }
            } catch (UnknownHostException e) {
               return false;
            }
         }

         return false;
      }

      @Override
      public String asReadableString(byte[] bytes, Function<Long, String> toNameFromValue) {
         if (bytes != null && bytes.length == 16) {
            try {
               return InetAddress.getByAddress(bytes).getHostAddress();
            } catch (UnknownHostException e) {
               return "";
            }
         } else {
            return "";
         }
      }
   };

   public abstract boolean trySetAttributeValue(IamRadiusServerAttribute var1, Object var2, Function<String, NameValueResolved> var3);

   public abstract String asReadableString(byte[] var1, Function<Long, String> var2);
}
