package org.msh.tb.cases.summary;

import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.international.Messages;
import org.msh.etbm.commons.Item;
import org.msh.tb.cases.CaseHome;
import org.msh.tb.entities.TbCase;
import org.msh.tb.entities.enums.DstResult;
import org.msh.tb.entities.enums.MicroscopyResult;
import org.msh.tb.entities.enums.XpertResult;
import org.msh.tb.entities.enums.XpertRifResult;
import org.msh.tb.workspaces.customizable.WorkspaceCustomizationService;
import org.msh.utils.date.DateUtils;
import org.msh.utils.date.Period;

import javax.persistence.EntityManager;
import java.util.*;

/**
 * Generate a summary of the current tb case pointed by the {@link CaseHome} component
 *
 * Created by rmemoria on 5/2/17.
 */
@Name("summaryHome")
public class SummaryHome {

    @In
    EntityManager entityManager;

    @In(required = true)
    CaseHome caseHome;


    private List<Item<String>> medicines;

    private List<SummaryRow> rows;

    /**
     * Return the list of medicines prescribed to the case
     * @return
     */
    public List<Item<String>> getMedicines() {
        if (medicines == null) {
            createRows();
        }
        return medicines;
    }

    /**
     * Return the rows of the summary report
     * @return
     */
    public List<SummaryRow> getRows() {
        if (rows == null) {
            createRows();
        }

        return rows;
    }

    /**
     * Create the rows of the summary
     */
    private void createRows() {
        medicines = new ArrayList<Item<String>>();

        createRowsTreatment();
        loadMedicines();
        loadWeights();
        loadXpertResults();
        loadMicroscopyResults();
        loadDst();

        // sort the rows
        Collections.sort(rows, new Comparator<SummaryRow>() {
            @Override
            public int compare(SummaryRow o1, SummaryRow o2) {
                if (o1.getMonth() < o2.getMonth()) {
                    return -1;
                }

                return o1.getMonth() == o2.getMonth() ? 0 : 1;
            }
        });
    }

    /**
     * Load DST exam results
     */
    private void loadDst() {
        List<Object[]> lst = entityManager.createQuery("select a.dateCollected, b.substance.abbrevName.name1, b.result " +
                "from ExamDSTResult b join b.exam a " +
                "where a.tbcase.id = :id")
                .setParameter("id", caseHome.getId())
                .getResultList();

        for (Object[] vals: lst) {
            Date dt = (Date)vals[0];
            String name = (String)vals[1];
            DstResult res = (DstResult) vals[2];
            // check if medicine is already registered
            Item<String> med = new Item<String>(name, name);
            if (!medicines.contains(med)) {
                medicines.add(med);
            }

            SummaryRow row = rowByDate(dt);
            DSTSummary item = row.dstResultByMedicine(name);

            // é um novo DST
            if (item == null) {
                item = new DSTSummary();
                item.setMedicine(name);
                item.setResult(res);
                row.getDstResults().add(item);
            } else if (res == DstResult.RESISTANT) {
                item.setResult(res);
            }
        }
    }

    /**
     * Load the microscopy results of the case
     */
    private void loadMicroscopyResults() {
        List<Object[]> lst = entityManager.createQuery("select c.dateCollected, c.result " +
                "from ExamMicroscopy c " +
                "where c.tbcase.id = :id")
                .setParameter("id", caseHome.getId())
                .getResultList();

        for (Object[] vals: lst) {
            Date dt = (Date)vals[0];
            MicroscopyResult result = (MicroscopyResult)vals[1];

            SummaryRow row = rowByDate(dt);
            row.getMicroscopyResults().add(new MicroscopySummary(dt, result));
        }
    }


