# 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 SocialVulnerabilityScore(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(SocialVulnerabilityScore, self).__init__(incore_client)

    def run(self):
        """Execute the social vulnerability score 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_score_model(df_navs, df_dem)

    def social_vulnerability_score_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 score 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 score model.

        """
        return {
            "name": "social-vulnerability-score",
            "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 score 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",
                }
            ],
        }
