package demo;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
import org.springframework.security.oauth2.core.user.DefaultOAuth2User;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.stereotype.Service;

/**
 * Maps a login at an external identity provider onto a local account.
 * The key is (provider, stable subject id) - NEVER the email address and never a display name.
 */
@Service
public class OAuth2LoginAccountService {

    public record Account(long id, String provider, String subject, String email, String name) {}

    private final Map<String, Account> accounts = new ConcurrentHashMap<>();
    private final AtomicLong sequence = new AtomicLong(100);

    public Account findOrCreate(String provider, String subject, String email, String name) {
        return accounts.computeIfAbsent(provider + ":" + subject,
                key -> new Account(sequence.incrementAndGet(), provider, subject, email, name));
    }

    public int count() {
        return accounts.size();
    }

    /** OIDC providers (Google): the stable key is the ID token's "sub" claim. */
    public OidcUser mapOidc(String registrationId, OidcUser oidc) {
        Account a = findOrCreate(registrationId, oidc.getSubject(), oidc.getEmail(), oidc.getFullName());
        return new DefaultOidcUser(withLocalRoles(oidc.getAuthorities(), a), oidc.getIdToken(), oidc.getUserInfo());
    }

    /** Plain OAuth2 (GitHub has no ID token): the stable key is the numeric "id" attribute, not "login". */
    public OAuth2User mapGithub(OAuth2User user) {
        String id = String.valueOf((Object) user.getAttribute("id"));
        Account a = findOrCreate("github", id, user.getAttribute("email"), user.getAttribute("login"));
        return new DefaultOAuth2User(withLocalRoles(user.getAuthorities(), a), user.getAttributes(), "id");
    }

    private static List<GrantedAuthority> withLocalRoles(java.util.Collection<? extends GrantedAuthority> existing, Account a) {
        var all = new java.util.ArrayList<GrantedAuthority>(existing);
        all.add(new SimpleGrantedAuthority("ROLE_USER"));
        all.add(new SimpleGrantedAuthority("ACCOUNT_" + a.id()));
        return all;
    }
}
