package com.eactive.eai.rms.onl.apim.approval;

import com.eactive.apim.portal.apispec.service.ApiSpecInfoService;
import com.eactive.apim.portal.apprequest.entity.AppRequest;
import com.eactive.apim.portal.apprequest.repository.AppRequestRepository;
import com.eactive.apim.portal.approval.entity.Approval;
import com.eactive.apim.portal.approval.entity.ApprovalType;
import com.eactive.apim.portal.approval.statemachine.*;
import com.eactive.apim.portal.portalproperty.service.PortalPropertyService;
import com.eactive.apim.portal.portaluser.entity.PortalUser;
import com.eactive.apim.portal.portaluser.repository.PortalUserRepository;
import com.eactive.eai.data.entity.onl.apim.apigroup.ApiGroup;
import com.eactive.eai.rms.common.base.BaseService;
import com.eactive.eai.rms.data.entity.onl.apim.apigroup.ApiGroupService;
import com.eactive.eai.rms.data.entity.onl.apim.approval.ApprovalService;
import com.eactive.eai.rms.data.entity.onl.apim.approval.PortalApprovalUISearch;
import com.eactive.eai.rms.onl.apim.apigroup.mapping.ApiGroupUIMapper;
import com.eactive.eai.rms.onl.apim.approval.app.PortalAppApprovalListener;
import com.eactive.eai.rms.onl.apim.approval.app.PortalAppApprovalUI;
import com.eactive.eai.rms.onl.apim.approval.app.PortalAppApprovalUIMapper;
import com.eactive.eai.rms.onl.apim.approval.user.PortalUserApprovalListener;
import com.eactive.eai.rms.onl.apim.approval.user.PortalUserApprovalUI;
import com.eactive.eai.rms.onl.apim.approval.user.PortalUserApprovalUIMapper;
import com.eactive.eai.rms.onl.apim.masking.MaskingUtils;
import com.eactive.eai.rms.onl.apim.portalorg.PortalOrgManService;
import com.eactive.eai.rms.onl.apim.portaluser.PortalUserManService;
import com.eactive.eai.rms.onl.apim.portaluser.PortalUserUI;
import com.eactive.eai.rms.common.acl.user.ui.UserUI;
import com.eactive.eai.common.util.SystemUtil;
import com.eactive.eai.rms.onl.common.exception.BizException;
import com.eactive.eai.rms.onl.transaction.apim.ApiInterfaceService;
import com.eactive.eai.rms.onl.transaction.apim.mapping.ApiSpecUIMapper;
import com.eactive.eai.rms.onl.transaction.apim.ui.ApiInterfaceUI;
import com.eactive.eai.rms.onl.transaction.apim.ui.ApiSpecInfoUI;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import java.util.*;

@Service
@Transactional(transactionManager = "transactionManagerForEMS")
@RequiredArgsConstructor
public class PortalApprovalManService extends BaseService {

    private static final String PORTAL_PROPERTY_GROUP = "Portal";
    private static final String HARD_DELETE_FLAG_KEY = "approval.hard-delete.enabled";

    private final ApprovalService approvalService;
    private final ApprovalStateMachine approvalStateMachine;
    private final AppRequestRepository appRequestRepository;
    private final PortalAppApprovalListener portalAppApprovalListener;
    private final PortalAppApprovalUIMapper portalAppApprovalUIMapper;
    private final PortalUserRepository portalUserRepository;
    private final PortalUserApprovalListener portalUserApprovalListener;
    private final PortalUserApprovalUIMapper portalUserApprovalUIMapper;
    private final PortalUserManService portalUserManService;
    private final ApiGroupService apiGroupService;
    private final ApiInterfaceService apiInterfaceService;
    private final ApiSpecInfoService apiSpecInfoService;
    private final ApiSpecUIMapper apiSpecUIMapper;
    private final ApiGroupUIMapper apiGroupUIMapper;
    private final PortalApprovalAuthorizer portalApprovalAuthorizer;
    private final PortalOrgManService portalOrgManService;
    private final PortalPropertyService portalPropertyService;

    @PersistenceContext(unitName = "entityManagerFactoryForEMS")
    private EntityManager entityManager;

    public Page<PortalApprovalUI> selectList(Pageable pageable, PortalApprovalUISearch portalApprovalUISearch) {
        Page<Approval> approval = approvalService.findAll(pageable, portalApprovalUISearch);
        return approval.map(entity -> {
            try {
                return getDetailWithMaskOption(entity.getId(), false);
            } catch (Exception e) {
                logger.error("승인 상세 조회 실패 - ID: {}, 오류: {}", entity.getId(), e.getMessage());
                return null;
            }
        });
    }

