package com.jasamedika.medifirst2000.service.impl;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.YearMonth;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAdjusters;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.jpa.JpaSystemException;
import org.springframework.stereotype.Service;

import com.jasamedika.medifirst2000.constants.Master;
import com.jasamedika.medifirst2000.converter.BaseConverterImpl;
import com.jasamedika.medifirst2000.dao.CatatanPerkembanganPasienTerintegrasiDao;
import com.jasamedika.medifirst2000.dao.KalenderDao;
import com.jasamedika.medifirst2000.dao.LogbookKinerjaDokterDao;
import com.jasamedika.medifirst2000.dao.MapPegawaiJabatanToUnitKerjaDao;
import com.jasamedika.medifirst2000.entities.AntrianPasienDiPeriksa;
import com.jasamedika.medifirst2000.entities.CatatanPerkembanganPasienTerintegrasi;
import com.jasamedika.medifirst2000.entities.Pasien;
import com.jasamedika.medifirst2000.entities.Pegawai;
import com.jasamedika.medifirst2000.entities.Ruangan;
import com.jasamedika.medifirst2000.exception.ServiceVOException;
import com.jasamedika.medifirst2000.service.CatatanPerkembanganPasienTerintegrasiService;
import com.jasamedika.medifirst2000.util.CommonUtil;
import com.jasamedika.medifirst2000.util.DateUtil;
import com.jasamedika.medifirst2000.vo.AntrianPasienDiPeriksaVO;
import com.jasamedika.medifirst2000.vo.CatatanPerkembanganPasienTerintegrasiVO;
import com.jasamedika.medifirst2000.vo.PasienVO;
import com.jasamedika.medifirst2000.vo.PegawaiVO;
import com.jasamedika.medifirst2000.vo.RuanganVO;
import com.jasamedika.medifirst2000.vo.custom.CpptDokterVo;

/**
 * @author salmanoe
 * @since Oct 21, 2021
 */
