package com.gnx.telemedicine.security;

import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
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.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.HttpStatusEntryPoint;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import com.gnx.telemedicine.config.SecurityHeadersConfig;

import java.util.List;

@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {
    private final CustomUserDetailsService customUserDetailsService;
    private final JwtAuthenticationFilter jwtAuthenticationFilter;
    private final RateLimitingFilter rateLimitingFilter;
    private final SecurityHeadersConfig securityHeadersConfig;

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.sessionManagement(s -> s.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .csrf(AbstractHttpConfigurer::disable)
                .authorizeHttpRequests(req -> req
                        .requestMatchers(HttpMethod.OPTIONS, "/**").permitAll()
                        .requestMatchers("/api/v3/auth/**").permitAll()
                        .requestMatchers("/api/v3/users/register/**").permitAll()
                        .requestMatchers("/api/v3/turn/**").permitAll()

                        .requestMatchers(HttpMethod.POST, "/api/v3/appointments").hasAnyRole("DOCTOR", "PATIENT")
                        .requestMatchers(HttpMethod.DELETE, "/api/v3/appointments/**").hasAnyRole("DOCTOR", "PATIENT", "ADMIN")

                        .requestMatchers("/api/v3/admin/**").hasRole("ADMIN")
                        .requestMatchers("/api/v3/audit/**").hasRole("ADMIN")

                        .requestMatchers(HttpMethod.POST, "/api/v3/availability/**").hasRole("DOCTOR")
                        .requestMatchers(HttpMethod.PATCH, "/api/v3/availability/**").hasRole("DOCTOR")
                        .requestMatchers(HttpMethod.DELETE, "/api/v3/availability/**").hasRole("DOCTOR")
                        .requestMatchers(HttpMethod.GET, "/api/v3/availability/**").authenticated()

                        .requestMatchers("/api/v3/messages/**").authenticated()
                        .requestMatchers("/api/v3/files/avatars/**").permitAll()
                        .requestMatchers("/api/v3/files/avatar").authenticated()
                        .requestMatchers("/api/v3/2fa/**").authenticated()
                        .requestMatchers("/api/v3/gdpr/**").authenticated()
                        .requestMatchers(HttpMethod.POST, "/api/v3/medical-records/**").hasRole("DOCTOR")
                        .requestMatchers(HttpMethod.PUT, "/api/v3/medical-records/**").hasRole("DOCTOR")
                        .requestMatchers(HttpMethod.DELETE, "/api/v3/medical-records/**").hasRole("DOCTOR")
                        .requestMatchers("/api/v3/medical-records/**").authenticated()
                        .requestMatchers(HttpMethod.POST, "/api/v3/vital-signs/**").hasAnyRole("DOCTOR", "PATIENT")
                        .requestMatchers(HttpMethod.DELETE, "/api/v3/vital-signs/**").hasRole("DOCTOR")
                        .requestMatchers("/api/v3/vital-signs/**").authenticated()
                        .requestMatchers(HttpMethod.POST, "/api/v3/lab-results/**").hasRole("DOCTOR")
                        .requestMatchers(HttpMethod.PUT, "/api/v3/lab-results/**").hasRole("DOCTOR")
                        .requestMatchers(HttpMethod.DELETE, "/api/v3/lab-results/**").hasRole("DOCTOR")
                        .requestMatchers("/api/v3/lab-results/**").authenticated()
                        .requestMatchers(HttpMethod.POST, "/api/v3/prescriptions/**").hasRole("DOCTOR")
                        .requestMatchers(HttpMethod.PUT, "/api/v3/prescriptions/**").hasRole("DOCTOR")
                        .requestMatchers(HttpMethod.DELETE, "/api/v3/prescriptions/**").hasRole("DOCTOR")
                        .requestMatchers("/api/v3/prescriptions/**").authenticated()
                        .requestMatchers(HttpMethod.POST, "/api/v3/medication-intake-logs/**").authenticated()
                        .requestMatchers("/api/v3/medication-intake-logs/**").authenticated()
                        .requestMatchers("/ws/**").permitAll()

                        .requestMatchers("/swagger-ui.html").permitAll()
                        .requestMatchers("/v3/api-docs/**").permitAll()
                        .requestMatchers("/swagger-ui/**").permitAll()
                        
                        // Health check endpoints
                        .requestMatchers("/actuator/health/**").permitAll()
                        .requestMatchers("/actuator/health").permitAll()

                        .anyRequest().authenticated()
                )
                .exceptionHandling(e -> e
                        .authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED))
                        .accessDeniedHandler(((request, response, accessDeniedException) ->
                                response.setStatus(HttpStatus.FORBIDDEN.value())))
                )
                .anonymous(AbstractHttpConfigurer::disable)
                .addFilterBefore(securityHeadersConfig, UsernamePasswordAuthenticationFilter.class)
                .addFilterBefore(rateLimitingFilter, UsernamePasswordAuthenticationFilter.class)
                .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
                .authenticationProvider(authenticationProvider());

        http.cors(c -> {}); // enable CORS using CorsConfigurationSource bean
        return http.build();
    }

    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration config = new CorsConfiguration();
        
        // Check if we're in production mode
        String profile = System.getenv("SPRING_PROFILES_ACTIVE");
        boolean isProduction = "prod".equals(profile) || "production".equals(profile);
        
        // Get allowed origins from environment or use defaults
        String allowedOriginsEnv = System.getenv("CORS_ALLOWED_ORIGINS");
        if (allowedOriginsEnv != null && !allowedOriginsEnv.isEmpty()) {
            // Parse comma-separated origins from environment variable
            List<String> allowedOrigins = List.of(allowedOriginsEnv.split(","));
            config.setAllowedOrigins(allowedOrigins);
        } else if (isProduction) {
            // In production, require explicit configuration - fail safe by allowing nothing
            // This forces explicit configuration via environment variable
            config.setAllowedOrigins(List.of());
            // Log warning - this should be configured via environment variable
            // Note: Using System.err here as logger may not be initialized yet during bean creation
            // In production, this should be caught during deployment validation
        } else {
            // Development mode: Allow localhost and specific common development origins only
            // Do NOT use wildcards for private networks in production
            config.setAllowedOrigins(List.of(
                    "http://localhost:4200",
                    "https://localhost:4200",
                    "http://127.0.0.1:4200",
                    "https://127.0.0.1:4200",
                    "http://localhost:5173",
                    "https://localhost:5173",
                    "http://127.0.0.1:5173",
                    "https://127.0.0.1:5173"
            ));
            
            // For development, allow specific IP patterns but be more restrictive
            // Only allow specific known development IPs, not all private networks
            String devIp = System.getenv("DEV_IP");
            if (devIp != null && !devIp.isEmpty()) {
                List<String> devOrigins = new java.util.ArrayList<>(config.getAllowedOrigins());
                devOrigins.add("http://" + devIp + ":4200");
                devOrigins.add("https://" + devIp + ":4200");
                devOrigins.add("http://" + devIp + ":5173");
                devOrigins.add("https://" + devIp + ":5173");
                config.setAllowedOrigins(devOrigins);
            }
        }
        
        // Get allowed methods from environment or use defaults
        String allowedMethodsEnv = System.getenv("CORS_ALLOWED_METHODS");
        if (allowedMethodsEnv != null && !allowedMethodsEnv.isEmpty()) {
            config.setAllowedMethods(List.of(allowedMethodsEnv.split(",")));
        } else {
            config.setAllowedMethods(List.of("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"));
        }
        
        // Restrict allowed headers - don't allow all headers
        String allowedHeadersEnv = System.getenv("CORS_ALLOWED_HEADERS");
        if (allowedHeadersEnv != null && !allowedHeadersEnv.isEmpty()) {
            config.setAllowedHeaders(List.of(allowedHeadersEnv.split(",")));
        } else {
            config.setAllowedHeaders(List.of("Authorization", "Content-Type", "X-Requested-With", "Accept", "Origin"));
        }
        
        config.setExposedHeaders(List.of("Authorization", "X-RateLimit-Remaining", "X-RateLimit-Reset"));
        config.setAllowCredentials(true);
        
        // Get max age from environment or use default
        String maxAgeEnv = System.getenv("CORS_MAX_AGE");
        long maxAge = maxAgeEnv != null ? Long.parseLong(maxAgeEnv) : 3600L;
        config.setMaxAge(maxAge); // Cache preflight requests

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", config);
        return source;
    }

    @Bean
    public AuthenticationProvider authenticationProvider() {
        DaoAuthenticationProvider provider = new DaoAuthenticationProvider(customUserDetailsService);
        provider.setPasswordEncoder(passwordEncoder());
        return provider;
    }

    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
        return config.getAuthenticationManager();
    }

}
