package eu.dnetlib.organizations;

import javax.sql.DataSource;

import org.apache.commons.codec.digest.DigestUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.access.AccessDeniedHandler;

import eu.dnetlib.organizations.controller.UserInfo;
import eu.dnetlib.organizations.controller.UserRole;
import eu.dnetlib.organizations.utils.OpenOrgsConstants;

@Profile("dev")
@Configuration
@EnableWebSecurity
public class MockSecurityConfig extends WebSecurityConfigurerAdapter {

	@Autowired
	private DataSource dataSource;

	@Value("${openaire.api.valid.subnet}")
	private String openaireApiValidSubnet;

	private static final String DEFAULT_PASSWORD = "dnet";

	private static Logger logger = LoggerFactory.getLogger(MockSecurityConfig.class);

	@Override
	protected void configure(final HttpSecurity http) throws Exception {
		http.headers().frameOptions().sameOrigin();

		http.csrf()
			.disable()
			.authorizeRequests()
			.antMatchers("/", "/api/**")
			.hasAnyRole(OpenOrgsConstants.VALID_ROLES)
			.antMatchers("/registration_api/**")
			.hasRole(OpenOrgsConstants.NOT_AUTORIZED_ROLE)
			.antMatchers("/common/**", "/resources/**", "/webjars/**", "/metrics", "/health", "/kpis", "/dbmodel/**")
			.permitAll()
			.antMatchers("/oa_api/**")
			.permitAll()
			.anyRequest()
			.authenticated()
			.and()
			.formLogin()
			.loginPage("/login")
			.permitAll()
			.and()
			.logout()
			.permitAll()
			.and()
			.exceptionHandling()
			.accessDeniedHandler(accessDeniedHandler());
	}

	private AccessDeniedHandler accessDeniedHandler() {
		return (req, res, e) -> {
			final Authentication auth = SecurityContextHolder.getContext().getAuthentication();

			if (auth != null) {
				logger
					.warn(String.format("User '%s' (%s) attempted to access the protected URL: %s", auth.getName(), req.getRemoteAddr(), req.getRequestURI()));
			}

			if (UserInfo.isNotAuthorized(auth)) {
				res.sendRedirect(req.getContextPath() + "/authorizationRequest");
			} else {
				res.sendRedirect(req.getContextPath() + "/alreadyRegistered");
			}

		};
	}

	@Autowired
	public void configureGlobal(final AuthenticationManagerBuilder auth) throws Exception {
		auth.jdbcAuthentication()
			.dataSource(dataSource)
			.usersByUsernameQuery("select ?, '{MD5}" + DigestUtils.md5Hex(DEFAULT_PASSWORD) + "', true")
			.authoritiesByUsernameQuery("with const as (SELECT ? as email) "
				+ "select c.email, 'ROLE_" + OpenOrgsConstants.OPENORGS_ROLE_PREFIX + "'||coalesce(u.role, '"
				+ UserRole.NOT_AUTHORIZED
				+ "') from const c left outer join users u on (u.email = c.email)");
	}

	@Bean
	public PasswordEncoder passwordEncoder() {
		return PasswordEncoderFactories.createDelegatingPasswordEncoder();
	}

}
