# Copyright (c) 2021 University of Illinois and others. All rights reserved.
#
# This program and the accompanying materials are made available under the
# terms of the Mozilla Public License v2.0 which accompanies this distribution,
# and is available at https://www.mozilla.org/en-US/MPL/2.0/

import pandas as pd
from pyincore import BaseAnalysis


class SocialVulnerability(BaseAnalysis):
    """This analysis computes a social vulnerability score for per associated zone in census data.

    The computation extracts zoning and a social vulnerability score obtained by computing demographic features of
    interest against national average values.

    The output of the computation is a dataset CSV format.

    Contributors
        | Science: Elaina Sutley, Amin Enderami
        | Implementation:  Amin Enderami, Santiago Núñez-Corrales, and NCSA IN-CORE Dev Team

    Related publications

    Args:
        incore_client (IncoreClient): Service authentication.

    """

    def __init__(self, incore_client):
        super(SocialVulnerability, self).__init__(incore_client)

    def run(self):
        """Execute the social vulnerability analysis using known parameters."""
        df_navs = pd.DataFrame(self.get_input_dataset('national_vulnerability_feature_averages').get_csv_reader())

        df_dem = pd.DataFrame(self.get_input_dataset('social_vulnerability_demographic_factors').get_csv_reader())

        # Make sure data types match
        df_dem["factor_white_nonHispanic"] = df_dem["factor_white_nonHispanic"].astype(float)
        df_dem["factor_owner_occupied"] = df_dem["factor_owner_occupied"].astype(float)
        df_dem["factor_earning_higher_than_national_poverty_rate"] =\
            df_dem["factor_earning_higher_than_national_poverty_rate"].astype(float)
        df_dem["factor_over_25_with_high_school_diploma_or_higher"] =\
            df_dem["factor_over_25_with_high_school_diploma_or_higher"].astype(float)
        df_dem["factor_without_disability_age_18_to_65"] =\
            df_dem["factor_without_disability_age_18_to_65"].astype(float)

        self.social_vulnerability_model(df_navs, df_dem)

    def social_vulnerability_model(self, df_navs, df_dem):
        """

        Args:
            df_navs (pd.DataFrame): dataframe containing the national average values for vulnerability factors
            df_dem (pd.DataFrame): dataframe containing demographic factors required for the vulnerability score

        Returns:

        """

        # Compute the social vulnerability index
        df_sv = self.compute_svs(df_dem, df_navs)

        # Save into a CSV file
        result_name = self.get_parameter("result_name")
        self.set_result_csv_data("sv_result", df_sv,
                                 name=result_name,
                                 source="dataframe")

    @staticmethod
    def compute_svs(df, df_navs):
        """ Computation of the social vulnerability score and corresponding zoning

        Args:
            df (pd.DataFrame): dataframe for the census geographic unit of interest
            df_navs (pd.DataFrame): dataframe containing national average values

        Returns:
            pd.DataFrame: Social vulnerability score and corresponding zoning data
        """
        navs = df_navs['average'].astype(float).array

        df['R1'] = df['factor_white_nonHispanic'] / navs[0]
        df['R2'] = df['factor_owner_occupied'] / navs[1]
        df['R3'] = df['factor_earning_higher_than_national_poverty_rate'] / navs[2]
        df['R4'] = df['factor_over_25_with_high_school_diploma_or_higher'] / navs[3]
        df['R5'] = df['factor_without_disability_age_18_to_65'] / navs[4]
        df['SVS'] = df.apply(lambda row: (row['R1'] + row['R2'] + row['R3'] + row['R4'] + row['R5']) / 5, axis=1)

        maximum_nav = 1/navs
        std = abs(1 - (sum(maximum_nav) / len(maximum_nav))) / 3

        lb_2 = 1 - 1.5*std
        lb_1 = 1 - 0.5*std
        ub_1 = 1 + 0.5*std
        ub_2 = 1 + 1.5*std

        zones = []

        for svs in df['SVS'].tolist():
            if svs < lb_2:
                new_zone = 'High Vulnerable (zone5)'
            elif svs < lb_1:
                new_zone = 'Medium to High Vulnerable (zone4)'
            elif svs < ub_1:
                new_zone = 'Medium Vulnerable (zone3)'
            elif svs < ub_2:
                new_zone = 'Medium to Low Vulnerable (zone2)'
            elif svs > ub_2:
                new_zone = 'Low Vulnerable (zone1)'
            else:
                new_zone = 'No Data'
            zones.append(new_zone)

        df['zone'] = zones
        df = df.sort_values(by="GEO_ID")

        return df

    def get_spec(self):
        """Get specifications of the housing serial recovery model.

        Returns:
            obj: A JSON object of specifications of the social vulnerability model.

        """
        return {
            'name': 'social-vulnerability',
            'description': 'Social vulnerability score model',
            'input_parameters': [
                {
                    'id': 'result_name',
                    'required': True,
                    'description': 'Result CSV dataset name',
                    'type': str
                },
            ],
            'input_datasets': [
                {
                    'id': 'national_vulnerability_feature_averages',
                    'required': True,
                    'description': 'A csv file with national vulnerability feature averages',
                    'type': ['incore:socialVulnerabilityFeatureAverages']
                },
                {
                    'id': 'social_vulnerability_demographic_factors',
                    'required': True,
                    'description': 'A csv file with social vulnerability demographic factors for a given geographic '
                                   'type',
                    'type': ['incore:socialVulnerabilityDemFactors']
                }
            ],
            'output_datasets': [
                {
                    'id': 'sv_result',
                    'parent_type': 'social_vulnerability_score',
                    'description': 'A csv file with zones containing demographic factors'
                                   'qualified by a social vulnerability score',
                    'type': 'incore:socialVulnerabilityScore'
                }
            ]
        }