    /**
     * 마스킹된 상세 정보 조회
     */
    public PortalApprovalUI selectDetail(String id) {
    	//2026.06.05 관리자가 승인요청을 조회시 심사중으로 상태 변경 
    	//APP 유형이고 REQUESTED 상태인 경우 관리자 조회 시 PROCESSING으로 변경
        approvalService.findById(id).ifPresent(approval -> {
            if (approval.getApprovalType().equals(ApprovalType.APP)
                    && approval.getApprovalStatus() instanceof RequestedState) {
                approval.setApprovalStatus(new ProcessingState());
                approvalService.save(approval);
            }
        });
        
        PortalApprovalUI result = getDetailWithMaskOption(id, false);
        if (result == null) {
            throw new IllegalArgumentException("승인 정보를 찾을 수 없습니다. ID: " + id);
        }
        return result;
    }

    /**
     * 마스킹 해제된 상세 정보 조회
     */
    public PortalApprovalUI selectDetailUnmask(String id) {
        PortalApprovalUI result = getDetailWithMaskOption(id, false);
        if (result == null) {
            throw new IllegalArgumentException("승인 정보를 찾을 수 없습니다. ID: " + id);
        }
        return result;
    }

    private PortalApprovalUI getDetailWithMaskOption(String id, boolean isMasked) {
        Optional<Approval> optApproval = approvalService.findById(id);
        if (!optApproval.isPresent()) {
            return null;
        }
        Approval approval = optApproval.get();

        if (approval.getApprovalType().equals(ApprovalType.USER)) {
            PortalUserApprovalUI userApprovalUI = portalUserApprovalUIMapper.toVo(approval);

            // 삭제된 요청자 처리
            if (userApprovalUI.getRequester() == null) {
                userApprovalUI.setRequester(createDeletedUserPlaceholder());
            }

            // 요청자 정보 마스킹 처리
            if (userApprovalUI.getRequester() != null && isMasked) {
                userApprovalUI.getRequester().setUserName(
                        MaskingUtils.maskName(userApprovalUI.getRequester().getUserName())
                );
                userApprovalUI.getRequester().setEmailAddr(
                        MaskingUtils.maskEmailId(userApprovalUI.getRequester().getEmailAddr())
                );
                userApprovalUI.getRequester().setLoginId(
                        MaskingUtils.maskEmailId(userApprovalUI.getRequester().getLoginId())
                );
                userApprovalUI.getRequester().setMobileNumber(
                        MaskingUtils.maskPhoneNumber(userApprovalUI.getRequester().getMobileNumber())
                );
            }

            // 사용자 정보 처리
            Optional<PortalUser> optUser = portalUserRepository.findById(approval.getTargetId());
            optUser.ifPresent(user -> {
                PortalUserUI userUI = isMasked ?
                    portalUserManService.selectDetail(user.getId(), true) :
                    portalUserManService.selectDetailUnmask(user.getId());

                // IP 마스킹 상태만 재설정
                if (user.getPortalOrg() != null) {
                    userUI.setPortalOrgUIs(null);  // 기존 마스킹된 PortalOrgUI 제거
                    portalOrgManService.getPortalOrgUI(user.getPortalOrg().getId(), isMasked)
                        .ifPresent(userUI::setPortalOrgUIs);
                }
                userApprovalUI.setPortalUser(userUI);
            });
            userApprovalUI.getApprovers().forEach(approver -> {
                if (approver.getUser() == null) {
                    approver.setUser(createDeletedApproverPlaceholder());
                } else {
                    approver.getUser().setUserName(MaskingUtils.maskName(approver.getUser().getUserName()));
                }
            });
            return userApprovalUI;

        } else if (approval.getApprovalType().equals(ApprovalType.APP)) {
            PortalAppApprovalUI appApprovalUI = portalAppApprovalUIMapper.toVo(approval);

            // 삭제된 요청자 처리
            if (appApprovalUI.getRequester() == null) {
                appApprovalUI.setRequester(createDeletedUserPlaceholder());
            }

            // 요청자 정보 마스킹 처리
            if (appApprovalUI.getRequester() != null && isMasked) {
                appApprovalUI.getRequester().setUserName(
                    MaskingUtils.maskName(appApprovalUI.getRequester().getUserName())
                );
                appApprovalUI.getRequester().setEmailAddr(
                        MaskingUtils.maskEmailId(appApprovalUI.getRequester().getEmailAddr())
                );
                appApprovalUI.getRequester().setLoginId(
                        MaskingUtils.maskEmailId(appApprovalUI.getRequester().getLoginId())
                );
                appApprovalUI.getRequester().setMobileNumber(
                        MaskingUtils.maskPhoneNumber(appApprovalUI.getRequester().getMobileNumber())
                );
            }

            Optional<AppRequest> optRequest = appRequestRepository.findById(approval.getTargetId());

            if (optRequest.isPresent()) {
                AppRequest appRequest = optRequest.get();
                appApprovalUI.setRequestType(appRequest.getType());


                if (appRequest.getApiGroupList() == null || appRequest.getApiGroupList().isEmpty()) {
                    appRequest.setApiGroupList("");
                }
                String[] apiGroupList = appRequest.getApiGroupList().split(",");
                for (String apiGroupId : apiGroupList) {
                    apiGroupService.findByIdWithApiList(apiGroupId).ifPresent(apiGroup ->
                            appApprovalUI.getApiServices().add(apiGroupUIMapper.toVo(apiGroup))
                    );
                }

                List<String> currentGroupNameList = new ArrayList<>();
                processApiList(appRequest.getApiList(), appApprovalUI.getApis(), currentGroupNameList);
                appApprovalUI.setApiGroupNameList(currentGroupNameList);

                if (appRequest.getPrevApiList() != null && !appRequest.getPrevApiList().isEmpty() && !appRequest.getPrevApiList().equals("null")) {

                    List<String> prevGroupNameList = new ArrayList<>();
                    List<ApiSpecInfoUI> prevApisList = new ArrayList<>();
                    processApiList(appRequest.getPrevApiList(), prevApisList, prevGroupNameList);
                    appApprovalUI.setPrevApisList(prevApisList);
                    appApprovalUI.setPrevApiGroupNameList(prevGroupNameList);
                } else {
                    appApprovalUI.setPrevApisList(null);
                    appApprovalUI.setPrevApiGroupNameList(null);
                }

                appApprovalUI.setApprovalDetail(appRequest.getReason());
                appApprovalUI.setClientName(appRequest.getClientName());

                appApprovalUI.getApprovers().forEach(approver -> {
                    if (approver.getUser() == null) {
                        approver.setUser(createDeletedApproverPlaceholder());
                    } else {
                        approver.getUser().setUserName(MaskingUtils.maskName(approver.getUser().getUserName()));
                    }
                });
            }

            return appApprovalUI;
        }
        return null;

    }

