package com.pandoli365.bibimbap.security;

import com.pandoli365.bibimbap.data.UserData;
import com.pandoli365.bibimbap.mapper.UserPermissionsMapper;
import com.pandoli365.bibimbap.mapper.UsersMapper;
import jakarta.servlet.http.HttpSession;
import java.util.HashSet;
import java.util.Set;
import org.springframework.stereotype.Component;

@Component
public class PermissionGate {

    private final UsersMapper usersMapper;
    private final UserPermissionsMapper userPermissionsMapper;

    public PermissionGate(UsersMapper usersMapper, UserPermissionsMapper userPermissionsMapper) {
        this.usersMapper = usersMapper;
        this.userPermissionsMapper = userPermissionsMapper;
    }

    public boolean has(HttpSession session, String permissionKey) {
        Long userId = sessionUserId(session);
        if (userId == null) {
            return false;
        }
        refreshIfStale(session, userId);

        String role = (String) session.getAttribute("role");
        if (Roles.ADMIN.equals(role)) {
            return true;
        }
        if (Roles.SUBADMIN.equals(role)) {
            Object p = session.getAttribute("permissions");
            return p instanceof Set<?> set && set.contains(permissionKey);
        }
        return false;
    }

    public boolean canModerate(HttpSession session) {
        return has(session, PermissionKeys.CONTENT_MODERATE.name());
    }

    /**
     * 미인증 vs 미인가 분기용(401 vs 403). 세션에 userId 가 있으면 인증됨.
     */
    public boolean isAuthenticated(HttpSession session) {
        return sessionUserId(session) != null;
    }

    /**
     * 콘솔(/admin/**) ADMIN 전용 게이트. epoch 대조 후 현재 role==ADMIN 만 통과.
     * 권한 키 기반 has 와 달리 SUBADMIN 은 어떤 키를 보유해도 통과시키지 않는다.
     */
    public boolean isAdmin(HttpSession session) {
        Long userId = sessionUserId(session);
        if (userId == null) {
            return false;
        }
        refreshIfStale(session, userId);
        return Roles.ADMIN.equals(session.getAttribute("role"));
    }

    public boolean require(HttpSession session, String permissionKey) {
        return has(session, permissionKey);
    }

    private Long sessionUserId(HttpSession session) {
        if (session == null) {
            return null;
        }
        Object attr = session.getAttribute("userId");
        if (attr instanceof Number number) {
            return number.longValue();
        }
        if (attr instanceof String s) {
            try {
                return Long.parseLong(s);
            } catch (NumberFormatException e) {
                return null;
            }
        }
        return null;
    }

    private void refreshIfStale(HttpSession session, long userId) {
        long dbEpoch = usersMapper.getPermissionsEpoch(userId);

        Object epochAttr = session.getAttribute("permsEpoch");
        long sessionEpoch = epochAttr instanceof Long l ? l : Long.MIN_VALUE;

        if (dbEpoch != sessionEpoch) {
            UserData u = usersMapper.getUser(userId);
            if (u == null) {
                session.removeAttribute("role");
                session.setAttribute("permissions", new HashSet<String>());
                session.setAttribute("permsEpoch", dbEpoch);
                return;
            }
            session.setAttribute("role", u.getRole());
            session.setAttribute("permissions", new HashSet<>(userPermissionsMapper.listKeys(userId)));
            session.setAttribute("permsEpoch", dbEpoch);
        }
    }
}
