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

import com.eactive.apim.portal.djb.apistatus.incident.entity.DjbApistatusIncident;
import com.eactive.apim.portal.djb.apistatus.incident.entity.DjbApistatusIncidentApi;
import com.eactive.apim.portal.djb.apistatus.incident.entity.DjbApistatusIncidentApiId;
import com.eactive.apim.portal.djb.apistatus.incident.entity.IncidentKind;
import com.eactive.apim.portal.djb.apistatus.incident.entity.IncidentState;
import com.eactive.apim.portal.djb.apistatus.incident.repository.DjbApistatusIncidentApiRepository;
import com.eactive.apim.portal.djb.apistatus.incident.repository.DjbApistatusIncidentRepository;
import com.eactive.apim.portal.djb.apistatus.incident.repository.DjbApistatusIncidentTimelineRepository;
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.portalNotice.entity.PortalNotice;
import com.eactive.eai.common.util.ContainerUtil;
import com.eactive.eai.rms.common.base.BaseService;
import com.eactive.eai.rms.data.entity.onl.apim.portalnotice.PortalNoticeService;
import com.eactive.eai.rms.data.entity.onl.apim.portalnotice.PortalNoticeUISearch;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
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 java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

@Service
@Transactional(transactionManager = "transactionManagerForEMS")
@Slf4j
public class PortalNoticeManService extends BaseService {
	private final PortalNoticeService portalNoticeService;
	private final PortalNoticeUIMapper portalNoticeUIMapper;
  	private final FileService fileService;
	private final DjbApistatusIncidentRepository incidentRepository;
	private final DjbApistatusIncidentApiRepository incidentApiRepository;
	private final DjbApistatusIncidentTimelineRepository incidentTimelineRepository;

	private String decodeString(String value) {
		if (ContainerUtil.get() == ContainerUtil.TOMCAT) {
			try {
				return new String(value.getBytes("euc-kr"), StandardCharsets.UTF_8);
			} catch (UnsupportedEncodingException e) {
				log.warn("Failed euc-kr to UTF-8", e);
				return value;
			}
		}
		return new String(value.getBytes(Charset.defaultCharset()), StandardCharsets.UTF_8);
	}

	@Autowired
	public PortalNoticeManService(PortalNoticeService portalNoticeService,
								  PortalNoticeUIMapper portalNoticeUIMapper,
								  FileService fileService,
								  DjbApistatusIncidentRepository incidentRepository,
								  DjbApistatusIncidentApiRepository incidentApiRepository,
								  DjbApistatusIncidentTimelineRepository incidentTimelineRepository){
		this.portalNoticeService = portalNoticeService;
		this.portalNoticeUIMapper = portalNoticeUIMapper;
		this.fileService = fileService;
		this.incidentRepository = incidentRepository;
		this.incidentApiRepository = incidentApiRepository;
		this.incidentTimelineRepository = incidentTimelineRepository;
	}

	private static boolean isIncidentKind(String noticeType) {
		return PortalNoticeUI.NOTICE_TYPE_INCIDENT.equals(noticeType)
				|| PortalNoticeUI.NOTICE_TYPE_MAINTENANCE.equals(noticeType);
	}

	private static IncidentKind toKind(String noticeType) {
		if (PortalNoticeUI.NOTICE_TYPE_INCIDENT.equals(noticeType)) return IncidentKind.INCIDENT;
		if (PortalNoticeUI.NOTICE_TYPE_MAINTENANCE.equals(noticeType)) return IncidentKind.MAINTENANCE;
		return null;
	}


	public Page<PortalNoticeUI> selectList(Pageable pageable, PortalNoticeUISearch portalNoticeUISearch) {
		return portalNoticeService.findAll(pageable, portalNoticeUISearch)
				.map(portalNotice -> {
					PortalNoticeUI ui = portalNoticeUIMapper.toVo(portalNotice);
					setFileInfo(portalNotice, ui);
					return ui;
				});
	}

	private void setFileInfo(PortalNotice portalNotice, PortalNoticeUI ui) {
		if (StringUtils.isBlank(portalNotice.getFileId())) {
			ui.setHasAttachment(false);
			return;
		}

		FileInfo fileInfo = fileService.findById(portalNotice.getFileId());

		if (fileInfo == null || fileInfo.getFileDetails().isEmpty()) {
			ui.setHasAttachment(false);
		} else {
			FileDetail fileDetail = fileInfo.getFileDetails().get(0);
			String fileNames = fileDetail.getOriginalFileName() + "." + fileDetail.getFileExtension();
			ui.setFileName(fileNames);
			ui.setHasAttachment(true);
			ui.setFileInfo(fileInfo);
		}
	}

