from openfisca_tunisia.variables.base import *  # noqa analysis:ignore


class nb_enf(Variable):
    value_type = float
    entity = FoyerFiscal
    label = "Nombre d'enfants"
    definition_period = YEAR

    def formula(foyer_fiscal, period, parameters):
        """
        TODO: fixme
        """
        age = foyer_fiscal.members("age", period=period.first_month)
        famille = parameters(period.start).impot_revenu.deductions.famille
        # res =+ (
        #    (ag < 20) +
        #    (ag < 25)*not_(boursier)*()
        #    )
        condition = (age >= 0) * (age <= famille.age)
        return foyer_fiscal.sum(condition, role=FoyerFiscal.PERSONNE_A_CHARGE)


class nb_enf_sup(Variable):
    value_type = float
    entity = FoyerFiscal
    label = "Nombre d'enfants étudiants du supérieur non boursiers"
    definition_period = YEAR

    def formula(foyer_fiscal, period):
        """
        TODO: Nombre d'enfants étudiants du supérieur non boursiers
        """
        # age = foyer_fiscal.members('age', period = period)
        etudiant = foyer_fiscal.members("etudiant", period=period)
        boursier = foyer_fiscal.members("boursier", period=period)

        return foyer_fiscal.sum(etudiant * not_(boursier))


class nb_infirme(Variable):
    value_type = float
    entity = FoyerFiscal
    label = "Nombre d'enfants infirmes"
    definition_period = YEAR

    def formula(foyer_fiscal, period):
        """
        Nombre d'enfants infirmes
        """
        infirme = foyer_fiscal.members("handicap", period=period) >= 3

        return foyer_fiscal.sum(1 * infirme, role=FoyerFiscal.PERSONNE_A_CHARGE)


class nb_parents(Variable):
    value_type = float
    entity = FoyerFiscal
    label = "Nombre de parents"
    definition_period = YEAR

    def formula(foyer_fiscal, period):
        """
        Nombre de parents
        """
        return (foyer_fiscal.declarant_principal("age", period) > 20) + (
            foyer_fiscal.conjoint("age", period) > 20
        )


class chef_de_famille(Variable):
    value_type = bool
    entity = FoyerFiscal
    label = "Indicatrice de chef de famille"
    definition_period = YEAR
    set_input = set_input_dispatch_by_period

    # Du point de vue fiscal, est considéré chef de famille :
    # - L’époux ;
    # - Le divorcé ou la divorcée qui a la garde des enfants (divorce & enfnats);
    # - Le veuf ou la veuve même sans enfants à charge;
    # - L’adoptant ou l’adoptante (adoptant). TODO
    # Cependant, l’épouse a la qualité de chef de famille dans les deux cas suivants :
    # - Lorsqu’elle justifie que le mari ne dispose d’aucune source de revenu durant l’année de réalisation du
    #   revenu. Tel est le cas d’une femme qui dispose d’un revenu et dont le mari, poursuivant des études, ne dispose
    #   d’aucun revenu propre. (marie & sexe & revenu_epoux == 0 & revenu_individu > 0) TODO
    # - Lorsque remariée, elle a la garde d’enfants issus d’un précédent mariage. TODO
    # Compte tenu de ce qui précède, n’est pas considéré comme chef de famille et ne bénéficie d’aucune déduction :
    # - Le célibataire ou la célibataire ;
    # - Le divorcé ou la divorcée qui n’a pas la garde des enfants ;
    # - La femme durant le mariage (sauf si elle dispose d’un revenu alors que son mari ne dispose d’aucun revenu) ;
    # - L’époux qui ne dispose pas d’une source de revenu. Dans ce cas, l’épouse acquiert la qualité de chef de famille
    #   au cas où elle réalise des revenus.

    def formula(foyer_fiscal, period):
        male = foyer_fiscal.declarant_principal("male", period=period)
        marie = foyer_fiscal.declarant_principal("marie", period=period)
        divorce = foyer_fiscal.declarant_principal("divorce", period=period)
        veuf = foyer_fiscal.declarant_principal("veuf", period=period)
        nb_enf = foyer_fiscal("nb_enf", period=period)
        chef_de_famille = (
            veuf | (marie & male) | (divorce & (nb_enf > 0))  # | (marie & (not male)) |
        )
        return chef_de_famille


