package io.netfoundry.billing.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import io.micrometer.tracing.Span;
import io.micrometer.tracing.TraceContext;
import io.micrometer.tracing.Tracer;
import io.netfoundry.auth.client.midleware.Auth0JwtValidator;
import io.netfoundry.auth.client.midleware.InterServiceTokenProvider;
import io.netfoundry.auth.clientv2.AuthClientV2;
import io.netfoundry.auth.clientv2.AuthorizationContext;
import io.netfoundry.auth.domainv2.AuthEntityResolver;
import io.netfoundry.common.util.RestServiceClient;
import io.netfoundry.common.util.logging.DiagnosticsServletFilter;
import io.netfoundry.messaging.config.EnablePlatformMessaging;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.jose4j.jwt.consumer.JwtConsumer;
import org.simplejavamail.mailer.Mailer;
import org.simplejavamail.mailer.MailerBuilder;
import org.simplejavamail.mailer.config.TransportStrategy;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.web.client.RestTemplateBuilderConfigurer;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.core.env.Environment;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.function.Supplier;

@Slf4j
@Configuration
@EnableAsync
@EnableScheduling
@EnablePlatformMessaging
public class ServerConfiguration implements WebMvcConfigurer {

    private final Environment environment;
    private final String awsRegion;

    @Autowired
    public ServerConfiguration(Environment environment,
        @Value("${aws.region:us-east-1}") String awsRegion) {
        this.environment = environment;
        this.awsRegion = awsRegion;
    }

    @Bean
    public RestServiceClient restServiceClient(RestTemplateBuilder restTemplateBuilder) {
        return new RestServiceClient(restTemplateBuilder);
    }

    @Bean
    public RestTemplateBuilder restTemplateBuilder(RestTemplateBuilderConfigurer restTemplateBuilderConfigurer) {
      return restTemplateBuilderConfigurer.configure(new RestTemplateBuilder());
    }

    @Bean
    public DiagnosticsServletFilter diagnosticsServletFilter(
        @Value("${spring.application.name}") String applicationName,
        @Value("${build.project.version}") String buildVersion,
        @Value("${build.timestamp}") String buildTimestamp,
        @Value("${io.netfoundry.audit-log.dont-log-payload.url-patterns:}")
            String auditLogBlackListUrlPatterns) {
        DiagnosticsServletFilter dsf =
            new DiagnosticsServletFilter(applicationName, buildVersion, buildTimestamp,
                auditLogBlackListUrlPatterns);
        dsf.setAcceptNfHeaders(true);
        return dsf;
    }


    @Bean
    public Object initAuthorizationContext(JwtConsumer jwtConsumer, ObjectMapper objectMapper,
        AuthEntityResolver authEntityResolver) {
        AuthorizationContext.initStatic(jwtConsumer, objectMapper, Optional.of(authEntityResolver));
        return null;
    }

    @Bean
    @Profile("!test")
    public Auth0JwtValidator auth0JwtValidator(
        @Value("${io.netfoundry.auth.provider.auth0.issuer}") String issuer,
        @Value("${io.netfoundry.auth.provider.auth0.audience}") String audience
    ) {
        return new Auth0JwtValidator(issuer, audience, /*isLogAsError*/ true);
    }

    @Bean
    @Profile("!test")
    @Qualifier("serviceIdentityIdProvider")
    public Supplier<UUID> serviceIdentityIdProvider(InterServiceTokenProvider tokenProvider) {
        return tokenProvider::getLocalServiceIdentity;
    }

    @Profile("!test")
    @Bean
    AuthClientV2 authClient(RestServiceClient restServiceClient,
        InterServiceTokenProvider tokenProvider,
        @Value("${io.netfoundry.auth.serviceUrl}") String authServiceUrl) {
        return new AuthClientV2(restServiceClient, tokenProvider, authServiceUrl);
    }

    @Bean
    @Profile("!test")
    public InterServiceTokenProvider interServiceTokenProvider(
        TaskScheduler taskScheduler,
        @Value("${io.netfoundry.billing.provider.auth0.clientid}") String clientId,
        @Value("${io.netfoundry.billing.provider.auth0.clientsecret}") String clientSecret,
        Auth0JwtValidator auth0JwtValidator,
        RestServiceClient restServiceClient,
        @Value("${io.netfoundry.auth.serviceUrl}") String authServiceUrl
    ) {
        return new InterServiceTokenProvider(
            taskScheduler,
            () -> clientId,
            () -> clientSecret,
            restServiceClient,
            auth0JwtValidator,
            authServiceUrl
        );
    }

    /*
     * This bean is configured to only register if in the production profile. In other environments
     * anything that would send an email should log instead.
     */
    @Bean
    @Profile({"production", "staging"})
    public Mailer getMailer(@Value("${io.netfoundry.billing.mailer.smtphost}") String smtpHost,
        @Value("${io.netfoundry.billing.mailer.smtpport}") int smtpPort,
        @Value("${io.netfoundry.billing.mailer.id}") String smtpUser,
        @Value("${io.netfoundry.billing.mailer.secret}") String smtpPass) {
        return MailerBuilder.withSMTPServer(smtpHost, smtpPort, smtpUser, smtpPass)
            .withTransportStrategy(TransportStrategy.SMTP_TLS).withSessionTimeout(10 * 1000)
            .withDebugLogging(false).buildMailer();
    }

    @Bean
    @ConfigurationProperties(prefix = "io.netfoundry.billing.chargebee.plans")
    public Map<String, ChargebeePlan> chargebeePlanMap() {
        return new HashMap<>();
    }


    @Bean
    @ConfigurationProperties(prefix = "io.netfoundry.billing.usage.metering")
    public Map<String, UsageMeteringConfig> usageMeteringConfig() {
        return new HashMap<>();
    }

    @Bean
    @ConfigurationProperties(prefix = "io.netfoundry.billing.overage.metering")
    public Map<String, OverageMeteringConfig> overageMeteringConfig() {
        return new HashMap<>();
    }

    @Autowired
    private ObjectMapper objectMapper;

    @PostConstruct
    public void init() {
        objectMapper.registerModules(new JavaTimeModule());
    }
    
    @Bean
    public Supplier<String> traceIdSupplier(Tracer tracer) {
      return () -> Optional.ofNullable(tracer)
          .map(Tracer::currentSpan)
          .map(Span::context)
          .map(TraceContext::traceId)
          .orElse(null);
    }

}