	public PortalNoticeUI selectDetail(String id) {
		PortalNotice portalNotice = portalNoticeService.getById(id);
		portalNotice.setReadCount(portalNotice.getReadCount() + 1);

		PortalNoticeUI ui = portalNoticeUIMapper.toVo(portalNotice);
		setFileInfo(portalNotice, ui);
		populateIncident(portalNotice, ui);
		return ui;
	}

	private void populateIncident(PortalNotice portalNotice, PortalNoticeUI ui) {
		if (!isIncidentKind(portalNotice.getNoticeType())) {
			ui.setAffectedApis(Collections.emptyList());
			return;
		}
		Optional<DjbApistatusIncident> incidentOpt = incidentRepository.findByNoticeId(portalNotice.getId());
		if (!incidentOpt.isPresent()) {
			ui.setAffectedApis(Collections.emptyList());
			return;
		}
		DjbApistatusIncident incident = incidentOpt.get();
		ui.setIncidentId(incident.getIncidentId());
		ui.setSummary(incident.getSummary());
		ui.setStartedAt(incident.getStartedAt());
		ui.setEndAt(incident.getEndAt());
		ui.setState(incident.getState() == null ? null : incident.getState().name());
		ui.setPreviousState(incident.getPreviousState() == null ? null : incident.getPreviousState().name());

		List<IncidentAffectedApiUI> apis = incidentApiRepository
				.findByIncidentIdOrderByApiId(incident.getIncidentId()).stream()
				.map(api -> {
					IncidentAffectedApiUI vo = new IncidentAffectedApiUI();
					vo.setApiId(api.getApiId());
					vo.setApiName(api.getApiName());
					return vo;
				}).collect(Collectors.toList());
		ui.setAffectedApis(apis);
	}

	public void insert(PortalNoticeUI portalNoticeUI) throws IOException {
		//portalNoticeUI.setNoticeSubject(decodeString(portalNoticeUI.getNoticeSubject()));
		//portalNoticeUI.setNoticeDetail(decodeString(StringEscapeUtils.unescapeHtml(portalNoticeUI.getNoticeDetail())));
		portalNoticeUI.setNoticeDetail(StringEscapeUtils.unescapeHtml(portalNoticeUI.getNoticeDetail()));

		PortalNotice portalNotice = portalNoticeUIMapper.toEntity(portalNoticeUI);
		portalNotice.setId(null);
		portalNotice.setReadCount(0L);

		if (portalNoticeUI.getFiles() != null && !portalNoticeUI.getFiles().isEmpty()) {
			handleFileUpload(portalNoticeUI, portalNotice);
		}

		portalNoticeService.save(portalNotice);

		if (isIncidentKind(portalNoticeUI.getNoticeType())) {
			persistIncident(portalNoticeUI, portalNotice, true);
		}
	}

	private void persistIncident(PortalNoticeUI portalNoticeUI, PortalNotice portalNotice, boolean isCreate) {
		IncidentKind kind = toKind(portalNoticeUI.getNoticeType());

		DjbApistatusIncident incident = incidentRepository.findByNoticeId(portalNotice.getId())
				.orElseGet(DjbApistatusIncident::new);

		incident.setKind(kind);
		incident.setTitle(portalNotice.getNoticeSubject());
		incident.setSummary(portalNoticeUI.getSummary());
		incident.setStartedAt(portalNoticeUI.getStartedAt() != null
				? portalNoticeUI.getStartedAt() : LocalDateTime.now());
		incident.setEndAt(portalNoticeUI.getEndAt());
		incident.setDetectedBy("MANUAL");
		incident.setNoticeId(portalNotice.getId());
		incident.setDraftYn("N");
		incident.setFixYn(StringUtils.defaultIfBlank(portalNotice.getFixYn(), "N"));

		if (kind == IncidentKind.INCIDENT) {
			IncidentState newState = StringUtils.isNotBlank(portalNoticeUI.getState())
					? IncidentState.valueOf(portalNoticeUI.getState())
					: IncidentState.INVESTIGATING;
			if (!isCreate && incident.getState() != newState) {
				incident.setPreviousState(incident.getState());
			}
			incident.setState(newState);
		} else {
			incident.setState(null);
			incident.setPreviousState(null);
		}

		DjbApistatusIncident saved = incidentRepository.save(incident);

		// 영향 API 동기화 — 단순 전체 삭제 후 재삽입
		incidentApiRepository.deleteByIncidentId(saved.getIncidentId());
		List<IncidentAffectedApiUI> affected = portalNoticeUI.getAffectedApis();
		if (affected != null && !affected.isEmpty()) {
			List<DjbApistatusIncidentApi> rows = new ArrayList<>();
			for (IncidentAffectedApiUI src : affected) {
				if (StringUtils.isBlank(src.getApiId())) continue;
				DjbApistatusIncidentApi api = new DjbApistatusIncidentApi();
				api.setIncidentId(saved.getIncidentId());
				api.setApiId(src.getApiId());
				api.setApiName(src.getApiName());
				rows.add(api);
			}
			if (!rows.isEmpty()) {
				incidentApiRepository.saveAll(rows);
			}
		}
	}