    private void processApiList(String apiListStr, List<ApiSpecInfoUI> apiList, List<String> groupNameList) {
        if (apiListStr == null || apiListStr.isEmpty()) {
            return;
        }

        String[] apiIds = apiListStr.split(",");
        for (String apiId : apiIds) {
            try {
                ApiSpecInfoUI api = apiSpecUIMapper.map(apiSpecInfoService.findById(apiId));
                apiList.add(api);

                List<ApiGroup> apiGroups = apiGroupService.findByApiId(apiId);
                if (!apiGroups.isEmpty()) {
                    StringBuilder groupNameBuilder = new StringBuilder();
                    apiGroups.forEach(apiGroup -> {
                        if (groupNameBuilder.length() > 0) {
                            groupNameBuilder.append(", ");
                        }
                        groupNameBuilder.append(apiGroup.getGroupName());
                    });
                    groupNameList.add(groupNameBuilder.toString());
                } else {
                    groupNameList.add("");
                }
            } catch (Exception e) {
                logger.error(e.getMessage(), e);
            }
        }
    }

//    private void setAppRequestDetails(PortalAppApprovalUI appApprovalUI, AppRequest appRequest) {
//        // API 그룹 정보 설정
//        String[] apiGroupList = appRequest.getApiGroupList().split(",");
//        for (String apiGroupId : apiGroupList) {
//            apiGroupService.findByIdWithApiList(apiGroupId).ifPresent(apiGroup ->
//                appApprovalUI.getApiServices().add(apiGroupUIMapper.toVo(apiGroup))
//            );
//        }
//
//        // API 정보 설정
//        String[] apiList = appRequest.getApiList().split(",");
//        for (String apiId : apiList) {
//            try {
//                ApiInterfaceUI api = apiInterfaceService.selectDetail(apiId);
//                appApprovalUI.getApis().add(api);
//            } catch (Exception e) {
//                logger.error(e.getMessage(), e);
//            }
//        }
//
//        appApprovalUI.setApprovalDetail(appRequest.getReason());
//        appApprovalUI.setClientName(appRequest.getClientName());
//    }

    private PortalUserUI createDeletedUserPlaceholder() {
        PortalUserUI placeholder = new PortalUserUI();
        placeholder.setUserName("삭제된 사용자");
        placeholder.setLoginId("");
        placeholder.setEmailAddr("");
        placeholder.setMobileNumber("");
        placeholder.setPhoneNumber("");
        return placeholder;
    }

