package com.pandoli365.bibimbap.controller;

import com.pandoli365.bibimbap.data.OperatorView;
import com.pandoli365.bibimbap.data.UserData;
import com.pandoli365.bibimbap.mapper.PermissionsMapper;
import com.pandoli365.bibimbap.mapper.RbacAuditMapper;
import com.pandoli365.bibimbap.mapper.UserPermissionsMapper;
import com.pandoli365.bibimbap.mapper.UsersMapper;
import com.pandoli365.bibimbap.security.CsrfTokens;
import com.pandoli365.bibimbap.security.PermissionKeys;
import com.pandoli365.bibimbap.security.Roles;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

@Controller
public class AdminConsoleController {

    private final UsersMapper usersMapper;
    private final UserPermissionsMapper userPermissionsMapper;
    private final RbacAuditMapper auditMapper;
    private final PermissionsMapper permissionsMapper;

    public AdminConsoleController(UsersMapper usersMapper,
                                  UserPermissionsMapper userPermissionsMapper,
                                  RbacAuditMapper auditMapper,
                                  PermissionsMapper permissionsMapper) {
        this.usersMapper = usersMapper;
        this.userPermissionsMapper = userPermissionsMapper;
        this.auditMapper = auditMapper;
        this.permissionsMapper = permissionsMapper;
    }

    @GetMapping("/admin/console")
    public String console(Model model, HttpServletRequest request) {
        model.addAttribute("operators", buildOperators());
        model.addAttribute("catalog", permissionsMapper.listActive());
        model.addAttribute("csrfToken", CsrfTokens.getOrCreate(request.getSession()));
        return "admin-console";
    }

    @PostMapping("/admin/users/{userId}/appoint")
    @Transactional
    public ResponseEntity<Map<String, Object>> appoint(@PathVariable("userId") long userId,
                                                       HttpServletRequest request,
                                                       HttpSession session) {
        if (!CsrfTokens.isValid(request)) {
            return ResponseEntity.status(HttpStatus.FORBIDDEN).body(CsrfTokens.errorBody());
        }
        UserData target = usersMapper.getUser(userId);
        if (target == null) {
            return response(HttpStatus.NOT_FOUND, "대상 사용자를 찾을 수 없습니다.");
        }
        if (!Roles.USER.equals(target.getRole())) {
            return response(HttpStatus.CONFLICT, "이미 운영진이거나 승격 대상이 아닙니다.");
        }
        Long actorId = sessionUserId(session);
        usersMapper.updateRole(userId, Roles.SUBADMIN);
        usersMapper.bumpPermissionsEpoch(userId);
        auditMapper.insert(actorId, userId, "APPOINT", null);

        Map<String, Object> body = new LinkedHashMap<>();
        body.put("status", HttpStatus.OK.value());
        body.put("message", "운영진으로 임명했습니다.");
        body.put("userId", userId);
        body.put("role", Roles.SUBADMIN);
        return ResponseEntity.ok(body);
    }

    @PostMapping("/admin/users/{userId}/permissions/{permissionKey}/toggle")
    @Transactional
    public ResponseEntity<Map<String, Object>> togglePermission(@PathVariable("userId") long userId,
                                                                @PathVariable("permissionKey") String permissionKey,
                                                                HttpServletRequest request,
                                                                HttpSession session) {
        if (!CsrfTokens.isValid(request)) {
            return ResponseEntity.status(HttpStatus.FORBIDDEN).body(CsrfTokens.errorBody());
        }
        if (!PermissionKeys.isValid(permissionKey)) {
            return response(HttpStatus.NOT_FOUND, "존재하지 않는 권한 키입니다.");
        }
        UserData target = usersMapper.getUser(userId);
        if (target == null) {
            return response(HttpStatus.NOT_FOUND, "대상 사용자를 찾을 수 없습니다.");
        }
        if (!Roles.SUBADMIN.equals(target.getRole())) {
            return response(HttpStatus.UNPROCESSABLE_ENTITY, "권한은 SUBADMIN 에게만 부여할 수 있습니다.");
        }
        Long actorId = sessionUserId(session);
        boolean granted;
        if (userPermissionsMapper.exists(userId, permissionKey)) {
            userPermissionsMapper.delete(userId, permissionKey);
            granted = false;
        } else {
            userPermissionsMapper.insert(userId, permissionKey, actorId);
            granted = true;
        }
        usersMapper.bumpPermissionsEpoch(userId);
        auditMapper.insert(actorId, userId, granted ? "GRANT" : "REVOKE", permissionKey);

        Map<String, Object> body = new LinkedHashMap<>();
        body.put("status", HttpStatus.OK.value());
        body.put("message", granted ? "권한을 부여했습니다." : "권한을 회수했습니다.");
        body.put("userId", userId);
        body.put("permissionKey", permissionKey);
        body.put("granted", granted);
        return ResponseEntity.ok(body);
    }

    @PostMapping("/admin/users/{userId}/demote")
    @Transactional
    public ResponseEntity<Map<String, Object>> demote(@PathVariable("userId") long userId,
                                                      HttpServletRequest request,
                                                      HttpSession session) {
        if (!CsrfTokens.isValid(request)) {
            return ResponseEntity.status(HttpStatus.FORBIDDEN).body(CsrfTokens.errorBody());
        }
        UserData target = usersMapper.getUser(userId);
        if (target == null) {
            return response(HttpStatus.NOT_FOUND, "대상 사용자를 찾을 수 없습니다.");
        }
        if (!Roles.SUBADMIN.equals(target.getRole())) {
            return response(HttpStatus.UNPROCESSABLE_ENTITY, "강등 대상이 아닙니다.");
        }
        Long actorId = sessionUserId(session);
        int revokedCount = userPermissionsMapper.deleteAllByUser(userId);
        usersMapper.updateRole(userId, Roles.USER);
        usersMapper.bumpPermissionsEpoch(userId);
        auditMapper.insert(actorId, userId, "DEMOTE", null);

        Map<String, Object> body = new LinkedHashMap<>();
        body.put("status", HttpStatus.OK.value());
        body.put("message", "운영진에서 해임했습니다.");
        body.put("userId", userId);
        body.put("role", Roles.USER);
        body.put("revokedCount", revokedCount);
        return ResponseEntity.ok(body);
    }

    @GetMapping("/admin/operators")
    public ResponseEntity<Map<String, Object>> operators() {
        List<OperatorView> ops = buildOperators();
        List<Map<String, Object>> operatorList = new ArrayList<>();
        for (OperatorView op : ops) {
            Map<String, Object> entry = new LinkedHashMap<>();
            entry.put("userId", op.getUserId());
            entry.put("displayName", op.getDisplayName());
            entry.put("role", op.getRole());
            entry.put("permissions", op.getPermissionKeys());
            operatorList.add(entry);
        }
        Map<String, Object> body = new LinkedHashMap<>();
        body.put("status", HttpStatus.OK.value());
        body.put("operators", operatorList);
        return ResponseEntity.ok(body);
    }

    private List<OperatorView> buildOperators() {
        List<OperatorView> ops = usersMapper.listOperators();
        for (OperatorView op : ops) {
            op.setPermissionKeys(userPermissionsMapper.listKeys(op.getUserId()));
        }
        return ops;
    }

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

    private ResponseEntity<Map<String, Object>> response(HttpStatus status, String message) {
        Map<String, Object> body = new LinkedHashMap<>();
        body.put("status", status.value());
        body.put("message", message);
        return ResponseEntity.status(status).body(body);
    }
}
