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

import com.eactive.apim.portal.file.entity.FileDetail;
import com.eactive.apim.portal.file.entity.FileInfo;
import com.eactive.apim.portal.file.service.FileService;
import com.eactive.apim.portal.file.service.FileTypeContext;
import com.eactive.apim.portal.portalorg.entity.PortalOrg;
import com.eactive.apim.portal.portalorg.entity.PortalOrgEnums.ApprovalStatus;
import com.eactive.apim.portal.portalorg.entity.PortalOrgEnums.OrgStatus;
import com.eactive.apim.portal.portaluser.entity.PortalUser;
import com.eactive.eai.common.util.SystemUtil;
import com.eactive.eai.common.util.UUID;
import com.eactive.eai.rms.common.base.BaseService;
import com.eactive.eai.rms.data.entity.onl.apim.portalorg.PortalOrgService;
import com.eactive.eai.rms.data.entity.onl.apim.portalorg.PortalOrgUISearch;
import com.eactive.eai.rms.data.entity.onl.apim.portaluser.PortalUserService;
import com.eactive.eai.rms.onl.apim.masking.MaskingUtils;
import com.eactive.eai.rms.onl.apim.portaluser.PortalUserCorpManagerUI;
import com.eactive.eai.rms.onl.apim.portaluser.PortalUserCorpManagerUIMapper;
import com.eactive.eai.rms.onl.common.exception.BizException;
import com.eactive.apim.portal.portalproperty.service.PortalPropertyService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
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 org.springframework.web.multipart.MultipartFile;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

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

    /** PortalProperty 그룹명 */
    private static final String PORTAL_PROPERTY_GROUP = "Portal";
    /** 법인 완전삭제(hard delete) 허용 프로퍼티 키 (true:허용, 기본 false) */
    private static final String HARD_DELETE_FLAG_KEY = "org.hard-delete.enabled";

    private final PortalOrgService portalOrgService;
    private final PortalOrgUIMapper portalOrgUIMapper;
    private final PortalUserService portalUserService;
    private final PortalUserCorpManagerUIMapper portalUserCorpManagerUIMapper;
    private final FileService fileService;
    private final PortalPropertyService portalPropertyService;

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

    /** 법인 목록 '선택 삭제'(완전삭제) 버튼 노출 여부 */
    public boolean isHardDeleteEnabled() {
        Map<String, String> properties = portalPropertyService.getPortalPropertiesAsMap(PORTAL_PROPERTY_GROUP);
        return Boolean.parseBoolean(properties.getOrDefault(HARD_DELETE_FLAG_KEY, "false"));
    }

    public Page<PortalOrgUI> selectList(Pageable pageable, PortalOrgUISearch portalOrgUISearch) {
        Page<PortalOrg> portalOrg = portalOrgService.findAll(pageable, portalOrgUISearch);
        return portalOrg.map(entity -> convertToUIWithMaskOption(entity, true));
    }

    public PortalOrgUI selectDetail(String id) {
        PortalOrg portalOrg = portalOrgService.getById(id);
        return portalOrgUIMapper.toVo(portalOrg);
    }

    public void insert(PortalOrgUI portalOrgUI) throws IOException {
        // 사업자등록증 필수 체크에서 제외함.
//        if (portalOrgUI.getCompRegFileUpload() == null) {
//            throw new BizException("사업자 등록증을 업로드 해주세요.");
//        }

        decodePortalOrgUIFields(portalOrgUI);
        PortalOrg portalOrg = portalOrgUIMapper.toEntity(portalOrgUI);

        if (!StringUtils.isEmpty(portalOrg.getCompRegFile())) {
            MultipartFile file = portalOrgUI.getCompRegFileUpload();
            if (file != null) {
                String decodedFileName = decodeString(portalOrgUI.getFileName());
                FileTypeContext.setFileType("certificate");
                FileInfo fileInfo = fileService.createOrUpdateSingleFile(null, file, decodedFileName);
                portalOrg.setCompRegFile(fileInfo.getFileId());
            }
        }

        if (portalOrg.getOrgStatus() == null) {
            portalOrg.setOrgStatus(OrgStatus.ACTIVE);
        }

        if (portalOrg.getApprovalStatus() == null) {
            portalOrg.setApprovalStatus(ApprovalStatus.COMPLETED);
        }

        if (portalOrg.getId() == null || portalOrg.getId().isEmpty()) {
            portalOrg.setId(UUID.randomUUID().toString());
        }
        portalOrgService.save(portalOrg);
    }

    private String decodeString(String value) {
        if (value == null) {
            return null;
        }
//		return new String(value.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8); // local
        return new String(value.getBytes(Charset.defaultCharset()), StandardCharsets.UTF_8);    // server
    }

    private void decodePortalOrgUIFields(PortalOrgUI portalOrgUI) {
        portalOrgUI.setOrgName(decodeString(portalOrgUI.getOrgName()));         // 법인명
        portalOrgUI.setOrgCode(decodeString(portalOrgUI.getOrgCode()));         // 법인명
        portalOrgUI.setOrgIndustryType(decodeString(portalOrgUI.getOrgIndustryType())); // 업종
        portalOrgUI.setOrgSectors(decodeString(portalOrgUI.getOrgSectors()));   // 업태
        portalOrgUI.setCeoName(decodeString(portalOrgUI.getCeoName()));         // 대표자성명
        portalOrgUI.setOrgAddr(decodeString(portalOrgUI.getOrgAddr()));         // 사업장소재지
        portalOrgUI.setServiceName(decodeString(portalOrgUI.getServiceName())); // 서비스명
        portalOrgUI.setScPhoneNumber(decodeString(portalOrgUI.getScPhoneNumber()));
        portalOrgUI.setOrgPhoneNumber(decodeString(portalOrgUI.getOrgPhoneNumber()));
    }

    public void update(PortalOrgUI portalOrgUI) throws IOException {
        decodePortalOrgUIFields(portalOrgUI);
        PortalOrg portalOrg = portalOrgService.getById(portalOrgUI.getId());
        portalOrgUIMapper.updateToEntity(portalOrgUI, portalOrg);

        // 새로운 파일이 업로드된 경우에만 파일 처리
        if (portalOrgUI.getCompRegFileUpload() != null && !portalOrgUI.getCompRegFileUpload().isEmpty()) {
            MultipartFile file = portalOrgUI.getCompRegFileUpload();
            String decodedFileName = decodeString(portalOrgUI.getFileName());
            FileTypeContext.setFileType("certificate");
            FileInfo fileInfo = fileService.createOrUpdateSingleFile(portalOrg.getCompRegFile(), file, decodedFileName);
            portalOrgUI.setCompRegFile(fileInfo.getFileId());
            // 엔티티에 새 파일 ID 설정
            portalOrgUIMapper.updateToEntity(portalOrgUI, portalOrg);
        }

        portalOrgService.save(portalOrg);
    }

    public void removeOrg(String id) {
        PortalOrg portalOrg = portalOrgService.getById(id);
        portalOrg.setOrgName("삭제된법인");
        portalOrg.setOrgStatus(OrgStatus.REMOVED);
        portalOrg.clearSensitiveDataOnRemoval();
        portalOrgService.save(portalOrg);
    }

    public void hardDeleteOrg(String id) {
        if (SystemUtil.isProdServer()) {
            throw new BizException("운영 환경에서는 완전삭제를 수행할 수 없습니다.");
        }
        PortalOrg portalOrg = portalOrgService.getById(id);
        if (portalOrg.getOrgStatus() != OrgStatus.REMOVED) {
            throw new BizException("삭제(탈퇴) 상태인 법인만 완전삭제할 수 있습니다.");
        }

        // 사용자(PortalUser)는 별도 화면에서 정리해야 하므로 cascade 하지 않고 차단
        long userCount = portalUserService.countByOrgId(id);
        if (userCount > 0) {
            throw new BizException("의존 데이터가 존재하여 완전삭제할 수 없습니다: 사용자 " + userCount + "건");
        }

        // 앱 신청(AppRequest) + 연결된 승인(Approval/Approver) 은 cascade 삭제
        cascadeDeleteAppRequestsByOrg(id);

        portalOrgService.deleteById(id);
    }

    private void cascadeDeleteAppRequestsByOrg(String orgId) {
        // 1) 해당 org 의 app_request 가 참조하는 approval 의 approver 제거
        entityManager.createNativeQuery(
            "DELETE FROM PTL_APPROVER WHERE APPROVAL_ID IN " +
            "(SELECT APPROVAL_ID FROM PTL_APP_REQUEST WHERE ORG_ID = :orgId AND APPROVAL_ID IS NOT NULL)"
        ).setParameter("orgId", orgId).executeUpdate();

        // 2) approval 제거
        entityManager.createNativeQuery(
            "DELETE FROM PTL_APPROVAL WHERE ID IN " +
            "(SELECT APPROVAL_ID FROM PTL_APP_REQUEST WHERE ORG_ID = :orgId AND APPROVAL_ID IS NOT NULL)"
        ).setParameter("orgId", orgId).executeUpdate();

        // 3) app_request 제거
        entityManager.createNativeQuery(
            "DELETE FROM PTL_APP_REQUEST WHERE ORG_ID = :orgId"
        ).setParameter("orgId", orgId).executeUpdate();
    }

    public Map<String, Object> selectDetailWithUsers(String id) {
        return getDetailWithUsers(id, true);
    }

    private Map<String, Object> getDetailWithUsers(String id, boolean isMasked) {
        PortalOrg portalOrg = portalOrgService.findById(id)
                .orElseThrow(() -> new BizException("해당 법인을 찾을 수 없습니다."));

        PortalOrgUI portalOrgUI = convertToUIWithMaskOption(portalOrg, isMasked);

        Map<String, Object> result = new HashMap<>();
        result.put("portalOrg", portalOrgUI);

        // Corporate Manager 처리
        portalUserService.findCorporateManagerByOrgId(id)
                .map(corporateManager -> convertCorporateManagerToUIWithMaskOption(corporateManager, isMasked))
                .ifPresent(corporateManagerUI -> result.put("corporateManager", corporateManagerUI));

        // 파일 정보 추가
        setFileInfo(portalOrg, portalOrgUI);

        return result;
    }

    private PortalOrgUI convertToUIWithMaskOption(PortalOrg portalOrg, boolean isMasked) {
        PortalOrgUI ui = portalOrgUIMapper.toVo(portalOrg);

        if (ui != null && isMasked) {
            if (StringUtils.isNotBlank(ui.getIpWhitelist())) {
                ui.setIpWhitelist(MaskingUtils.maskIpAddress(ui.getIpWhitelist()));
            }
        }
        return ui;
    }

    private PortalUserCorpManagerUI convertCorporateManagerToUIWithMaskOption(PortalUser corporateManager, boolean isMasked) {
        PortalUserCorpManagerUI ui = portalUserCorpManagerUIMapper.toVo(corporateManager);

        return ui;
    }

    private void setFileInfo(PortalOrg portalOrg, PortalOrgUI ui) {
        Optional.ofNullable(portalOrg.getCompRegFile())
                .filter(StringUtils::isNotBlank)
                .map(fileService::findById)
                .map(fileInfo -> {
                    ui.setFileInfo(fileInfo);
                    if (!fileInfo.getFileDetails().isEmpty()) {
                        FileDetail fileDetail = fileInfo.getFileDetails().get(0);
                        ui.setFileName(fileDetail.getOriginalFileName() + "." + fileDetail.getFileExtension());
                    }
                    return fileInfo;
                });
    }

    public Optional<PortalOrgUI> getPortalOrgUI(String portalOrgId) {
        return portalOrgService.findById(portalOrgId)
                .map(portalOrgUIMapper::toVo);
    }

    public Optional<PortalOrgUI> getPortalOrgUI(String portalOrgId, boolean isMasked) {
        return portalOrgService.findById(portalOrgId)
                .map(portalOrg -> {
                    PortalOrgUI ui = portalOrgUIMapper.toVo(portalOrg);
                    if (ui != null) {
                        // IP 마스킹 처리
                        if (isMasked && StringUtils.isNotBlank(ui.getIpWhitelist())) {
                            ui.setIpWhitelist(MaskingUtils.maskIpAddress(ui.getIpWhitelist()));
                        }

                        // 파일 정보 처리 추가
                        setFileInfo(portalOrg, ui);
                    }
                    return ui;
                });
    }

    public Optional<PortalOrg> findById(String orgId) {
        return portalOrgService.findById(orgId);
    }
}
