package com.arms.api.issue.almapi.service;

import java.time.Instant;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

import com.arms.api.issue.almapi.model.vo.AlmIssueWithRequirementVO;
import com.arms.api.issue.almapi.model.entity.AlmIssueEntity;
import com.arms.api.issue.almapi.model.vo.AlmIssueVOCollection;
import com.arms.api.issue.almapi.model.vo.AlmIssueVO;

import com.arms.api.issue.almapi.repository.AlmIssueToEntityConverterRepository;
import lombok.extern.slf4j.Slf4j;

import static com.arms.api.issue.almapi.model.entity.AlmIssueEntity.*;
import static com.arms.api.util.ApplicationContextProvider.getBean;
import static java.util.stream.Collectors.toList;

@Slf4j
public class AlmIssueToEntityConverter {

	private final AlmIssueEntityCollection almIssueEntityCollection = new AlmIssueEntityCollection();

	private final AlmIssueVOCollection almIssueVOCollection;

	private AlmIssueToEntityConverter(AlmIssueVOCollection almIssueVOCollection)  {
		this.almIssueVOCollection = almIssueVOCollection;
	}

	public static AlmIssueToEntityConverter discoveryRequirementIssue(AlmIssueVOCollection almIssueCollection) {

		AlmIssueToEntityConverter almIssueToEntityConverter = new AlmIssueToEntityConverter(almIssueCollection);

		almIssueToEntityConverter.createRequirementEntity();

		return almIssueToEntityConverter;
	}

	public static AlmIssueToEntityConverter discoveryNoneRequirementIssue(AlmIssueVOCollection almIssueCollections){

		AlmIssueToEntityConverter almIssueToEntityConverter = new AlmIssueToEntityConverter(almIssueCollections);

		almIssueToEntityConverter.createNoneRequirementEntities();

		return almIssueToEntityConverter;
	}

	private void createRequirementEntity() {

		List<AlmIssueVO> almIssueVOS = this.almIssueVOCollection.listWithLinkedIssue();

		almIssueVOS
				.stream()
				.filter(vo -> {
					AlmIssueEntity recentDocByRecentId = getBean(AlmIssueToEntityConverterRepository.class).findRecentDocByRecentId(vo.recentId());
					return recentDocByRecentId.izReqTrue()&&vo.getAlmIssueWithRequirementVO().getIssueKey().equals(recentDocByRecentId.getKey());
				})
				.forEach(vo-> almIssueEntityCollection.addEntity(
						this.createEntity(vo)
				));

	}

	private void createNoneRequirementEntities() {

		List<AlmIssueVO> relationList = almIssueVOCollection.listWithLinkedIssue();

		List<AlmIssueEntity> almIssueEntities = relationList
				.stream()
				.filter(AlmIssueVO::isIncludeFromSave)
				.distinct()
				.map(this::createEntity)
				.filter(this::isPossibleTraceParentKeyOrIsSelfGeneratedIssue)
				.filter(AlmIssueEntity::izReqFalse)
				.toList();

		almIssueEntityCollection.addEntities(almIssueEntities);
	}

	private boolean isPossibleTraceParentKeyOrIsSelfGeneratedIssue(AlmIssueEntity almIssueEntity) {
		return	   (almIssueEntity.getUpperKey() == null && almIssueEntity.getParentReqKey() == null  )
				|| (almIssueEntity.getUpperKey() != null && almIssueEntity.getParentReqKey() != null);
	}

