package com.erp.security;

import com.erp.ai.security.AiApiKeyAuthFilter;
import com.erp.ai.service.AiAgentApiKeyService;
import com.erp.service.SubstituteService;
import com.erp.tenant.CompanyResolver;
import com.erp.tenant.SubdomainResolverFilter;
import com.erp.tenant.TenantDbSettings;
import com.erp.tenant.TenantRoutingDataSource;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import java.util.Arrays;
import java.util.List;

@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
@Slf4j
public class SecurityConfig {

    private final JwtTokenProvider jwtTokenProvider;
    private final CompanyResolver companyResolver;
    private final TenantRoutingDataSource tenantRoutingDataSource;
    private final TenantDbSettings tenantDbSettings;
    private final AiAgentApiKeyService aiAgentApiKeyService;
    private final SubstituteService substituteService;

    /**
     * CORS 화이트리스트. 콤마 구분 문자열로 주입 (예: "http://localhost:3000,https://v1.invyone.com").
     * application.yml 또는 환경변수 CORS_ALLOWED_ORIGINS 로 설정.
     */
    @Value("${cors.allowed-origins}")
    private String corsAllowedOrigins;

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
                .csrf(AbstractHttpConfigurer::disable)
                .cors(cors -> cors.configurationSource(corsConfigurationSource()))
                .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .authorizeHttpRequests(auth -> auth
                        // ⚠️ 95개 컨트롤러 중 자체 토큰 검증을 하는 건 AuthController 한 곳뿐.
                        // SecurityConfig 단계에서 강제 인증을 켜면 회귀 위험 너무 큼 (Phase 3 별도 트랙).
                        // 그동안은 기존 동작 유지하되, JwtAuthenticationFilter 가 valid 토큰일 때
                        // SecurityContext 를 채워주어 컨트롤러가 사용자 정보 attribute 를 받을 수 있게 함.
                        .requestMatchers("/api/**").permitAll()
                        .anyRequest().authenticated()
                )
                // ⚠️ Spring Security 6 부터 addFilterBefore/After 의 anchor 는 _이미 등록된_
                //    필터여야 함. 따라서 JwtAuthenticationFilter 를 가장 먼저 (Spring 표준
                //    UsernamePasswordAuthenticationFilter 기준으로) 등록한 뒤, 나머지 커스텀
                //    필터들이 JwtAuthenticationFilter 를 anchor 로 사용한다.
                .addFilterBefore(new JwtAuthenticationFilter(jwtTokenProvider),
                        UsernamePasswordAuthenticationFilter.class)
                // Phase 2 (2026-04-24): 서브도메인 → CompanyResolver → TenantRoutingDataSource 라우팅.
                // JwtAuthenticationFilter 보다 앞에서 실행되어야 tenant 컨텍스트가 먼저 결정됨.
                .addFilterBefore(
                        new SubdomainResolverFilter(companyResolver, tenantRoutingDataSource, tenantDbSettings),
                        JwtAuthenticationFilter.class)
                // AiApi 키 인증 — jwt 앞에서 sk-pipe-* 형식 처리, 매칭되지 않으면 jwt 로 통과.
                .addFilterBefore(new AiApiKeyAuthFilter(aiAgentApiKeyService),
                        JwtAuthenticationFilter.class)
                // JwtAuthenticationFilter 뒤 — JWT.company_code 와 서브도메인의 company_code 대조.
                .addFilterAfter(new TenantConsistencyGuardFilter(jwtTokenProvider),
                        JwtAuthenticationFilter.class)
                // TenantConsistencyGuardFilter 뒤 — 대무자(代務者) 컨텍스트 effective_user_ids 주입.
                .addFilterAfter(new SubstituteContextFilter(substituteService),
                        TenantConsistencyGuardFilter.class)
                // SubstituteContextFilter 뒤 — 비번 강제 변경 대기자는 허용 경로만 통과.
                .addFilterAfter(new ForcePasswordChangeGuardFilter(jwtTokenProvider),
                        SubstituteContextFilter.class);

        return http.build();
    }

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

    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration config = new CorsConfiguration();
        // 테넌트 서브도메인 지원을 위해 setAllowedOriginPatterns 사용
        // (setAllowedOrigins 는 정확한 매칭만 허용해서 *.invyone.com 같은 패턴이 안 됨)
        // 전체 와일드카드 '*' 는 금지 — 반드시 명시된 prefix 만 허용.
        List<String> patterns = Arrays.stream(corsAllowedOrigins.split(","))
                .map(String::trim)
                .filter(s -> !s.isEmpty())
                .toList();
        log.info("[CORS] raw value from @Value = '{}'", corsAllowedOrigins);
        log.info("[CORS] parsed patterns ({}): {}", patterns.size(), patterns);
        config.setAllowedOriginPatterns(patterns);
        config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"));
        config.setAllowedHeaders(List.of("*"));
        config.setAllowCredentials(true);

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