	private void handleFileUpload(PortalNoticeUI portalNoticeUI, PortalNotice portalNotice) throws IOException {
		MultipartFile file = portalNoticeUI.getFiles();

		if (file != null && !file.isEmpty()) {  // 새로운 파일이 업로드된 경우에만 처리
			String decodedFileName = decodeString(portalNoticeUI.getFileName());
			FileTypeContext.setFileType("notice");
			FileInfo fileInfo = fileService.createOrUpdateSingleFile(portalNotice.getFileId(), file, decodedFileName, true);

			if (fileInfo != null) {
				portalNotice.setFileId(fileInfo.getFileId());
			}
		}

	}

	public void update(PortalNoticeUI portalNoticeUI) throws IOException {
		//portalNoticeUI.setNoticeSubject(decodeString(portalNoticeUI.getNoticeSubject()));
 		//portalNoticeUI.setNoticeDetail(decodeString(StringEscapeUtils.unescapeHtml(portalNoticeUI.getNoticeDetail())));
		portalNoticeUI.setNoticeDetail(StringEscapeUtils.unescapeHtml(portalNoticeUI.getNoticeDetail()));

		PortalNotice portalNotice = portalNoticeService.getById(portalNoticeUI.getId());
		String previousNoticeType = portalNotice.getNoticeType();

		if (portalNoticeUI.getFiles() != null && !portalNoticeUI.getFiles().isEmpty()) {
			handleFileUpload(portalNoticeUI, portalNotice);
		} else if (portalNoticeUI.isFileDeleted() && StringUtils.isNotBlank(portalNotice.getFileId()))  {
				fileService.deleteFile(portalNotice.getFileId());
				portalNotice.setFileId(null);
		}

		portalNoticeUIMapper.updateToEntity(portalNoticeUI, portalNotice);
		portalNoticeService.save(portalNotice);

		if (isIncidentKind(portalNotice.getNoticeType())) {
			persistIncident(portalNoticeUI, portalNotice, false);
		} else if (isIncidentKind(previousNoticeType)) {
			// 일반 공지로 유형 변경 → 기존 incident 정리
			deleteIncidentIfExists(portalNotice.getId());
		}
	}

	private void deleteIncidentIfExists(String noticeId) {
		incidentRepository.findByNoticeId(noticeId).ifPresent(incident -> {
			incidentApiRepository.deleteByIncidentId(incident.getIncidentId());
			incidentTimelineRepository.deleteByIncidentId(incident.getIncidentId());
			incidentRepository.delete(incident);
		});
	}

	public void delete(String id) {
		PortalNotice portalNotice = portalNoticeService.getById(id);
		Optional.ofNullable(portalNotice.getFileId()).ifPresent(fileService::deleteFile);
		deleteIncidentIfExists(id);
		portalNoticeService.deleteById(id);
	}

	public void deleteBulk(String ids) {
		if (StringUtils.isBlank(ids)) {
			return;
		}

		List<String> idList = Arrays.asList(ids.split(","));

		// 첨부파일 정리 (단건 삭제와 동일한 처리)
		idList.forEach(id -> {
			PortalNotice portalNotice = portalNoticeService.getById(id);
			Optional.ofNullable(portalNotice.getFileId()).ifPresent(fileService::deleteFile);
		});

		portalNoticeService.deleteByIds(idList);
	}

}