	private AlmIssueEntity createEntity(AlmIssueVO reqAlmIssueVO) {

		AlmIssueWithRequirementVO almIssueWithRequirementVO = reqAlmIssueVO.getAlmIssueWithRequirementVO();

		String upperKey = reqAlmIssueVO.getUpperKey();

		AlmIssueVO almIssueVO
				= Optional.ofNullable(upperKey)
					.map(a-> reqAlmIssueVO.parentKey(almIssueWithRequirementVO.getIssueKey())).orElse(reqAlmIssueVO);

		Long cReqStatusId = almIssueWithRequirementVO.getCReqStatusId();

		Long serviceId = almIssueWithRequirementVO.getServiceId();

		List<Long> versions = almIssueWithRequirementVO.getVersions();

		Long cReqLink = almIssueWithRequirementVO.getCReqLink();

		boolean isReq =  isRequirement(almIssueVO);

		Long pdServiceId = isReq ? serviceId : getPdServiceId(almIssueVO);

		List<Long> pdServiceVersions = isReq ? versions : getPdServiceVersions(almIssueVO);

		String profile = System.getProperty("spring.profiles.active");

		AlmIssueEntity almIssueEntity = builder()
				.jira_server_id(almIssueWithRequirementVO.getJiraServerId())
				.isReq(isReq)
				.upperKey(almIssueVO.getUpperKey())
				.parentReqKey(almIssueVO.getParentKey())
				.issueID(almIssueVO.getId())
				.key(almIssueVO.getKey())
				.self(almIssueVO.getSelf())
				.project(
						Optional.ofNullable(almIssueVO.getFields().getProject())
								.map(project -> Project.builder()
										.id(project.getId())
										.key(project.getKey())
										.name(project.getName())
										.self(project.getSelf())
										.build())
								.orElse(null)
				)
				.issuetype(
						Optional.ofNullable(almIssueVO.getFields().getIssuetype())
								.map(issuetype -> IssueType.builder()
										.self(issuetype.getSelf())
										.id(issuetype.getId())
										.description(issuetype.getDescription())
										.name(issuetype.getName())
										.subtask(issuetype.getSubtask())
										.untranslatedName(issuetype.getUntranslatedName())
										.hierarchyLevel(issuetype.getHierarchyLevel())
										.build())
								.orElse(null)
				)
				.creator(
						Optional.ofNullable(almIssueVO.getFields().getCreator())
								.map(creator -> Creator.builder()
										.accountId(creator.getAccountId())
										.emailAddress(creator.getEmailAddress())
										.displayName(creator.getDisplayName())
										.build())
								.orElse(null)
				)
				.reporter(
						Optional.ofNullable(almIssueVO.getFields().getReporter())
								.map(reporter -> Reporter.builder()
										.accountId(reporter.getAccountId())
										.emailAddress(reporter.getEmailAddress())
										.displayName(reporter.getDisplayName())
										.build())
								.orElse(null)
				)
				.assignee(
						Optional.ofNullable(almIssueVO.getFields().getAssignee())
								.map(assignee -> Assignee.builder()
										.accountId(assignee.getAccountId())
										.emailAddress(assignee.getEmailAddress())
										.displayName(assignee.getDisplayName())
										.build())
								.orElse(null)
				)
				.labels(almIssueVO.getFields().getLabels())
				.priority(
						Optional.ofNullable(almIssueVO.getFields().getPriority())
								.map(priority -> Priority.builder()
										.self(priority.getSelf())
										.id(priority.getId())
										.name(priority.getName())
										.description(priority.getDescription())
										.isDefault(Optional.of(priority.isDefault()).orElse(false)) // 기본값을 false로 설정
										.build())
								.orElse(null)
				)
				.status(
						Optional.ofNullable(almIssueVO.getFields().getStatus())
								.map(status -> 상태.builder()
										.self(status.getSelf())
										.id(status.getId())
										.name(status.getName())
										.description(status.getDescription())
										.build())
								.orElse(null)
				)
				.resolution(
						Optional.ofNullable(almIssueVO.getFields().getResolution())
								.map(resolution -> Resolution.builder()
										.self(resolution.getSelf())
										.id(resolution.getId())
										.name(resolution.getName())
										.description(resolution.getDescription())
										.isDefault(Optional.of(resolution.isDefault()).orElse(false)) // 기본값을 false로 설정
										.build())
								.orElse(null)
				)
				.resolutiondate(parseDate(almIssueVO.getFields().getResolutiondate()))
				.created(parseDate(almIssueVO.getFields().getCreated()))
				.updated(parseDate(almIssueVO.getFields().getUpdated()))
				.overallUpdatedDate(parseDate(almIssueVO.getFields().getUpdated()))
				.worklogs(
						Optional.ofNullable(almIssueVO.getFields().getWorklogs())
								.orElse(Collections.emptyList()) // null인 경우 빈 리스트 반환
								.stream()
								.map(worklogItem -> new Worklogs(
										worklogItem.getSelf(),
										Optional.ofNullable(worklogItem.getAuthor())
												.map(author -> new Author(
														author.getAccountId(),
														author.getEmailAddress()))
												.orElse(null),
										Optional.ofNullable(worklogItem.getUpdateAuthor())
												.map(updateAuthor -> new UpdateAuthor(
														updateAuthor.getAccountId(),
														updateAuthor.getEmailAddress()))
												.orElse(null),
										worklogItem.getCreated(),
										worklogItem.getUpdated(),
										worklogItem.getStarted(),
										worklogItem.getTimeSpent(),
										worklogItem.getTimeSpentSeconds(),
										worklogItem.getId(),
										worklogItem.getIssueId()
								))
								.collect(toList())
				)
				.timespent(almIssueVO.getFields().getTimespent())
				.summary(almIssueVO.getFields().getSummary())
				.cReqStatusId(isReq?cReqStatusId:getCReqStatusId(almIssueVO))
				.pdServiceId(pdServiceId)
				.pdServiceVersions(pdServiceVersions)
				.cReqLink(isReq?cReqLink:getCReqLink(almIssueVO))
				.linkedIssues(almIssueVO.getLinkedIssues())
				.linkedIssuePdServiceIds(Optional.ofNullable(almIssueVO.getLinkedIssuePdServiceIds()).map(a->a.stream().distinct().toList()).orElse(List.of()))
				.linkedIssuePdServiceVersions(Optional.ofNullable(almIssueVO.getLinkedIssuePdServiceVersions()).map(a->a.stream().distinct().toList()).orElse(List.of()))
				.cReqProperty(
						Optional.ofNullable(almIssueWithRequirementVO.getCReqProperty())
								.map(cReqProperty-> isReq? AlmIssueEntity.CReqProperty.builder()
										.cReqPriorityLink(cReqProperty.getCReqPriorityLink())
										.cReqPriorityName(cReqProperty.getCReqPriorityName())
										.cReqDifficultyLink(cReqProperty.getCReqDifficultyLink())
										.cReqDifficultyName(cReqProperty.getCReqDifficultyName())
										.cReqStateLink(cReqProperty.getCReqStateLink())
										.cReqStateName(cReqProperty.getCReqStateName())
										.build() : getAlmCReqProperty(almIssueVO))
								.orElse(null)
				)
				.armsStateCategory(
						Optional.ofNullable(almIssueVO.getArmsStateCategory())
								.map(armsStateCategory->{
									return almIssueVO.getArmsStateCategory();})
								.orElse(null)
				)
				.rawData(
					Optional.of(profile)
						.filter(a->!a.equals("live"))
						.map(a-> almIssueVO.getRawData())
						.orElse(null)
				) //stg,dev 에서는 저장 처리 해야함 임시 주석
				.build();


		almIssueEntity.generateId();

		return almIssueEntity;

	}

