package ru.kirillius.pf.sdn.core.Auth;

import jakarta.servlet.http.HttpSession;
import ru.kirillius.pf.sdn.core.AppService;
import ru.kirillius.pf.sdn.core.Context;
import ru.kirillius.pf.sdn.core.Util.HashUtil;
import ru.kirillius.utils.logging.SystemLogger;

import java.io.IOException;
import java.util.Objects;
import java.util.UUID;

/**
 * Coordinates authentication flows, password hashing, and token lifecycle management.
 */
public class AuthManager extends AppService {
    public final static String SESSION_AUTH_KEY = "auth";
    public final static String SESSION_TOKEN = "token";
    private final static String CTX = AuthManager.class.getSimpleName();



    /**
     * Verifies the provided password against the stored hash.
     */
    public boolean validatePassword(String pass) {
        var config = context.getConfig();
        return HashUtil.hash(pass, config.getPasswordSalt()).equals(config.getPasswordHash());
    }

    /**
     * Updates the stored password hash and generates a new salt.
     */
    public void updatePassword(String pass) {
        var config = context.getConfig();
        config.setPasswordSalt(UUID.randomUUID().toString());
        config.setPasswordHash(
                HashUtil.hash(pass, config.getPasswordSalt())
        );
    }

    /**
     * Creates a new authentication token with the provided description and persists it.
     */
    public AuthToken createToken(String description) {
        var token = new AuthToken();
        token.setDescription(description);
        var tokenStorage = context.getServiceManager().getService(TokenService.class);
        tokenStorage.add(token);
        try {
            tokenStorage.store();
        } catch (IOException e) {
            SystemLogger.error("Failed to save token storage", CTX, e);
        }
        return token;
    }

    /**
     * Checks whether the storage contains the specified token.
     */
    public boolean validateToken(AuthToken token) {
        return context.getServiceManager().getService(TokenService.class).contains(token);
    }

    /**
     * Stores the authentication flag in the HTTP session.
     */
    public void setSessionAuthState(HttpSession session, boolean state) {
        session.setAttribute(SESSION_AUTH_KEY, state);
    }

    /**
     * Associates the given token with the HTTP session.
     */
    public void setSessionToken(HttpSession session, AuthToken token) {
        session.setAttribute(SESSION_TOKEN, token);
    }

    /**
     * Retrieves the token associated with the HTTP session.
     */
    public AuthToken getSessionToken(HttpSession session) {
        return (AuthToken) session.getAttribute(SESSION_TOKEN);
    }

    /**
     * Returns whether the HTTP session is marked as authenticated.
     */
    public boolean getSessionAuthState(HttpSession session) {
        return Objects.equals(session.getAttribute(SESSION_AUTH_KEY), Boolean.TRUE);
    }

    /**
     * Removes the provided token from storage if present.
     */
    public void invalidateToken(AuthToken token) {
        var tokenStorage = context.getServiceManager().getService(TokenService.class);
        if (tokenStorage.contains(token)) {
            tokenStorage.remove(token);
            try {
                tokenStorage.store();
            } catch (IOException e) {
                SystemLogger.error("Failed to save token storage", CTX, e);
            }
        }

    }

    /**
     * Checks token validity using its raw string value.
     */
    public boolean validateToken(String token) {
        return validateToken(new AuthToken(token));
    }

    /**
     * Invalidates a token identified by its string representation.
     */
    public void invalidateToken(String token) {
        invalidateToken(new AuthToken(token));
    }

    /**
     * Creates the manager bound to the shared context.
     */
    public AuthManager(Context context) {
        super(context);
    }

    /**
     * No-op close implementation.
     */
    @Override
    public void close() throws IOException {
        //no-op
    }
}