# Revenus catégoriels (voir répertoire idoine)
# 1. Bénéfices industriels et commerciaux  bic.py
# 2. Bénéfices des professions non commerciales  bnc.py
# 3. Bénéfices de l'exploitation agricole et de pêche beap.py
# 4. Revenus fonciers foncier.py
# 5. Traitements, salaires, indemnités, pensions et rentes viagères  tspr.py
# 6. Revenus de valeurs mobilières et de capitaux mobiliers  rvcm.py
# 7. revenus de source étrangère

# Déroulé du calcul de l'irpp


class rng(Variable):
    value_type = float
    entity = FoyerFiscal
    label = "Revenu net global"
    definition_period = YEAR

    def formula(foyer_fiscal, period):
        return (
            foyer_fiscal("bic", period=period)
            + foyer_fiscal("bnc", period=period)
            + foyer_fiscal("beap", period=period)
            + foyer_fiscal("revenus_fonciers", period=period)
            + foyer_fiscal("tspr", period=period)
            + foyer_fiscal("rvcm", period=period)
            + foyer_fiscal("revenus_source_etrangere", period=period)
        )


#############################
#    Déductions
#############################

# # 1/ Au titre des revenus et bénéfices provenant de l’activité

# # 2/ Autres déductions


class deduction_interets(Variable):
    value_type = float
    entity = FoyerFiscal
    label = "Déductions intérêts issus de comptes spéciaux ou d'obligations"
    definition_period = YEAR

    def formula(foyer_fiscal, period, parameters):
        compte_special_epargne_banque = foyer_fiscal("compte_special_epargne_banque")
        compte_special_epargne_cent = foyer_fiscal("compte_special_epargne_cent")
        emprunt_obligataire = foyer_fiscal("emprunt_obligataire")
        deductions = parameters(period).impot_revenu.deductions
        return max_(
            max_(
                (
                    max_(compte_special_epargne_banque, deductions.banq.plaf)
                    + max_(compte_special_epargne_cent, deductions.cent.plaf)
                ),
                deductions.banq.plaf,
            )
            + max_(emprunt_obligataire, deductions.oblig.plaf),
            deductions.oblig.plaf,
        )


class deduction_famille(Variable):
    value_type = float
    entity = FoyerFiscal
    label = "Déductions pour situation et charges de famille"
    definition_period = YEAR

    def formula(foyer_fiscal, period, parameters):
        # rng = foyer_fiscal('rng', period = period)
        chef_de_famille = foyer_fiscal("chef_de_famille", period=period)
        nb_enf = foyer_fiscal("nb_enf", period=period)
        nb_infirme = foyer_fiscal("nb_infirme", period=period)
        famille = parameters(period.start).impot_revenu.deductions.famille
        #  chef de famille
        chef_de_famille = famille.chef_de_famille * chef_de_famille

        infirme = famille.infirme * nb_infirme
        nb_enf = max_(nb_enf - nb_infirme, 0)
        enf = (
            (nb_enf >= 1) * famille.enf1
            + (nb_enf >= 2) * famille.enf2
            + (nb_enf >= 3) * famille.enf3
            + (nb_enf >= 4) * famille.enf4
        )

        #    sup = P.enf_sup * nb_enf_sup

        #    parent = min_(P.parent_taux * rng, P.parent_max)
        #    return chef_de_famille + enf + sup + infirme + parent

        res = chef_de_famille + enf + infirme
        return res


class deduction_rente(Variable):
    value_type = float
    entity = FoyerFiscal
    label = "Arrérages et rentes payées à titre obligatoire et gratuit"
    definition_period = YEAR

    def formula(foyer_fiscal, period):
        rente = foyer_fiscal("rente", period=period)

        return rente  # TODO:


