package de.assecutor.votianlt.security;

import com.vaadin.flow.spring.security.AuthenticationContext;
import de.assecutor.votianlt.service.DemoModeService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.bson.types.ObjectId;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import java.util.Optional;

@Component
@Slf4j
public class SecurityService {

    private final AuthenticationContext authenticationContext;
    private final DemoModeService demoModeService;

    public SecurityService(AuthenticationContext authenticationContext, DemoModeService demoModeService) {
        this.authenticationContext = authenticationContext;
        this.demoModeService = demoModeService;
    }

    public Optional<UserDetails> getAuthenticatedUser() {
        return authenticationContext.getAuthenticatedUser(UserDetails.class);
    }

    public boolean isUserLoggedIn() {
        if (authenticationContext.isAuthenticated())
            return true;
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        return auth != null && auth.isAuthenticated() && !(auth instanceof AnonymousAuthenticationToken);
    }

    public String getCurrentUsername() {
        // 1) Versuche SecurityContext zuerst (robuster bei Custom Login)
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        if (auth != null && auth.isAuthenticated() && !(auth instanceof AnonymousAuthenticationToken)) {
            Object principal = auth.getPrincipal();
            if (principal instanceof CustomUserPrincipal cup) {
                de.assecutor.votianlt.model.User u = cup.getUser();
                if (u != null) {
                    String namePart = (nullToEmpty(u.getFirstname()) + " " + nullToEmpty(u.getName())).trim();
                    if (!namePart.isBlank())
                        return namePart;
                    if (u.getEmail() != null && !u.getEmail().isBlank())
                        return u.getEmail();
                }
                return cup.getUsername();
            }
            if (principal instanceof UserDetails ud) {
                return ud.getUsername();
            }
            if (principal instanceof String s && !"anonymousUser".equalsIgnoreCase(s)) {
                return s;
            }
        }

        // 2) Fallback: Vaadin AuthenticationContext
        return getAuthenticatedUser().map(UserDetails::getUsername).orElse("Anonymous");
    }

    private String nullToEmpty(String s) {
        return s == null ? "" : s;
    }

    /**
     * Get the complete MongoDB User entity from the session
     */
    public de.assecutor.votianlt.model.User getCurrentDatabaseUser() {
        return getAuthenticatedUser().filter(userDetails -> userDetails instanceof CustomUserPrincipal)
                .map(userDetails -> ((CustomUserPrincipal) userDetails).getUser())
                .orElseThrow(() -> new RuntimeException("No user logged in"));
    }

    /**
     * Get the MongoDB User ID from the session
     */
    public ObjectId getCurrentUserId() {
        return getCurrentDatabaseUser().getId();
    }

    /**
     * Get the usrId from the session
     */
    public int getCurrentUsrId() {
        return getCurrentDatabaseUser().getUsrId();
    }

    /**
     * @deprecated Use getCurrentDatabaseUser() instead
     */
    @Deprecated
    public User getCurrentUser() {
        if (getAuthenticatedUser().isPresent()) {
            return (User) getAuthenticatedUser().get();
        } else {
            throw new RuntimeException("No user logged in");
        }
    }

    public void logout() {
        try {
            String authenticatedUsername = getAuthenticatedUser().map(UserDetails::getUsername).orElse(null);
            if (DemoModeService.isDemoUsername(authenticatedUsername)) {
                demoModeService.cleanupCurrentSessionIfOwned();
            }
        } catch (Exception ex) {
            log.warn("Demo logout cleanup failed: {}", ex.getMessage(), ex);
        }
        authenticationContext.logout();
    }

    public boolean hasRole(String role) {
        return getAuthenticatedUser().map(user -> user.getAuthorities().stream()
                .anyMatch(authority -> authority.getAuthority().equals("ROLE_" + role))).orElse(false);
    }
}