@Service("cpptService")
public class CatatanPerkembanganPasienTerintegrasiServiceImpl extends BaseVoServiceImpl
		implements CatatanPerkembanganPasienTerintegrasiService {

	@Autowired
	private BaseConverterImpl<CatatanPerkembanganPasienTerintegrasiVO, CatatanPerkembanganPasienTerintegrasi> cpptConverter;

	@Autowired
	private BaseConverterImpl<AntrianPasienDiPeriksaVO, AntrianPasienDiPeriksa> apdConverter;

	@Autowired
	private BaseConverterImpl<PegawaiVO, Pegawai> pegawaiConverter;

	@Autowired
	private BaseConverterImpl<RuanganVO, Ruangan> ruanganConverter;

	@Autowired
	private BaseConverterImpl<PasienVO, Pasien> pasienConverter;

	@Autowired
	private CatatanPerkembanganPasienTerintegrasiDao cpptDao;

	@Autowired
	private KalenderDao kalenderDao;

	@Autowired
	private MapPegawaiJabatanToUnitKerjaDao mapJabatanDao;

	@Autowired
	private LogbookKinerjaDokterDao logbookKinerjaDokterDao;

	public Double getCapaianPresensiCppt(List<CatatanPerkembanganPasienTerintegrasi> data, Integer idDokter,
			Integer days) {
		DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
		DateFormat hf = new SimpleDateFormat("HH:mm:ss");

		double totCapaian = 0.0;
		for (CatatanPerkembanganPasienTerintegrasi cppt : data) {
			if (idDokter.equals(cppt.getPegawai().getId())) {
				LocalDateTime tenthOClock = LocalDateTime.of(LocalDate.parse(df.format(cppt.getTglInput())),
						LocalTime.of(10, 0, 0, 0));
				LocalDateTime tglInput = LocalDateTime.of(LocalDate.parse(df.format(cppt.getTglInput())),
						LocalTime.parse(hf.format(cppt.getTglInput())));
				if (tglInput.isBefore(tenthOClock) || tglInput.equals(tenthOClock)) {
					totCapaian += 100;
				}
			}
		}

		return totCapaian / days;
	}

	@Override
	public CatatanPerkembanganPasienTerintegrasiVO add(CatatanPerkembanganPasienTerintegrasiVO vo)
			throws JpaSystemException, ServiceVOException {
		CatatanPerkembanganPasienTerintegrasi cppt = cpptConverter.transferVOToModel(vo,
				new CatatanPerkembanganPasienTerintegrasi());

		AntrianPasienDiPeriksa apd = apdConverter.transferVOToModel(vo.getAntrianPasienDiperiksa(),
				new AntrianPasienDiPeriksa());
		Pegawai pegawai = pegawaiConverter.transferVOToModel(vo.getPegawai(), new Pegawai());
		Pegawai pegawaiAsal = pegawaiConverter.transferVOToModel(vo.getPegawaiAsal(), new Pegawai());
		Ruangan ruangan = ruanganConverter.transferVOToModel(vo.getRuangan(), new Ruangan());
		Pasien pasien = pasienConverter.transferVOToModel(vo.getPasien(), new Pasien());

		cppt.setAntrianPasienDiperiksa(apd);
		cppt.setPegawai(pegawai);
		cppt.setPegawaiAsal(pegawaiAsal);
		cppt.setRuangan(ruangan);
		cppt.setPasien(pasien);

		CatatanPerkembanganPasienTerintegrasi resultModel = cpptDao.save(cppt);

		CatatanPerkembanganPasienTerintegrasiVO resultVO = new CatatanPerkembanganPasienTerintegrasiVO();
		resultVO = cpptConverter.transferModelToVO(resultModel, resultVO);

		return resultVO;
	}

	@Override
	public CatatanPerkembanganPasienTerintegrasiVO update(CatatanPerkembanganPasienTerintegrasiVO vo)
			throws JpaSystemException, ServiceVOException {
		CatatanPerkembanganPasienTerintegrasi cpptLama = cpptDao.findOne(vo.getNoRec());

		CatatanPerkembanganPasienTerintegrasi cpptBaru = cpptConverter.transferVOToModel(vo, cpptLama);

		AntrianPasienDiPeriksa apd = apdConverter.transferVOToModel(vo.getAntrianPasienDiperiksa(),
				new AntrianPasienDiPeriksa());
		Pegawai pegawai = pegawaiConverter.transferVOToModel(vo.getPegawai(), new Pegawai());
		Pegawai pegawaiAsal = pegawaiConverter.transferVOToModel(vo.getPegawaiAsal(), new Pegawai());
		Ruangan ruangan = ruanganConverter.transferVOToModel(vo.getRuangan(), new Ruangan());
		Pasien pasien = pasienConverter.transferVOToModel(vo.getPasien(), new Pasien());

		cpptBaru.setAntrianPasienDiperiksa(apd);
		cpptBaru.setPegawai(pegawai);
		cpptBaru.setPegawaiAsal(pegawaiAsal);
		cpptBaru.setRuangan(ruangan);
		cpptBaru.setPasien(pasien);

		CatatanPerkembanganPasienTerintegrasi resultModel = cpptDao.save(cpptBaru);

		CatatanPerkembanganPasienTerintegrasiVO resultVO = new CatatanPerkembanganPasienTerintegrasiVO();
		resultVO = cpptConverter.transferModelToVO(resultModel, resultVO);

		return resultVO;
	}

	@Override
	public Boolean delete(String key) throws JpaSystemException {
		cpptDao.delete(key);

		return true;
	}

	@Override
	public CatatanPerkembanganPasienTerintegrasiVO findById(String key) throws JpaSystemException {
		CatatanPerkembanganPasienTerintegrasiVO result = new CatatanPerkembanganPasienTerintegrasiVO();

		CatatanPerkembanganPasienTerintegrasi cppt = cpptDao.findOne(key);
		if (CommonUtil.isNotNullOrEmpty(cppt)) {
			result = cpptConverter.transferModelToVO(cppt, result);
		}

		return result;
	}

	@Override
	public List<CatatanPerkembanganPasienTerintegrasiVO> findAll() throws JpaSystemException {
		List<CatatanPerkembanganPasienTerintegrasiVO> result = new ArrayList<CatatanPerkembanganPasienTerintegrasiVO>();

		Iterable<CatatanPerkembanganPasienTerintegrasi> models = cpptDao.findAll();
		if (CommonUtil.isNotNullOrEmpty(models)) {
			for (CatatanPerkembanganPasienTerintegrasi model : models) {
				CatatanPerkembanganPasienTerintegrasiVO vo = new CatatanPerkembanganPasienTerintegrasiVO();
				vo = cpptConverter.transferModelToVO(model, vo);
				result.add(vo);
			}
		}

		return result;
	}

	@Override
	public Map<String, Object> findAllWithPageAndLimitAndSortByAndDirectionParameter(Integer page, Integer limit,
			String sort, String dir) {

		return null;
	}

	/**
	 * @author salmanoe
	 * @since Nov 15, 2021
	 * @param idKsm
	 *            id kelompok staf medik
	 * @param idKk
	 *            id kelompok kerja
	 * @param idDr
	 *            id pegawai dokter
	 * @return daftar dokter
	 */
	@Override
	public List<Map<String, Object>> findDokter(Integer idKsm, Integer idKk, Integer idDr) throws JpaSystemException {
		List<Map<String, Object>> result = new ArrayList<>();

		if (CommonUtil.isNotNullOrEmpty(idKsm) && CommonUtil.isNotNullOrEmpty(idKk)
				&& CommonUtil.isNotNullOrEmpty(idDr)) {
			result = mapJabatanDao.findDokterById(idKsm, idKk, idDr);
		} else if (CommonUtil.isNullOrEmpty(idKsm) && CommonUtil.isNullOrEmpty(idKk)
				&& CommonUtil.isNotNullOrEmpty(idDr)) {
			result = mapJabatanDao.findDokterById(idDr);
		} else if (CommonUtil.isNotNullOrEmpty(idKk) && CommonUtil.isNullOrEmpty(idDr)) {
			result = mapJabatanDao.findDokterByKk(idKsm, idKk);
		} else if (CommonUtil.isNotNullOrEmpty(idKsm) && CommonUtil.isNullOrEmpty(idKk)) {
			result = mapJabatanDao.findDokterByKsm(idKsm);
		} else {
			result = mapJabatanDao.findDokter();
		}

		return result;
	}

	/**
	 * @author salmanoe
	 * @since Nov 12, 2021
	 * @param idKsm
	 *            id kelompok staf medik
	 * @param idKk
	 *            id kelompok kerja
	 * @param idDr
	 *            id pegawai dokter
	 * @param periode
	 *            periode pengambilan data
	 * @return presensi visite dokter
	 */
	@Override
	public Map<String, Object> findPresensiVisiteDokter(Integer idKsm, Integer idKk, Integer idDr, Long periode)
			throws JpaSystemException {
		Map<String, Object> result = new HashMap<>();
		List<Map<String, Object>> resultDetail = new ArrayList<>();
		List<Map<String, Object>> dataInit = new ArrayList<>();

		List<Integer> listIdDokter = new ArrayList<>();

		DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd");
		DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
		DateFormat df_ina = new SimpleDateFormat("dd-MM-yyyy");
		DateFormat sf_ina = new SimpleDateFormat("HH:mm:ss");

		String strStartDate;
		String strEndDate;
		LocalDate localMonth = Instant.ofEpochMilli(periode).atZone(ZoneId.systemDefault()).toLocalDate();
		YearMonth yearMonth = YearMonth.from(localMonth);
		if (YearMonth.now() == yearMonth) {
			strStartDate = LocalDate.now().with(TemporalAdjusters.firstDayOfMonth()).format(dtf);
			strEndDate = LocalDate.now().format(dtf);
		} else {
			strStartDate = localMonth.with(TemporalAdjusters.firstDayOfMonth()).format(dtf);
			strEndDate = localMonth.with(TemporalAdjusters.lastDayOfMonth()).format(dtf);
		}

		List<Map<String, Object>> listDokter = this.findDokter(idKsm, idKk, idDr);
		for (Map<String, Object> map : listDokter) {
			listIdDokter.add(Integer.valueOf(map.get("pegawaiId").toString()));
		}

		List<Date> listTgl = kalenderDao.getDateFromPeriodicParameter(strStartDate, strEndDate);
		for (Map<String, Object> dokter : listDokter) {
			for (Date tgl : listTgl) {
				Map<String, Object> mapDate = new HashMap<>();
				mapDate.putAll(dokter);
				mapDate.put("tgl", tgl);
				dataInit.add(mapDate);
			}

		}

		int daysOfMonth = listTgl.size();
		CpptDokterVo cpptDokterVo = new CpptDokterVo();
		if (CommonUtil.isNotNullOrEmpty(listIdDokter)) {
			List<CatatanPerkembanganPasienTerintegrasi> rawData = cpptDao.findPresensiCPPT(listIdDokter, strStartDate,
					strEndDate);
			if (CommonUtil.isNotNullOrEmpty(idDr)) {
				Double persenTepatHadir = 0.0;
				for (Integer idDokter : listIdDokter) {
					persenTepatHadir = getCapaianPresensiCppt(rawData, idDokter, daysOfMonth);
				}

				Date start = DateUtil.startMonth(periode);
				Date end = DateUtil.endMonth(periode);
				Map<String, Object> cpptDokter = logbookKinerjaDokterDao.findDetailKinerjaDokter(start, end, idDr,
						Master.IndikatorKinerja.KEPATUHAN_PELAYANAN_MEDIS,
						Master.IndikatorKinerjaDetail.KEPATUHAN_WAKTU_VISITE);
				if (CommonUtil.isNotNullOrEmpty(cpptDokter)) {
					cpptDokterVo.setLogbookId(cpptDokter.get("logbookId").toString());
					cpptDokterVo.setDetailId(cpptDokter.get("detailId").toString());
				}
				cpptDokterVo.setDetailCapaian(persenTepatHadir);
			}

			for (Map<String, Object> map : dataInit) {
				Map<String, Object> mapRs = new HashMap<>();
				mapRs.put("cpptId", null);
				mapRs.put("tglInput", null);
				mapRs.put("strTgl", df_ina.format(map.get("tgl")));
				mapRs.put("strJamInput", null);
				mapRs.put("antrianId", null);
				mapRs.put("namaRuangan", null);
				mapRs.put("isDpjp", null);
				for (CatatanPerkembanganPasienTerintegrasi cppt : rawData) {
					if (df.format(map.get("tgl")).equals(df.format(cppt.getTglInput()))
							&& map.get("pegawaiId").equals(cppt.getPegawaiId())) {
						Boolean isDpjp = false;
						if (CommonUtil.isNotNullOrEmpty(cppt.getAntrianPasienDiperiksa())
								&& CommonUtil.isNotNullOrEmpty(cppt.getAntrianPasienDiperiksa().getPegawai())) {
							isDpjp = cppt.getAntrianPasienDiperiksa().getPegawai().getId().equals(map.get("pegawaiId"))
									? true : false;
						}

						mapRs.put("cpptId", cppt.getNoRec());
						mapRs.put("tglInput", cppt.getTglInput());
						mapRs.put("strTgl", df_ina.format(map.get("tgl")));
						mapRs.put("strJamInput", sf_ina.format(cppt.getTglInput()));
						mapRs.put("antrianId", cppt.getAntrianPasienDiperiksaId());
						mapRs.put("namaRuangan", cppt.getRuangan().getNamaRuangan());
						mapRs.put("isDpjp", isDpjp);

						break;
					}
				}
				mapRs.putAll(map);

				resultDetail.add(mapRs);
			}
		}
		if (CommonUtil.isNotNullOrEmpty(cpptDokterVo)) {
			result.put("indikator", cpptDokterVo);
		}
		if (CommonUtil.isNotNullOrEmpty(cpptDokterVo) && CommonUtil.isNotNullOrEmpty(cpptDokterVo.getDetailCapaian())) {
			result.put("persenTepatHadir",
					new BigDecimal(cpptDokterVo.getDetailCapaian()).setScale(2, RoundingMode.HALF_UP));
		}
		result.put("detail", resultDetail);

		return result;
	}

	@Override
	public Map<String, Object> findPresensiVisiteDokter(Integer idPegawai, Long bulan) throws JpaSystemException {
		DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd");

		LocalDate localMonth = Instant.ofEpochMilli(bulan).atZone(ZoneId.systemDefault()).toLocalDate();

		String strStartDate = localMonth.with(TemporalAdjusters.firstDayOfMonth()).format(dtf);
		String strEndDate = localMonth.with(TemporalAdjusters.lastDayOfMonth()).format(dtf);

		List<Date> listTgl = kalenderDao.getDateFromPeriodicParameter(strStartDate, strEndDate);

		int daysOfMonth = listTgl.size();

		List<Integer> listIdDokter = new ArrayList<>();
		List<Map<String, Object>> listDokter = mapJabatanDao.findDokterByRemun(Arrays.asList(Master.UnitKerja.KSM),
				idPegawai);
		for (Map<String, Object> dokter : listDokter) {
			if (!listIdDokter.contains(Integer.valueOf(dokter.get("pegawaiId").toString()))) {
				listIdDokter.add(Integer.valueOf(dokter.get("pegawaiId").toString()));
			}
		}

		List<CatatanPerkembanganPasienTerintegrasi> rawData = cpptDao.findPresensiCPPT(listIdDokter, strStartDate,
				strEndDate);
		for (Map<String, Object> dokter : listDokter) {
			dokter.put("rataCapaian",
					getCapaianPresensiCppt(rawData, Integer.valueOf(dokter.get("pegawaiId").toString()), daysOfMonth));
		}

		return listDokter.get(0);
	}

}
