package com.bizcare.agent;

import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import jakarta.servlet.FilterChain;
import jakarta.servlet.http.*;
import lombok.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.*;
import org.springframework.http.HttpHeaders;
import org.springframework.security.authentication.*;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.*;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.*;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.stereotype.Component;
import org.springframework.web.cors.*;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.*;

@Configuration @EnableWebSecurity
class SecurityConfig {
    @Value("${app.cors.origins}")
    private String corsOrigins;

    @Bean PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); }
    @Bean SecurityFilterChain filterChain(HttpSecurity http, JwtFilter filter) throws Exception {
        return http.csrf(c -> c.disable())
            .cors(c -> c.configurationSource(cors()))
            .httpBasic(b -> b.disable())
            .formLogin(f -> f.disable())
            .logout(l -> l.disable())
            .sessionManagement(s -> s.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
            .authorizeHttpRequests(a -> a
                .requestMatchers(org.springframework.http.HttpMethod.OPTIONS, "/**").permitAll()
                .requestMatchers("/api/auth/**", "/api/health").permitAll()
                .requestMatchers("/api/agent/**").hasRole("AGENT")
                .requestMatchers("/api/shop/**").hasAnyRole("SHOP", "AGENT")
                .anyRequest().authenticated())
            .exceptionHandling(e -> e
                .authenticationEntryPoint((req, res, ex) -> {
                    res.setStatus(401);
                    res.setContentType("application/json;charset=UTF-8");
                    res.getWriter().write("{\"result\":false,\"msg\":\"로그인이 필요합니다.\",\"data\":null}");
                })
                .accessDeniedHandler((req, res, ex) -> {
                    res.setStatus(403);
                    res.setContentType("application/json;charset=UTF-8");
                    res.getWriter().write("{\"result\":false,\"msg\":\"권한이 없습니다.\",\"data\":null}");
                }))
            .addFilterBefore(filter, UsernamePasswordAuthenticationFilter.class)
            .build();
    }
    @Bean CorsConfigurationSource cors() {
        CorsConfiguration c = new CorsConfiguration();
        List<String> origins = Arrays.stream(corsOrigins.split(",")).map(String::trim).filter(s -> !s.isEmpty()).toList();
        if (origins.isEmpty() || origins.contains("*")) {
            c.setAllowedOriginPatterns(List.of("*"));
        } else {
            // 로컬/사설망 개발 접근을 허용하면서도 credentials 유지
            List<String> patterns = new ArrayList<>(origins);
            patterns.add("http://localhost:*");
            patterns.add("http://127.0.0.1:*");
            patterns.add("http://192.168.*.*:*");
            patterns.add("http://10.*.*.*:*");
            c.setAllowedOriginPatterns(patterns);
        }
        c.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"));
        c.setAllowedHeaders(List.of("*"));
        c.setExposedHeaders(List.of("*"));
        c.setAllowCredentials(true);
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", c);
        return source;
    }
}
@Component
class JwtService {
    private final byte[] secret; private final long expiration;
    JwtService(@Value("${app.jwt.secret}") String secret, @Value("${app.jwt.expiration-ms}") long expiration) { this.secret=secret.getBytes(StandardCharsets.UTF_8); this.expiration=expiration; }
    String create(Member m) { return Jwts.builder().subject(m.getUserid()).claim("role",m.getRole()).claim("groupId",m.getGroupId()).claim("companyId",m.getCompanyId()).issuedAt(new Date()).expiration(new Date(System.currentTimeMillis()+expiration)).signWith(Keys.hmacShaKeyFor(secret)).compact(); }
    Claims claims(String token) { return Jwts.parser().verifyWith(Keys.hmacShaKeyFor(secret)).build().parseSignedClaims(token).getPayload(); }
}
@Component @RequiredArgsConstructor
class JwtFilter extends OncePerRequestFilter {
    private final JwtService jwt;
    @Override protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain) throws IOException, jakarta.servlet.ServletException {
        String header=req.getHeader(HttpHeaders.AUTHORIZATION);
        if(header != null && header.startsWith("Bearer ")) try {
            Claims c=jwt.claims(header.substring(7)); JwtUser user=new JwtUser(c.getSubject(), c.get("role",String.class), c.get("groupId",String.class), c.get("companyId",Long.class));
            var auth=new UsernamePasswordAuthenticationToken(user,null,user.getAuthorities()); SecurityContextHolder.getContext().setAuthentication(auth);
        } catch (JwtException | IllegalArgumentException ignored) { SecurityContextHolder.clearContext(); }
        chain.doFilter(req,res);
    }
}
@Getter @AllArgsConstructor
class JwtUser implements UserDetails {
    private String userid, role, groupId; private Long companyId;
    @Override public Collection<? extends GrantedAuthority> getAuthorities() { return List.of(new SimpleGrantedAuthority("ROLE_"+role)); }
    @Override public String getPassword() { return ""; } @Override public String getUsername() { return userid; }
    @Override public boolean isAccountNonExpired(){return true;} @Override public boolean isAccountNonLocked(){return true;} @Override public boolean isCredentialsNonExpired(){return true;} @Override public boolean isEnabled(){return true;}
}
