import datetime

from AvenirCommon.Database import *
from AvenirCommon.Util import GBGetKeyValue

from SpectrumCommon.Const.GB import *
from SpectrumCommon.Modvars.GB.GBDefs import *
from SpectrumCommon.Modvars.GB.GBUtil import get_country_details

from SpectrumCommon.Modvars.FP.FPModvars import init_modvars as init_FP_modvars
from SpectrumCommon.Modvars.TB.TBModvars import init_modvars as init_TB_modvars
from SpectrumCommon.Modvars.DP.DPModvars import init_modvars as init_DP_modvars
from SpectrumCommon.Modvars.AM.AMModvars import init_modvars as init_AM_modvars
from SpectrumCommon.Modvars.RP.RPModVars import init_modvars as init_RP_modvars
from SpectrumCommon.Modvars.CS.CSModvars import init_modvars as init_CS_modvars
from SpectrumCommon.Modvars.GB.GBModvars import init_modvars as init_GB_modvars
from SpectrumCommon.Modvars.VW.VWModvars import init_modvars as init_VW_modvars
from SpectrumCommon.Modvars.GB.PJNModvars import init_modvars as init_PJN_modvars
# IHT
from SpectrumCommon.Modvars.IH.IHModVars import init_modvars as init_IH_modvars
from SpectrumCommon.Modvars.IC.ICModVars import init_modvars as init_IC_modvars
from SpectrumCommon.Modvars.PC.PCModVars import init_modvars as init_PC_modvars
from SpectrumCommon.Modvars.HW.HWModVars import init_modvars as init_HW_modvars
from SpectrumCommon.Modvars.IS.ISModVars import init_modvars as init_IS_modvars
from SpectrumCommon.Modvars.HS.HSModVars import init_modvars as init_HS_modvars
from SpectrumCommon.Modvars.FS.FSModvars import init_modvars as init_FS_modvars
from SpectrumCommon.Modvars.BG.BGModVars import init_modvars as init_BG_modvars
from SpectrumCommon.Modvars.GV.GVModVars import init_modvars as init_GV_modvars
from SpectrumCommon.Modvars.HF.HFModVars import init_modvars as init_HF_modvars
from SpectrumCommon.Modvars.LG.LGModVars import init_modvars as init_LG_modvars
from SpectrumCommon.Modvars.HV.HVModvars import init_modvars as init_HV_modvars
from SpectrumCommon.Modvars.RN.RNModvars import init_modvars as init_RN_modvars

import SpectrumCommon.Const.IH.IHTags as ihmvt
import SpectrumCommon.Const.IC.ICTags as icmvt
import SpectrumCommon.Const.IS.ISTags as ismvt
import SpectrumCommon.Const.HW.HWTags as hwmvt
import SpectrumCommon.Const.PC.PCTags as pcmvt
import SpectrumCommon.Const.FP.FPTags as fpmvt
import SpectrumCommon.Const.CS.CSTags as csmvt

# from SpectrumCommon.Modvars.RP.RPModVarDefs import RP_init_sector_list

#from App.TempCalc.GB.GBMain import GBCalculate

# def get_country_ISO3Alpha(countryISO3 = int):
#     countries = GB_get_db_json(env[GB_SPECT_MOD_DATA_CONN_ENV], 'globals', formatCountryFName(GBCountryListDBName, GBDatabaseVersion))
#     for country in countries:
#         if country['ISO3_Numeric'] == countryISO3:
#             return country['ISO3_Alpha']