    /**
     * Load exam Xpert results of the case
     */
    private void loadXpertResults() {
        List<Object[]> lst = entityManager.createQuery("select c.dateCollected, c.result, c.rifResult " +
                "from ExamXpert c " +
                "where c.tbcase.id = :id")
                .setParameter("id", caseHome.getId())
                .getResultList();

        for (Object[] vals: lst) {
            Date dt = (Date)vals[0];
            XpertResult result = (XpertResult) vals[1];
            XpertRifResult rifResult = (XpertRifResult) vals[2];

            SummaryRow row = rowByDate(dt);
            row.getXpertResults().add(new XpertSummary(dt, result, rifResult));
        }
    }

    /**
     * Load the weights of the patient
     */
    private void loadWeights() {
        List<Object[]> lst = entityManager.createQuery("select c.date, c.weight from MedicalExamination c " +
                "where c.tbcase.id = :id and c.weight is not null")
                .setParameter("id", caseHome.getId())
                .getResultList();

        for (Object[] vals: lst) {
            Date dt = (Date)vals[0];
            Double weight = (Double)vals[1];

            SummaryRow row = rowByDate(dt);
            row.getWeights().add(new WeightSummary(dt, weight));
        }
    }

    /**
     * Initialize the rows based on the months of treatment
     */
    private void createRowsTreatment() {
        TbCase tbcase = caseHome.getInstance();

        Period p = tbcase.getTreatmentPeriod();

        // there is no treatment ?
        if (p == null || p.isEmpty()) {
            return;
        }

        // count months
        int months = p.getMonths();

        rows = new ArrayList<SummaryRow>();

        // create the initial rows of the report with the months of treatment
        Date dt = p.getIniDate();
        for (int i = 1; i <= months; i++) {
            rowByDate(dt);
            dt = DateUtils.incMonths(dt, 1);
        }
    }


    /**
     * Load the medicines of the treatment in the rows of the summary
     */
    private void loadMedicines() {
        List<Object[]> lst = entityManager.createQuery("select s.id, " +
                "s.abbrevName.name1, pm.period, m.id, m.abbrevName " +
                "from PrescribedMedicine pm join pm.medicine m left join m.components c " +
                "left join c.substance s " +
                "where pm.tbcase.id = :id")
                .setParameter("id", caseHome.getId())
                .getResultList();

        for (Object[] vals: lst) {
            String id = vals[1] != null ? (String)vals[1] : (String)vals[4];
            String name = id;
            Period p = (Period)vals[2];

            // add new item to the column of medicines
            Item<String> item = new Item<String>(id, name);
            if (!medicines.contains(item)) {
                medicines.add(item);
            }

            Date dt = p.getIniDate();
            // mount the list of medicines in use each month of treatment
            while (!dt.after(p.getEndDate())) {
                SummaryRow row = rowByDate(dt);
                if (!row.getMedicines().contains(id)) {
                    row.getMedicines().add(item);
                }
                dt = DateUtils.incMonths(dt, 1);
            }
        }
    }

    /**
     * Return the displayable name of the month of the treatment
     * @param monthTreatment the month of treatment
     * @return
     */
    private String monthDisplay(int monthTreatment) {
        if (monthTreatment == -1)
            return Messages.instance().get("cases.exams.prevdt");

        return monthTreatment == 0 ? Messages.instance().get("cases.exams.zero") : Integer.toString(monthTreatment);
    }

    /**
     * Search for a row of the summary by a date. The date is converted to the month of the treatment
     * If the row is not found, a new one is included in the summary
     * @param date the date to search for the row
     * @return instance of {@link SummaryRow}
     */
    private SummaryRow rowByDate(Date date) {
        TbCase tbcase = caseHome.getInstance();

        int monthTreatment = tbcase.getMonthTreatment(date);

        for (SummaryRow row: rows) {
            if (row.getMonth() == monthTreatment) {
                return row;
            }
        }

        SummaryRow row = new SummaryRow();
        row.setMonth(monthTreatment);
        row.setMonthDisplay(monthDisplay(monthTreatment));
        rows.add(row);

        return row;
    }
}
