from otree.api import (
    models, widgets, BaseConstants, BaseSubsession, BaseGroup, BasePlayer,
    Currency as c, currency_range
)

# from mpl.config import *
import random
from random import randrange
import importlib, os

APP_DIR=os.path.basename(os.path.dirname(__file__))

mdl = importlib.import_module(APP_DIR+'.config')
# is there an __all__?  if so respect it
if "__all__" in mdl.__dict__:
    names = mdl.__dict__["__all__"]
else:
    # otherwise we import all names that don't begin with _
    names = [x for x in mdl.__dict__ if not x.startswith("_")]
# now drag them in
globals().update({k: getattr(mdl, k) for k in names})

author = 'Felix Holzmeister'

doc = """
Multiple price list task as proposed by Holt/Laury (2002), American Economic Review 92(5).
"""


# ******************************************************************************************************************** #
# *** CLASS SUBSESSION
# ******************************************************************************************************************** #
class Subsession(BaseSubsession):
    proba = models.IntegerField()

    def creating_session(self):
        shuffled_probabilities = Constants.probabilities.copy()
        random.shuffle(shuffled_probabilities)
        print(shuffled_probabilities)

        for i in range(1, self.round_number + 1):

            if self.round_number == 1:
                # Shuffle probabilities once and store them in session vars
                self.session.vars['proba'] = shuffled_probabilities[0]
            elif self.round_number == 2:
                self.session.vars['proba'] = shuffled_probabilities[1]
            elif self.round_number == 3:
                self.session.vars['proba'] = shuffled_probabilities[2]
            elif self.round_number == 4:
                self.session.vars['proba'] = shuffled_probabilities[3]
            else:
                self.session.vars['proba'] = shuffled_probabilities[4]

            self.proba = self.session.vars['proba']

            n = Constants.num_choices
            for p in self.get_players():
                indices = [j for j in range(1, n)]
                if Constants.certain_choice:
                    indices.append(n)

                if Constants.percentage:
                    probabilities = [
                        "{0:.2f}".format(self.proba) + "%" for k in indices
                    ]
                else:
                    probabilities = [
                        str(self.proba) + "/" + str(100) for k in indices
                    ]

                # Lottery A remains constant (high = 20, low = 0)
                lottery_a_hi_values = [Constants.lottery_a_hi + 0*k for k in indices]
                lottery_a_lo_values = [Constants.lottery_a_lo * k for k in indices]

                # Lottery B increases by 1€ in each row
                lottery_a_certain = [Constants.lottery_a_certain + k for k in indices]

                form_fields = ['choice_' + str(k) for k in indices]

                p.participant.vars['mpl_choices'] = list(
                    zip(indices, form_fields, probabilities, lottery_a_hi_values,
                        lottery_a_lo_values, lottery_a_certain,
                        ["pie_a_%d" % i for i in indices],
                        ["pie_b_%d" % i for i in indices],[self.round_number for i in indices])
                )

                # Randomly determine index/choice of binary decision to pay
                p.participant.vars['mpl_index_to_pay'] = random.choice(indices)
                p.participant.vars['mpl_choice_to_pay'] = 'choice_' + str(p.participant.vars['mpl_index_to_pay'])

                # Initiate list for choices made
                p.participant.vars['mpl_choices_made'] = [None for _ in range(1, n + 1)]

            # Generate random switching point for PlayerBot in tests.py
            for participant in self.session.get_participants():
                participant.vars['mpl_switching_point'] = random.randint(1, n)

# ******************************************************************************************************************** #
# *** CLASS GROUP
# ******************************************************************************************************************** #
class Group(BaseGroup):
    pass


# ******************************************************************************************************************** #
# *** CLASS PLAYER
# ******************************************************************************************************************** #
class Player(BasePlayer):

    # add model fields to class player
    # ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
    if Constants.certain_choice:
        for j in range(1, Constants.num_choices + 1):
            locals()['choice_' + str(j)] = models.StringField()
        del j
    else:
        for j in range(1, Constants.num_choices):
            locals()['choice_' + str(j)] = models.StringField()
        del j

    random_draw = models.IntegerField()
    choice_to_pay = models.StringField()
    option_to_pay = models.StringField()
    inconsistent = models.IntegerField()
    switching_row = models.IntegerField()
    current_payoff = models.FloatField()

    # set player's payoff
    # ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
    def set_payoffs(self):
        # Retrieve the chosen row (randomly selected during session creation)
        chosen_row = self.participant.vars['mpl_index_to_pay']

        # Find the probability for this row (Extract from mpl_choices)
        for tpl in self.participant.vars['mpl_choices']:
            if tpl[0] == chosen_row:  # Matching the index (first element)
                probability = int(tpl[2].split('/')[0])  # Extract "x" from "x/100"
                break
        else:
            probability = 0  # Default case (should not happen)

        # Generate a random number between 1-100 to simulate probability
        self.random_draw = randrange(1, 101)  # Random draw from 1 to 100

        # Set <choice_to_pay> to participant.var['choice_to_pay'] determined in creating_session
        self.choice_to_pay = self.participant.vars['mpl_choice_to_pay']

        # Determine whether Lottery A or B was chosen
        self.option_to_pay = getattr(self, self.choice_to_pay)

        # Set player's payoff
        if self.option_to_pay == 'A':
            if self.random_draw <= probability:
                my_payoff = Constants.lottery_a_hi  # High outcome with given probability
            else:
                my_payoff = Constants.lottery_a_lo  # Low outcome otherwise
        elif self.option_to_pay == 'B':
            # Safe option: Find corresponding certain value (6th element in tuple)
            for tpl in self.participant.vars['mpl_choices']:
                if tpl[0] == chosen_row:
                    my_payoff = tpl[5]  # The certain value
                    break
            else:
                my_payoff = 0  # Default case (error handling)

        if not Constants.hypothetic:
            self.payoff = my_payoff

        self.current_payoff = my_payoff
        self.participant.vars['mpl_payoff'] = my_payoff
        self.participant.holt_laury_gain = my_payoff
        # If no matching row is found (error case)

        if not Constants.hypothetic:
            self.payoff = my_payoff
        self.current_payoff = my_payoff
        # set payoff as global variable
        # ------------------------------------------------------------------------------------------------------------
        self.participant.vars['mpl_payoff'] = my_payoff
        self.participant.holt_laury_gain = my_payoff

    # determine consistency
    # ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
    def set_consistency(self):

        n = Constants.num_choices

        # replace A's by 1's and B's by 0's
        self.participant.vars['mpl_choices_made'] = [
            1 if j == 'A' else 0 for j in self.participant.vars['mpl_choices_made']
        ]

        # check for multiple switching behavior
        for j in range(1, n):
            choices = self.participant.vars['mpl_choices_made']
            self.inconsistent = 1 if choices[j] > choices[j - 1] else 0
            if self.inconsistent == 1:
                break

    # determine switching row
    # ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
    def set_switching_row(self):

        # set switching point to row number of first 'B' choice
        if self.inconsistent == 0:
            self.switching_row = sum(self.participant.vars['mpl_choices_made']) + 1