# Sorts the modules if we need something from one when initializing another.
def sort_modules(modules : list):
    modules_sorted = []

    # Add modules whose order we care about first:
    #   PC needs programme areas from IH.
    #   HW needs facility types from IS.
    #   IC needs programme areas from IH, staff types from HW, costing sections from PC, active methods from FamPlan, selected interventions from LiST.
    #   BG needs many modules; IH, IC, PC, HR, IS, HS, HW, GV, HF, LG
    #   RP needs FP
    if GB_IH in modules:
        modules_sorted.append(GB_IH)
    if GB_PC in modules:
        modules_sorted.append(GB_PC)
    if GB_IS in modules:
        modules_sorted.append(GB_IS)
    if GB_HW in modules:
        modules_sorted.append(GB_HW)
    if GB_HS in modules:
        modules_sorted.append(GB_HS)
    if GB_GV in modules:
        modules_sorted.append(GB_GV)
    if GB_HF in modules:
        modules_sorted.append(GB_HF)
    if GB_FP in modules:
        modules_sorted.append(GB_FP)
    if GB_RP in modules:
        modules_sorted.append(GB_RP)
    if GB_CS in modules:
        modules_sorted.append(GB_CS)
    if GB_IC in modules:
        modules_sorted.append(GB_IC)
    if GB_FS in modules:
        modules_sorted.append(GB_FS)
    if GB_BG in modules:
        modules_sorted.append(GB_BG)
    # if GB_HV in modules:
    #     modules_sorted.append(GB_HV)
    # if GB_RN in modules:
    #     modules_sorted.append(GB_RN)
    
    # Get list of the modules we haven't added yet.
    modules_diff = list(set(modules) - set(modules_sorted))

    # Add other modules whose order we don't care about.
    for module in modules_diff:
        if module not in GB_PseudoModules:
            modules_sorted.append(module)

    return modules_sorted

