package com.cloudhandson.vpdbackoffice.config;

import java.util.ArrayList;
import java.util.List;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;

@Configuration
public class SecurityConfig {

  @Bean
  SecurityFilterChain securityFilterChain(
      HttpSecurity http,
      BackofficeProperties properties,
      UserDetailsService userDetailsService
  ) throws Exception {
    if (properties.security().requireHttps()) {
      http.requiresChannel(channel -> channel.anyRequest().requiresSecure());
    }

    var security = properties.security();
    if (security.rememberMeConfigured()) {
      http.rememberMe(rememberMe -> rememberMe
          .key(security.rememberMeKey())
          .userDetailsService(userDetailsService)
          .rememberMeParameter("remember-me")
          .rememberMeCookieName("VPD_REMEMBER_ME")
          .tokenValiditySeconds(security.rememberMeValiditySeconds())
          .useSecureCookie(true)
          .alwaysRemember(true));
    }

    return http
        .csrf(csrf -> csrf.ignoringRequestMatchers(
            "/mcp", "/mcp/messages", "/mcp/*/messages"))
        .headers(headers -> headers.httpStrictTransportSecurity(hsts -> hsts
            .includeSubDomains(true)
            .maxAgeInSeconds(31_536_000)))
        .exceptionHandling(exceptions -> exceptions
            .authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login")))
        .authorizeHttpRequests(auth -> auth
            .requestMatchers(
                "/css/**", "/js/**", "/webjars/**",
                "/mcp", "/mcp/sse", "/mcp/*/sse", "/mcp/messages", "/mcp/*/messages")
            .permitAll()
            .requestMatchers(HttpMethod.POST, "/login", "/logout").permitAll()
            .requestMatchers(HttpMethod.POST,
                "/probe",
                "/vector-knowledge/search",
                "/security-sql-scripts/explanation",
                "/mcp-chatbot",
                "/mcp-client-demo",
                "/mcp-reasoning")
            .authenticated()
            .requestMatchers(HttpMethod.PUT, "/**").hasRole("ADMIN")
            .requestMatchers(HttpMethod.PATCH, "/**").hasRole("ADMIN")
            .requestMatchers(HttpMethod.POST, "/**").hasRole("ADMIN")
            .requestMatchers(HttpMethod.DELETE, "/**").hasRole("ADMIN")
            .anyRequest().authenticated())
        .formLogin(login -> login
            .loginPage("/login")
            .permitAll())
        .logout(logout -> logout.logoutSuccessUrl("/login?logout"))
        .build();
  }

  @Bean
  UserDetailsService userDetailsService(
      BackofficeProperties properties,
      PasswordEncoder passwordEncoder
  ) {
    var security = properties.security();
    String encodedPassword = security.adminPasswordHash() == null || security.adminPasswordHash().isBlank()
        ? passwordEncoder.encode(security.adminPassword())
        : security.adminPasswordHash();
    List<UserDetails> users = new ArrayList<>();
    users.add(User.withUsername(security.adminUser())
        .password(encodedPassword)
        .roles("ADMIN")
        .build());
    if (security.guestConfigured()) {
      String encodedGuestPassword = security.guestPasswordHash() == null || security.guestPasswordHash().isBlank()
          ? passwordEncoder.encode(security.guestPassword())
          : security.guestPasswordHash();
      users.add(User.withUsername(security.guestUser())
          .password(encodedGuestPassword)
          .roles("VIEWER")
          .build());
    }
    return new InMemoryUserDetailsManager(users);
  }

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