class deduction_assurance_vie(Variable):
    value_type = float
    entity = FoyerFiscal
    label = "Primes afférentes aux contrats d'assurance-vie collectifs ou individuels"
    definition_period = YEAR

    def formula(foyer_fiscal, period, parameters):
        primes_assurance_vie = foyer_fiscal.members(
            "prime_assurance_vie", period=period
        )
        somme_primes_assurance_vie = foyer_fiscal.sum(primes_assurance_vie)
        marie = foyer_fiscal.declarant_principal("statut_marital", period=period)
        nb_enf = foyer_fiscal("nb_enf", period=period)
        assurance_vie = parameters(period.start).impot_revenu.deductions.assurance_vie
        deduction = min_(
            somme_primes_assurance_vie,
            assurance_vie.plaf
            + marie * assurance_vie.conj_plaf
            + nb_enf * assurance_vie.enf_plaf,
        )
        return deduction


#     - Les remboursements des prêts universitaires en principal et en intérêts
#    - Revenus et bénéfices réinvestis dans les conditions et limites
#    prévues par la législation en vigueur dont notamment :
#     les revenus provenant de l'exportation, totalement pendant 10
#    ans à partir de la première opération d'exportation;
#     les revenus provenant de l’hébergement et de la restauration
#    des étudiants pendant dix ans ;
#     les revenus provenant du courtage international dans la limite
#    de 50% pendant 10 ans à partir de la première opération de courtage
#    international;
#     les montants déposés dans les comptes-épargne pour
#    l’investissement et dans les comptes-épargne en actions dans la
#    limite de 20.000D par an et sous réserve du minimum d’IR ;
#     la plus-value provenant des opérations de transmission des
#    entreprises en difficultés économiques ou des entreprises sous
#    forme de participations ou d’actifs pour départ du propriétaire à la
#    retraite ou pour incapacité de poursuivre la gestion et ce, sous
#    certaines conditions,
#     la plus-value provenant de l’apport d’une entreprise individuelle
#    au capital d’une société ;
#     les revenus réinvestis dans la souscription au capital des
#    entreprises dans les conditions prévues par la législation relative aux
#    incitations fiscales;
#     les revenus réinvestis dans la réalisation de projets
#    d’hébergement et de restauration universitaires sous réserve du
#    minimum d’IR.
#     Les revenus provenant de la location des terres agricoles
#    réservées aux grandes cultures objet de contrat de location conclus
#    pour une période minimale de trois ans.


class deduction_smig(Variable):
    value_type = float
    entity = FoyerFiscal
    label = "Déduction supplémentaire pour les salariés payés au SMIG et SMAG"
    definition_period = YEAR

    def formula(foyer_fiscal, period):
        chef = foyer_fiscal("chef_de_famille", period=period)

        return 0 * chef  # TODO: voir avec tspr


class deductions(Variable):
    value_type = float
    entity = FoyerFiscal
    label = "Revenu net imposable"
    definition_period = YEAR

    def formula(foyer_fiscal, period):
        return (
            foyer_fiscal("deduction_famille", period=period)
            + foyer_fiscal.declarant_principal("rente", period=period)
            + foyer_fiscal("deduction_assurance_vie", period=period)
            + foyer_fiscal.sum(
                foyer_fiscal.members(
                    "plus_value_cession_actifs_cotes_bourse", period=period
                )
            )
            + foyer_fiscal.sum(
                foyer_fiscal.members("interets_acquisition_logement", period=period)
            )
        )


class revenu_net_imposable(Variable):
    value_type = float
    entity = FoyerFiscal
    label = "Revenu net imposable"
    definition_period = YEAR

    def formula(foyer_fiscal, period):
        """
        Revenu net imposable ie soumis à au barême de l'impôt après déduction des dépenses
        et charges professionnelles et des revenus non soumis à l'impôt
        """
        return max_(foyer_fiscal("rng", period) - foyer_fiscal("deductions", period), 0)


class impot_revenu_brut(Variable):
    value_type = float
    entity = FoyerFiscal
    label = "Impôt brut avant non-imposabilité"
    definition_period = YEAR

    def formula(foyer_fiscal, period, parameters):
        revenu_net_imposable = foyer_fiscal("revenu_net_imposable", period=period)
        bareme = parameters(period.start).impot_revenu.bareme
        impot_revenu_brut = bareme.calc(revenu_net_imposable)
        return impot_revenu_brut