def GB_CreateProjection(params : createProjectionParams):

        #convert module strings to id's
        if not params.firstYear:
            date = datetime.datetime.now().date()
            params.firstYear = int(date.strftime("%Y"))
        countryID = params.country
        first_year = params.firstYear
        final_year = params.finalYear

        modules = []
        for md in params.modules:
            if type(md)==str:
                md = md.strip()
            if isinstance(md, str):
                if md in GB_MOD_ID:
                    modules.append(GB_MOD_ID[md])
            else:
                 if md in GB_EXISTING_MOD_ID.values():
                    modules.append(md)
        params.modules = modules

        # Extra params

        extra = params.extra
        if GB_TB_COSTING_ON not in extra:
            extra[GB_TB_COSTING_ON] = False

        if GB_LIST_COSTING_ON not in extra:
            extra[GB_LIST_COSTING_ON] = False

        if GB_IHT_ON not in extra:
            extra[GB_IHT_ON] = False

        # extra = params.extra
        # if not (GB_RAPID_SECTORS_ON in extra):
        #     extra[GB_RAPID_SECTORS_ON] = RP_init_sector_list()

        subNatCode = GBGetKeyValue(extra, 'subNatCode', 0)
        calculate = GBGetKeyValue(extra, 'calculate', 0)

        extra['fileName'] = params.fileName
        extra[GB_MODULES] = params.modules

        extra['countryDetails'] = get_country_details(countryID, subNatCode)

        if extra['countryDetails'] == None:
            raise Exception('Country ' + countryID + ' does not exist')

        init_modvars_fun = {
            GB_GB : init_GB_modvars,
            GB_PJ : init_PJN_modvars,
            GB_DP : init_DP_modvars,
            GB_AM : init_AM_modvars,
            GB_FP : init_FP_modvars,
            GB_RP : init_RP_modvars,
            GB_TB : init_TB_modvars,
            GB_CS : init_CS_modvars,
            GB_IH : init_IH_modvars,
            GB_IC : init_IC_modvars,
            GB_PC : init_PC_modvars,
            GB_HW : init_HW_modvars,
            GB_IS : init_IS_modvars,
            GB_HS : init_HS_modvars,
            GB_FS : init_FS_modvars,
            GB_BG : init_BG_modvars,
            GB_GV : init_GV_modvars,
            GB_HF : init_HF_modvars,
            GB_LG : init_LG_modvars,
            GB_HV : init_HV_modvars,
            GB_RN : init_RN_modvars,
            # GB_UA : init_UA_modvars,
        }

        #force GB, PJ, and DP to be in modules
        modules.append(GB_GB) if GB_GB not in modules else None
        modules.append(GB_PJ) if GB_PJ not in modules else None
        modules.append(GB_DP) if GB_DP not in modules else None

        # Add IH for modules that require it
        if (GB_IH not in modules) and ((GB_IC in modules) or (GB_PC in modules)):
            modules.append(GB_IH)

        if (GB_IS not in modules) and (GB_HW in modules):
            modules.append(GB_IS)

        if (GB_HW not in modules) and (GB_IC in modules):
            modules.append(GB_HW)

        if (GB_PC not in modules) and (GB_IC in modules):
            modules.append(GB_PC)

        # Temporarily require BG for TB app until we get modify projection working.
        if (GB_BG not in modules) and extra[GB_TB_COSTING_ON]:
            modules.append(GB_BG)

        modules_sorted = sort_modules(modules)

        #all_modvars = {}
        projection = {}#init_PJN_modvars(countryID, first_year, final_year, extra)

        # MVs needed for PC, IC; comes from IH
        prog_areas = None
        health_sys_admin_units = None
        # MVs needed for HW; comes from IS
        facilities = None
        # MVs needed for IC; comes from HW, PC, FP, CS respectively
        staff_types = None
        costing_sections = None
        activities = None
        active_methods = None # Also needed for RP
        selected_intervs_CS = None
        IVInfo_CS = None
        syph_among_WRA = None
        enter_BF_improve_data = None
        # MVs needed for BG; comes from IC, IH, respectively
        drugs_supplies = None
        interventions = None
        deliv_chans = None
        # MVs needed for RP
        method_mix = None

        for m in modules_sorted:
            if m in init_modvars_fun:
                try:
                    extra_info = None

                    # If we're on PC, add ModVars from IH that it needs
                    if m == GB_PC:
                        extra_info = {
                            ihmvt.IH_PROG_AREA_RECORDS_TAG_V1 : prog_areas,
                            ihmvt.IH_HEALTH_SYS_ADMIN_UNIT_RECORDS_TAG : health_sys_admin_units
                        }
                        extra_info.update(extra)

                    elif m == GB_IS:
                        extra_info = {
                            ihmvt.IH_PROG_AREA_RECORDS_TAG_V1 : prog_areas,
                        }
                        extra_info.update(extra)

                    elif m == GB_HW:
                        extra_info = {ismvt.IS_FACILITY_TYPE_RECORDS_TAG : facilities}
                        extra_info.update(extra)

                    elif m == GB_IC:
                        extra_info = {
                            ihmvt.IH_PROG_AREA_RECORDS_TAG_V1    : prog_areas,
                            hwmvt.HW_STAFF_TYPE_RECORDS_TAG_V1   : staff_types,
                            pcmvt.PC_COSTING_SECT_RECORDS_TAG    : costing_sections,
                            fpmvt.FP_ActiveMethodsTag            : active_methods,
                            csmvt.CS_SelectedIVSetTag            : selected_intervs_CS,
                            csmvt.CS_IVInfoTag                   : IVInfo_CS,
                            csmvt.CS_SyphilisAmongWRATag         : syph_among_WRA,
                            # csmvt.CS_EnterBFImproveDataTag       : enter_BF_improve_data,
                        }
                        extra_info.update(extra)

                    elif m == GB_BG:
                        extra_info = {
                            icmvt.IC_INTERV_RECORDS_TAG           : interventions,
                            icmvt.IC_DRUG_SUPPLY_RECORDS_TAG      : drugs_supplies,
                            ihmvt.IH_PROG_AREA_RECORDS_TAG_V1     : prog_areas,
                            ihmvt.IH_DELIV_CHAN_RECORDS_TAG_V1    : deliv_chans,
                            pcmvt.PC_COSTING_SECT_RECORDS_TAG     : costing_sections,
                            pcmvt.PC_ACT_RECORDS_TAG              : activities,
                            ismvt.IS_FACILITY_TYPE_RECORDS_TAG    : facilities,
                            hwmvt.HW_STAFF_TYPE_RECORDS_TAG_V1    : staff_types,
                        }
                        extra_info.update(extra)

                    elif m == GB_CS:
                        #Give these fields in extra default values in case they were not initialized.
                        def init_extra_field(key, value):
                            if key not in extra:
                                extra[key] = value

                        init_extra_field(GB_GSKMode, False)
                        init_extra_field('exploreLiST', False)
                        init_extra_field('level', '')
                        init_extra_field('source', '')
                        init_extra_field('appID', '')

                        extra_info = {}
                        extra_info.update(extra)

                    elif m == GB_FP:
                        #Give these fields in extra default values in case they were not initialized.
                        def init_extra_field(key, value):
                            if key not in extra:
                                extra[key] = value

                        init_extra_field('exploreLiST', False)
                        init_extra_field('RapidProj', GB_RP in modules)
                        init_extra_field('LiSTProj', GB_CS in modules)

                        extra_info = {}
                        extra_info.update(extra)

                    elif m == GB_RP:
                        extra_info = {
                            fpmvt.FP_ActiveMethodsTag : active_methods,
                            fpmvt.FP_MethodMixTag     : method_mix,
                        }
                        extra_info.update(extra)

                    else:
                        extra_info = extra

                    modvars = init_modvars_fun[m](countryID, first_year, final_year, extra_info)

                    # Save MVs needed in dependent modules
                    if m == GB_IH:
                        prog_areas = modvars[ihmvt.IH_PROG_AREA_RECORDS_TAG_V1]
                        deliv_chans = modvars[ihmvt.IH_DELIV_CHAN_RECORDS_TAG_V1]
                        health_sys_admin_units = modvars[ihmvt.IH_HEALTH_SYS_ADMIN_UNIT_RECORDS_TAG]

                    elif m == GB_IS:
                        facilities = modvars[ismvt.IS_FACILITY_TYPE_RECORDS_TAG]

                    elif m == GB_HW:
                        staff_types = modvars[hwmvt.HW_STAFF_TYPE_RECORDS_TAG_V1]

                    elif m == GB_PC:
                        costing_sections = modvars[pcmvt.PC_COSTING_SECT_RECORDS_TAG]
                        activities = modvars[pcmvt.PC_ACT_RECORDS_TAG]

                    elif m == GB_IC:
                        interventions = modvars[icmvt.IC_INTERV_RECORDS_TAG]
                        drugs_supplies = modvars[icmvt.IC_DRUG_SUPPLY_RECORDS_TAG]

                    elif m == GB_CS:
                        selected_intervs_CS = modvars[csmvt.CS_SelectedIVSetTag]
                        IVInfo_CS = modvars[csmvt.CS_IVInfoTag]
                        syph_among_WRA = modvars[csmvt.CS_SyphilisAmongWRATag]
                        # enter_BF_improve_data = modvars[csmvt.CS_EnterBFImproveDataTag]

                    elif m == GB_FP:
                        active_methods = modvars[fpmvt.FP_ActiveMethodsTag]
                        method_mix = modvars[fpmvt.FP_MethodMixTag]

                    projection.update(modvars)
                except Exception as e:
                    raise Exception({"msg": f"Failed to create {GB_MOD_STR[m]}",
                                     "error" : e.args[0] if len(e.args)>0 else ''})

            else:
                raise Exception(f"Module {m} does not exist")

#        for tag in projection:
#            try:
#                mv_jsonstr = ujson.dumps(projection[tag])
#            except:
#                logger.error(f'Failed to JSONIFY {tag}')

        if not extra_info.get("level","") == "":
            modvars = init_VW_modvars(countryID, first_year, final_year, extra_info, projection)
            projection.update(modvars)
            params.modules.append(GB_VW)

        return projection