	private Boolean isRequirement(AlmIssueVO almIssueVO) {
		AlmIssueEntity almIssueEntity = getBean(AlmIssueToEntityConverterRepository.class).findRecentDocByRecentId((almIssueVO.recentId()));
		if(almIssueEntity==null||almIssueEntity.getIsReq()==null){
			return false;
		}

		return almIssueEntity.getIsReq();
	}

	private AlmIssueEntity.CReqProperty getAlmCReqProperty(AlmIssueVO almIssueVO) {
		AlmIssueEntity almIssueEntity = getBean(AlmIssueToEntityConverterRepository.class).findRecentDocByRecentId(almIssueVO.recentParentId());
		if(almIssueEntity==null||almIssueEntity.getCReqProperty()==null){
			return null;
		}
		return almIssueEntity.getCReqProperty();
	}

	private Long getCReqStatusId(AlmIssueVO almIssueVO) {
		AlmIssueEntity almIssueEntity = getBean(AlmIssueToEntityConverterRepository.class).findRecentDocByRecentId(almIssueVO.recentParentId());
		if(almIssueEntity==null||almIssueEntity.getCReqStatusId()==null){
			return null;
		}
		return almIssueEntity.getCReqStatusId();
	}

	private Long getCReqLink(AlmIssueVO almIssueVO) {
		AlmIssueEntity almIssueEntity = getBean(AlmIssueToEntityConverterRepository.class).findRecentDocByRecentId(almIssueVO.recentParentId());
		if(almIssueEntity==null||almIssueEntity.getCReqLink()==null){
			return null;
		}
		return almIssueEntity.getCReqLink();
	}

	private Long getPdServiceId(AlmIssueVO almIssueVO) {

		AlmIssueEntity almIssueEntity = getBean(AlmIssueToEntityConverterRepository.class).findRecentDocByRecentId(almIssueVO.recentParentId());

		if(almIssueEntity==null||almIssueEntity.getPdServiceId()==null){
			return null;
		}

		return almIssueEntity.getPdServiceId();
	}

	private List<Long> getPdServiceVersions(AlmIssueVO almIssueVO) {
		AlmIssueEntity almIssueEntity = getBean(AlmIssueToEntityConverterRepository.class).findRecentDocByRecentId(almIssueVO.recentParentId());
		if(almIssueEntity==null||almIssueEntity.getPdServiceVersions()==null){
			return null;
		}
		return almIssueEntity.getPdServiceVersions();
	}

	private Date parseDate(String dateString) {
		if (dateString == null || dateString.isEmpty()) {
			return null;
		}

		// Z 형식 (예: 2025-12-25T04:31:47.671Z)
		if (dateString.endsWith("Z")) {
			return Date.from(Instant.parse(dateString));
		}

		// +0900 또는 +09:00 형식 → UTC로 변환
		Instant instant = OffsetDateTime.parse(dateString, getDateTimeFormatter(dateString)).toInstant();
		return Date.from(instant);
	}

	private DateTimeFormatter getDateTimeFormatter(String dateString) {
		// +0900 형식 (콜론 없음)
		if (dateString.matches(".*[+-]\\d{4}$")) {
			return DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
		}
		// +09:00 형식 (콜론 있음)
		return DateTimeFormatter.ISO_OFFSET_DATE_TIME;
	}

	public List<AlmIssueEntity> almIssueEntities(){
		return almIssueEntityCollection.almIssueEntities();
	}

}