class exoneration(Variable):
    value_type = bool
    entity = FoyerFiscal
    label = "Exoneration de l'impôt sur le revenu des personnes physiques"
    definition_period = YEAR
    end = "2016-12-31"

    def formula_2014(foyer_fiscal, period, parameters):
        # Les éligibles ne doivent percevoir que des salaires et des pensions
        rng = foyer_fiscal("rng", period=period)
        tspr = foyer_fiscal("tspr", period=period)
        eligble = rng == tspr
        # Condition de revenu
        deduction_famille = foyer_fiscal("deduction_famille", period=period)
        condition_de_revenu = (rng - deduction_famille) <= parameters(
            period.start
        ).impot_revenu.exoneration.seuil
        return eligble * condition_de_revenu


class irpp(Variable):
    value_type = float
    entity = FoyerFiscal
    label = "Impôt sur le revenu des personnes physiques"
    definition_period = YEAR

    def formula(foyer_fiscal, period):
        impot_revenu_brut = foyer_fiscal("impot_revenu_brut", period=period)
        exoneration = foyer_fiscal("exoneration", period=period)
        return impot_revenu_brut * not_(exoneration)


class irpp_salarie_preleve_a_la_source(Variable):
    value_type = float
    entity = Individu
    label = "Impôt sur le revenu des personnes physiques prélevé à la source pour les salariés"
    definition_period = MONTH

    def formula(individu, period, parameters):
        salaire_imposable = individu("salaire_imposable", period=period)
        deduction_famille_annuelle = individu.foyer_fiscal(
            "deduction_famille", period=period.this_year
        )

        return calcule_impot_revenu_brut(
            salaire_imposable,
            deduction_famille_annuelle,
            period,
            parameters,
        )


class irpp_net_a_payer(Variable):
    value_type = float
    entity = FoyerFiscal
    label = "IRPP net des prélèvements à la source"
    definition_period = YEAR

    def formula(foyer_fiscal, period, parameters):
        return foyer_fiscal("irpp", period) - foyer_fiscal.sum(
            foyer_fiscal.members(
                "irpp_salarie_preleve_a_la_source", period, options=[ADD]
            )
        )


# Utils


def calcule_base_imposable(
    salaire_mensuel, deduction_famille_annuelle, period, parameters
):
    revenu_assimile_salaire = salaire_mensuel
    smig_40h_mensuel = parameters(period.start).marche_travail.smig_40h_mensuel
    smig = revenu_assimile_salaire <= smig_40h_mensuel
    tspr = parameters(period.start).impot_revenu.tspr
    abattement_frais_professionnels = min_(
        tspr.abat_sal * revenu_assimile_salaire, tspr.max_abat_sal / 12
    )

    if period.start.year >= 2011:
        revenu_assimile_salaire_apres_abattement = max_(
            revenu_assimile_salaire
            - abattement_frais_professionnels
            - max_(
                smig * tspr.abattement_pour_salaire_minimum,
                (revenu_assimile_salaire <= tspr.smig_ext)
                * tspr.abattement_pour_salaire_minimum,
            ),
            0,
        )
    else:
        revenu_assimile_salaire_apres_abattement = max_(
            revenu_assimile_salaire
            - abattement_frais_professionnels
            - smig * tspr.abattement_pour_salaire_minimum,
            0,
        )

    non_exonere = revenu_assimile_salaire_apres_abattement >= 0
    if 2014 <= period.start.year <= 2016:
        non_exonere = (
            12 * revenu_assimile_salaire_apres_abattement - deduction_famille_annuelle
        ) > parameters(period.start).impot_revenu.exoneration.seuil

    return non_exonere, revenu_assimile_salaire_apres_abattement


def calcule_impot_revenu_brut(
    salaire_mensuel, deduction_famille_annuelle, period, parameters
):
    non_exonere, revenu_assimile_salaire_apres_abattement = calcule_base_imposable(
        salaire_mensuel, deduction_famille_annuelle, period, parameters
    )
    bareme = parameters(period.start).impot_revenu.bareme
    return (
        non_exonere
        * bareme.calc(
            12 * revenu_assimile_salaire_apres_abattement - deduction_famille_annuelle
        )
    ) / 12