    private UserUI createDeletedApproverPlaceholder() {
        UserUI placeholder = new UserUI();
        placeholder.setUserName("삭제된 사용자");
        placeholder.setUserId("");
        return placeholder;
    }

    public void approve(String id, String approverId) {
        Map options = new HashMap<>();

        Approval approval = approvalService.findById(id).orElseThrow(() -> new IllegalArgumentException("승인 정보를 찾을 수 없습니다. ID: " + id));
        if (!portalApprovalAuthorizer.isAuthorized(approval, approverId)) {
            throw new IllegalArgumentException("아직 승인 차례가 아닙니다.");
        }

        options.put(ApprovalConstants.APPROVER, approverId);
        sendEvent(id, ApprovalEvent.APPROVE, options);
    }

    public void reject(String id, String approverId, String rejectReason) {
        Map options = new HashMap<>();

        Approval approval = approvalService.findById(id).orElseThrow(() -> new IllegalArgumentException("승인 정보를 찾을 수 없습니다. ID: " + id));
        if (!portalApprovalAuthorizer.isAuthorized(approval, approverId)) {
            throw new IllegalArgumentException("아직 승인 차례가 아닙니다.");
        }

        options.put(ApprovalConstants.APPROVER, approverId);
        options.put(ApprovalConstants.MESSAGE, rejectReason);
        sendEvent(id, ApprovalEvent.DENY, options);
    }

    public void update(String id, String expectEndDate) {
        Approval approval = approvalService.findById(id)
                .orElseThrow(() -> new IllegalArgumentException("승인 정보를 찾을 수 없습니다. ID: " + id));
        approval.setExpectEndDate(expectEndDate);
        approvalService.save(approval);
    }

    public void redeploy(String id) {
        Map options = new HashMap<>();
        Approval approval = approvalService.findById(id).orElseThrow(() -> new IllegalArgumentException("승인 정보를 찾을 수 없습니다. ID: " + id));

        if (approval.getApprovalStatus() instanceof DeployFailedState) {
            sendEvent(id, ApprovalEvent.REDEPLOY, options);
        }

    }

    public boolean isHardDeleteEnabled() {
        // 승인 요청 완전삭제는 dev(개발) 환경에서만 허용
        if (!SystemUtil.isDevServer()) {
            return false;
        }
        Map<String, String> properties = portalPropertyService.getPortalPropertiesAsMap(PORTAL_PROPERTY_GROUP);
        return Boolean.parseBoolean(properties.getOrDefault(HARD_DELETE_FLAG_KEY, "false"));
    }

    public void hardDelete(String id) {
        if (!isHardDeleteEnabled()) {
            throw new BizException("승인 요청 완전삭제 기능이 비활성화되어 있습니다. (PTL_PROPERTY: " + HARD_DELETE_FLAG_KEY + ")");
        }
        // Approval.approvers 가 EAGER + Approver.user 가 NotFound 처리 없음 → 사라진 UserInfo 로 인해
        // findById/deleteById 가 hydration 단계에서 실패하는 row 가 존재. 네이티브 SQL 로 우회.
        entityManager.createNativeQuery("DELETE FROM PTL_APPROVER WHERE APPROVAL_ID = :id")
            .setParameter("id", id)
            .executeUpdate();
        int affected = entityManager.createNativeQuery("DELETE FROM PTL_APPROVAL WHERE ID = :id")
            .setParameter("id", id)
            .executeUpdate();
        if (affected == 0) {
            throw new BizException("승인 정보를 찾을 수 없습니다. ID: " + id);
        }
    }

    public void sendEvent(String id, ApprovalEvent event, Map<String, Object> options) {
        approvalService.findById(id).ifPresent(approval -> {
            options.put("authorizer", portalApprovalAuthorizer);

            if (approval.getApprovalType().equals(ApprovalType.APP)) {
                options.put("listener", portalAppApprovalListener);
            }

            if (approval.getApprovalType().equals(ApprovalType.USER)) {
                logger.debug("Setting listener - approval ID: {}, listener type: {}",
                    approval.getId(),
                    portalUserApprovalListener.getClass().getSimpleName());
                options.put("listener", portalUserApprovalListener);
            }

            logger.debug("Sending event - ID: {}, Event: {}", id, event);
            approvalStateMachine.transition(approval, event, options);

            if (approval.getApprovalStatus() instanceof DeniedState || approval.getApprovalStatus() instanceof ApprovedState) {
                logger.debug("Sending END event after DENY - ID: {}", id);
                approvalStateMachine.transition(approval, ApprovalEvent.END, options);  // DeniedState의 handleEnd() 호출을 위한 END 이벤트
            }

            approvalService.save(approval);
        });
    }

}
