package com.jasamedika.medifirst2000.task.schedule;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.jasamedika.medifirst2000.dao.PasienDao;
import com.jasamedika.medifirst2000.dto.PesertaBpjsDto;
import com.jasamedika.medifirst2000.entities.Pasien;
import com.jasamedika.medifirst2000.util.CommonUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

import java.net.URI;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

import static org.springframework.http.HttpMethod.GET;
import static org.springframework.http.HttpStatus.CREATED;
import static org.springframework.http.HttpStatus.OK;

/**
 * @author salmanoe
 * @version 1.0.0
 * @since 11/10/2023
 */
@Component
public class PasienTask {
	private static final Logger LOGGER = LoggerFactory.getLogger(PasienTask.class);

	@Autowired
	private ObjectMapper objectMapper;

	@Autowired
	private RestTemplate restTemplate;

	@Autowired
	private PasienDao pasienDao;

	@Scheduled(cron = "0 0 0 * * *")
	public void nikPesertaBpjs() {
		LOGGER.info("Task PasienTask.nikPesertaBpjs {}", LocalDateTime.now());

		adjustNikPesertaBpjs();
	}

	@Transactional
	private void adjustNikPesertaBpjs() {
		LOGGER.info("Adjusting NIK Peserta BPJS {}", LocalDateTime.now());

		List<PesertaBpjsDto> listDtoUpdate = new ArrayList<>();
		List<PesertaBpjsDto> listDtoNotUpdate = new ArrayList<>();
		List<Pasien> pasienValidBpjs = pasienDao.findByValidBpjs();
		for (Pasien pasien : pasienValidBpjs) {
			try {
				PesertaBpjsDto pesertaBpjsDto = cekKepesertaan(pasien.getNoBpjs());
				if (pesertaBpjsDto.getKdProfile() == 0)
					listDtoUpdate.add(pesertaBpjsDto);
				if (pesertaBpjsDto.getKdProfile() == 1)
					listDtoNotUpdate.add(pesertaBpjsDto);

				Thread.sleep((long) (Math.random() * 1000 * 8));
			} catch (Exception e) {
				LOGGER.error("Error adjusting NIK Peserta BPJS {} -> {}", pasien.getNoBpjs(), e.getMessage());
			}
		}
		{
			List<String> listNoKartu = listDtoUpdate.stream().map(PesertaBpjsDto::getNoKartu)
					.collect(Collectors.toList());
			List<Pasien> availableValid = pasienValidBpjs.stream()
					.filter(pasien -> listNoKartu.contains(pasien.getNoBpjs())).collect(Collectors.toList());
			availableValid.forEach(pasien -> {
				Optional<PesertaBpjsDto> first = listDtoUpdate.stream()
						.filter(dto -> pasien.getNoBpjs().equals(dto.getNoKartu())).findFirst();
				first.ifPresent(dto -> {
					pasien.setKdProfile(dto.getKdProfile());
					pasien.setNoIdentitas(dto.getNik());
				});
			});
			if (CommonUtil.isNotNullOrEmpty(availableValid)) {
				LOGGER.info("Save available valid data");

				pasienDao.save(availableValid);
			}
		}
		{
			List<String> listNoKartu = listDtoNotUpdate.stream().map(PesertaBpjsDto::getNoKartu)
					.collect(Collectors.toList());
			List<Pasien> availableValid = pasienValidBpjs.stream()
					.filter(pasien -> listNoKartu.contains(pasien.getNoBpjs())).collect(Collectors.toList());
			availableValid.forEach(pasien -> {
				Optional<PesertaBpjsDto> first = listDtoNotUpdate.stream()
						.filter(dto -> pasien.getNoBpjs().equals(dto.getNoKartu())).findFirst();
				first.ifPresent(dto -> pasien.setKdProfile(dto.getKdProfile()));
			});
			if (CommonUtil.isNotNullOrEmpty(availableValid)) {
				LOGGER.info("Flag kdProfile can not update");

				pasienDao.save(availableValid);
			}
		}
	}

	private PesertaBpjsDto cekKepesertaan(String noKartu) {
		try {
			LOGGER.info("Cek kepesertaan BPJS {} {}", noKartu, LocalDateTime.now());

			URI uri = new URI("https://daftar.rsabhk.co.id/api/data/pasien/jaminan/bpjs/cek-kepesertaan/" + noKartu);
			HttpEntity<Object> request = new HttpEntity<>(null);
			ResponseEntity<Object> response = restTemplate.exchange(uri, GET, request, Object.class);
			if (OK.equals(response.getStatusCode()) || CREATED.equals(response.getStatusCode())) {
				Map<String, Object> responseBody = objectMapper.convertValue(response.getBody(), Map.class);
				Map<String, Object> responseMap = objectMapper.convertValue(responseBody.get("response"),
						HashMap.class);
				Map<String, Object> responsePeserta = objectMapper.convertValue(responseMap.get("peserta"),
						HashMap.class);
				return PesertaBpjsDto.builder().noKartu(responsePeserta.get("noKartu").toString())
						.nik(responsePeserta.get("nik").toString()).kdProfile((short) 0).build();
			} else {
				LOGGER.error("Error handshake cek kepesertaan BPJS {}", response.getStatusCode().getReasonPhrase());

				return PesertaBpjsDto.builder().noKartu(noKartu).kdProfile((short) 1).build();
			}
		} catch (Exception e) {
			LOGGER.error("Error cek kepesertaan BPJS {}", e.getMessage());

			return PesertaBpjsDto.builder().noKartu(noKartu).kdProfile((short) 1).build();
		}
	}
}
