package com.goldenchart.service;

import com.goldenchart.auth.MenuIds;
import com.goldenchart.auth.UserRole;
import com.goldenchart.dto.MenuPermissionsResponse;
import com.goldenchart.dto.RolePermissionsUpdateRequest;
import com.goldenchart.entity.GcRoleMenuPermission;
import com.goldenchart.entity.GcUser;
import com.goldenchart.repository.GcRoleMenuPermissionRepository;
import com.goldenchart.repository.GcUserRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.server.ResponseStatusException;

import java.util.*;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
public class RolePermissionService {

    private final GcRoleMenuPermissionRepository permRepo;
    private final GcUserRepository userRepo;

    @Transactional(readOnly = true)
    public MenuPermissionsResponse resolveForUserId(Long userId) {
        if (userId == null) {
            return buildForRole(UserRole.GUEST.name());
        }
        GcUser user = userRepo.findById(userId)
                .filter(u -> Boolean.TRUE.equals(u.getEnabled()))
                .orElse(null);
        if (user == null) {
            return buildForRole(UserRole.GUEST.name());
        }
        String role = user.getRole() != null ? user.getRole() : UserRole.USER.name();
        return buildForRole(role);
    }

    @Transactional(readOnly = true)
    public MenuPermissionsResponse buildForRole(String role) {
        String r = UserRole.fromString(role).name();
        Map<String, Boolean> map = defaultMap();
        for (GcRoleMenuPermission p : permRepo.findByRole(r)) {
            map.put(p.getMenuId(), Boolean.TRUE.equals(p.getAllowed()));
        }
        return MenuPermissionsResponse.builder().role(r).permissions(map).build();
    }

    @Transactional(readOnly = true)
    public Map<String, Map<String, Boolean>> listAllRolePermissions() {
        Map<String, Map<String, Boolean>> out = new LinkedHashMap<>();
        for (UserRole role : UserRole.values()) {
            out.put(role.name(), buildForRole(role.name()).getPermissions());
        }
        return out;
    }

    @Transactional
    public void updateRolePermissions(RolePermissionsUpdateRequest req) {
        if (req == null || req.getRole() == null || req.getPermissions() == null) {
            throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "role과 permissions가 필요합니다.");
        }
        String role = UserRole.fromString(req.getRole()).name();
        permRepo.deleteByRole(role);
        List<GcRoleMenuPermission> rows = new ArrayList<>();
        for (String menuId : MenuIds.ALL) {
            Boolean allowed = req.getPermissions().getOrDefault(menuId, false);
            rows.add(GcRoleMenuPermission.builder()
                    .role(role)
                    .menuId(menuId)
                    .allowed(allowed)
                    .build());
        }
        permRepo.saveAll(rows);
    }

    @Transactional(readOnly = true)
    public void requireAdmin(Long userId) {
        GcUser user = userRepo.findById(userId)
                .orElseThrow(() -> new ResponseStatusException(HttpStatus.UNAUTHORIZED, "로그인이 필요합니다."));
        if (!UserRole.ADMIN.name().equalsIgnoreCase(user.getRole())) {
            throw new ResponseStatusException(HttpStatus.FORBIDDEN, "관리자 권한이 필요합니다.");
        }
        if (!Boolean.TRUE.equals(user.getEnabled())) {
            throw new ResponseStatusException(HttpStatus.FORBIDDEN, "비활성화된 계정입니다.");
        }
    }

    private static Map<String, Boolean> defaultMap() {
        return MenuIds.ALL.stream().collect(Collectors.toMap(id -> id, id -> false, (a, b) -> b, LinkedHashMap::new));
    }
}
