package com.airlock.iam.oauth2.application.configuration.openid.claims;

import com.airlock.iam.oauth2.api.application.configuration.claims.ClaimConditionConfig;
import com.airlock.iam.oauth2.api.application.configuration.openid.claims.OpenIDConnectStandardClaimConfig;
import com.airlock.iam.oauth2.api.domain.model.claims.UserClaimExtractor;
import com.airlock.iam.oauth2.domain.model.claims.openid.standard.StringBirthdateClaimExtractor;
import com.airlock.iam.plugin.api.annotation.config.ConfigurationValidator;
import com.airlock.iam.plugin.api.annotation.config.Plugin;
import com.airlock.iam.plugin.api.annotation.license.PluginLicenseRestriction;
import com.airlock.iam.plugin.api.annotation.property.StringProperty;
import com.airlock.iam.plugin.api.config.ConfigurationContext;
import com.airlock.iam.plugin.api.config.ConfigurationEnvironment;
import com.airlock.iam.plugin.api.config.Environment;
import com.airlock.iam.plugin.api.config.PluginConfiguration;
import com.airlock.iam.plugin.api.exception.ConfigurationException;
import com.airlock.iam.plugin.api.exception.PropertyConstraintViolationException;
import com.airlock.iam.plugin.api.valuereader.StringPropertyValueReader;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.time.format.DateTimeFormatter;

@Plugin(
   description = "<p>The \"birthdate\" claim that will be parsed from a string context data field.</p><p>This plugin can be used for untyped string context data item (legacy). For typed date context data fields, the OIDC Birthdate Standard Claim (Date) plugin should be used</p>",
   displayName = "OIDC Birthdate Standard Claim (String)"
)
@PluginLicenseRestriction(moduleAffiliation = "OAuthServer")
public final class OpenIDConnectStringBirthdateStandardClaimConfig extends OpenIDConnectStandardClaimConfig {
   @StringProperty(
      description = "The context data field that should be included in the \"birthdate\" claim. If the context data field does not contain a value or the value could not be parsed and therefore brought to the ISO 8601 format, the value will not be included in the response.",
      exampleValues = "birthdate",
      optional = false
   )
   private String contextDataField;
   @StringProperty(
      description = "The date format of the context data field. The following letters can be used in the format.<table><thead><tr><th>Symbol</th>   <th>Meaning</th>         <th>Presentation</th> <th>Examples</th></thead><tbody><tr><th>G</th>       <td>era</td>                         <td>text</td>              <td>AD; Anno Domini; A</td><tr><th>u</th>       <td>year</td>                        <td>year</td>              <td>2004; 04</td><tr><th>y</th>       <td>year-of-era</td>                 <td>year</td>              <td>2004; 04</td><tr><th>D</th>       <td>day-of-year</td>                 <td>number</td>            <td>189</td><tr><th>M/L</th>     <td>month-of-year</td>               <td>number/text</td>       <td>7; 07; Jul; July; J</td><tr><th>d</th>       <td>day-of-month</td>                <td>number</td>            <td>10</td><tr><th>g</th>       <td>modified-julian-day</td>         <td>number</td>            <td>2451334</td><tr><th>Q/q</th>     <td>quarter-of-year</td>             <td>number/text</td>       <td>3; 03; Q3; 3rd quarter</td><tr><th>Y</th>       <td>week-based-year</td>             <td>year</td>              <td>1996; 96</td><tr><th>w</th>       <td>week-of-week-based-year</td>     <td>number</td>            <td>27</td><tr><th>W</th>       <td>week-of-month</td>               <td>number</td>            <td>4</td><tr><th>E</th>       <td>day-of-week</td>                 <td>text</td>              <td>Tue; Tuesday; T</td><tr><th>e/c</th>     <td>localized day-of-week</td>       <td>number/text</td>       <td>2; 02; Tue; Tuesday; T</td><tr><th>F</th>       <td>day-of-week-in-month</td>        <td>number</td>            <td>3</td><tr><th>a</th>       <td>am-pm-of-day</td>                <td>text</td>              <td>PM</td><tr><th>h</th>       <td>clock-hour-of-am-pm (1-12)</td>  <td>number</td>            <td>12</td><tr><th>K</th>       <td>hour-of-am-pm (0-11)</td>        <td>number</td>            <td>0</td><tr><th>k</th>       <td>clock-hour-of-day (1-24)</td>    <td>number</td>            <td>24</td><tr><th>H</th>       <td>hour-of-day (0-23)</td>          <td>number</td>            <td>0</td><tr><th>m</th>       <td>minute-of-hour</td>              <td>number</td>            <td>30</td><tr><th>s</th>       <td>second-of-minute</td>            <td>number</td>            <td>55</td><tr><th>S</th>       <td>fraction-of-second</td>          <td>fraction</td>          <td>978</td><tr><th>A</th>       <td>milli-of-day</td>                <td>number</td>            <td>1234</td><tr><th>n</th>       <td>nano-of-second</td>              <td>number</td>            <td>987654321</td><tr><th>N</th>       <td>nano-of-day</td>                 <td>number</td>            <td>1234000000</td><tr><th>V</th>       <td>time-zone ID</td>                <td>zone-id</td>           <td>America/Los_Angeles; Z; -08:30</td><tr><th>v</th>       <td>generic time-zone name</td>      <td>zone-name</td>         <td>Pacific Time; PT</td><tr><th>z</th>       <td>time-zone name</td>              <td>zone-name</td>         <td>Pacific Standard Time; PST</td><tr><th>O</th>       <td>localized zone-offset</td>       <td>offset-O</td>          <td>GMT+8; GMT+08:00; UTC-08:00</td><tr><th>X</th>       <td>zone-offset 'Z' for zero</td>    <td>offset-X</td>          <td>Z; -08; -0830; -08:30; -083015; -08:30:15</td><tr><th>x</th>       <td>zone-offset</td>                 <td>offset-x</td>          <td>+0000; -08; -0830; -08:30; -083015; -08:30:15</td><tr><th>Z</th>       <td>zone-offset</td>                 <td>offset-Z</td>          <td>+0000; -0800; -08:00</td></tbody></table>",
      suggestedValues = {"dd.MM.yyyy", "dd-MM-yyyy", "yyyy-MM-dd", "MM-dd-yyyy", "MM-dd-yyyy HH:mm:ss", "yyyy-MM-dd'T'HH:mm:ss.SSSZ"},
      optional = false
   )
   private String dateFormat;
   static final String DATA_FORMAT_FIELD = "dateFormat";

   @ConfigurationValidator
   public static void validateFormat(PluginConfiguration config, ConfigurationEnvironment env, ConfigurationContext context, Environment validationEnvironment) throws ConfigurationException {
      try {
         DateTimeFormatter.ofPattern(StringPropertyValueReader.effectiveStringConfigValue(config, "dateFormat", env, context));
      } catch (IllegalArgumentException e) {
         throw new PropertyConstraintViolationException("dateFormat", config, env, context, "Invalid date format");
      }
   }

   @Override
   public UserClaimExtractor<?> logic() {
      return StringBirthdateClaimExtractor.builder()
         .contextDataField(this.contextDataField)
         .format(DateTimeFormatter.ofPattern(this.dateFormat))
         .condition(this.getClaimCondition())
         .build();
   }

   public OpenIDConnectStringBirthdateStandardClaimConfig(String contextDataField, String dateFormat, ClaimConditionConfig claimCondition) {
      super(claimCondition);
      this.contextDataField = contextDataField;
      this.dateFormat = dateFormat;
   }

   @SuppressFBWarnings(justification = "generated code")
   public static OpenIDConnectStringBirthdateStandardClaimConfig.OpenIDConnectStringBirthdateStandardClaimConfigBuilder builder() {
      return new OpenIDConnectStringBirthdateStandardClaimConfig.OpenIDConnectStringBirthdateStandardClaimConfigBuilder();
   }

   @SuppressFBWarnings(justification = "generated code")
   public OpenIDConnectStringBirthdateStandardClaimConfig() {
   }

   @SuppressFBWarnings(justification = "generated code")
   public static class OpenIDConnectStringBirthdateStandardClaimConfigBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private String contextDataField;
      @SuppressFBWarnings(justification = "generated code")
      private String dateFormat;
      @SuppressFBWarnings(justification = "generated code")
      private ClaimConditionConfig claimCondition;

      @SuppressFBWarnings(justification = "generated code")
      OpenIDConnectStringBirthdateStandardClaimConfigBuilder() {
      }

      @SuppressFBWarnings(justification = "generated code")
      public OpenIDConnectStringBirthdateStandardClaimConfig.OpenIDConnectStringBirthdateStandardClaimConfigBuilder contextDataField(String contextDataField) {
         this.contextDataField = contextDataField;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public OpenIDConnectStringBirthdateStandardClaimConfig.OpenIDConnectStringBirthdateStandardClaimConfigBuilder dateFormat(String dateFormat) {
         this.dateFormat = dateFormat;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public OpenIDConnectStringBirthdateStandardClaimConfig.OpenIDConnectStringBirthdateStandardClaimConfigBuilder claimCondition(
         ClaimConditionConfig claimCondition
      ) {
         this.claimCondition = claimCondition;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public OpenIDConnectStringBirthdateStandardClaimConfig build() {
         return new OpenIDConnectStringBirthdateStandardClaimConfig(this.contextDataField, this.dateFormat, this.claimCondition);
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "OpenIDConnectStringBirthdateStandardClaimConfig.OpenIDConnectStringBirthdateStandardClaimConfigBuilder(contextDataField="
            + this.contextDataField
            + ", dateFormat="
            + this.dateFormat
            + ", claimCondition="
            + this.claimCondition
            + ")";
      }
   }
}
