from app.main.data_model.games.leadedge.le_company_result import LECompanyResult
from app.main.util.service_helpers.games.leadedge import leadedge_helper
import numpy as np


class LeadEdgeSimulator:

    def __init__(self, round_scenario, external_report):
        self.round_scenario = round_scenario
        self.external_report = external_report
        self.demand_by_product = {}
        self.marketing_by_product = {}
        self.last_price_market_share_by_product = {}
        self.reputation_by_company = {}
        self.attraction_by_company = {}
        self.market_shares_by_company = {}
        self.quantities_by_company = {}
        self.company_scenarios_dict = {}
        self.company_results_dict = {}
        self.new_company_results_dict = {}
        self.revenu_per_capita_factor = 0
        self.qualities1 = None
        self.qualities2 = None
        self.qualities3 = None

    def init(self):
        self.company_scenarios_dict = {company_scenario.company_id: company_scenario for
                                       company_scenario in self.round_scenario.scenarios}
        self.company_results_dict = {company_result.company_id: company_result for
                                       company_result in self.round_scenario.results}
        for company_id in self.company_scenarios_dict:
            if company_id not in self.company_results_dict:
                company_result = LECompanyResult(company_id, self.round_scenario.id)
                company_result.init_market_shares(round(1. / len(self.company_scenarios_dict), 3))
                self.company_results_dict[company_id] = company_result
        self.revenu_per_capita_factor = pow(leadedge_helper.REVENU_PER_CAPITA_EVOLUTION,
                                            leadedge_helper.REVENU_PER_CAPITA_ELASTICITY)
        self.reputation_by_company = {company_result.company_id: company_result.reputation for
                                       company_result in self.round_scenario.results}

    def compute(self):
        self.init()
        self.compute_demands()
        self.compute_attractions()
        self.compute_market_shares_and_quantities()
        self.compute_new_results()

    def compute_demands(self):
        self.compute_product1_demand()
        self.compute_product2_demand()
        self.compute_product3_demand()

    def compute_attractions(self):
        self.compute_product1_attractions()
        self.compute_product2_attractions()
        self.compute_product3_attractions()

    def get_results(self):
        return list(self.new_company_results_dict.values())

    def get_demands(self):
        return self.demand_by_product[leadedge_helper.PRODUCT1], self.demand_by_product[leadedge_helper.PRODUCT2], self.demand_by_product[
            leadedge_helper.PRODUCT3]

    def get_promotions(self):
        return self.marketing_by_product[leadedge_helper.PRODUCT1], self.marketing_by_product[leadedge_helper.PRODUCT2], self.marketing_by_product[
            leadedge_helper.PRODUCT3]

    def get_last_price_market_shares(self):
        return self.last_price_market_share_by_product[leadedge_helper.PRODUCT1], self.last_price_market_share_by_product[
            leadedge_helper.PRODUCT2], \
               self.last_price_market_share_by_product[leadedge_helper.PRODUCT3]

    def get_result(self, company_id):
        return self.new_company_results_dict[company_id]

    def get_power_factor(self, initial_factor, division_factor, power_factor):
        if division_factor == 0:
            return 1
        result = initial_factor / division_factor
        if result == 0:
            return 1
        return float(pow(result, power_factor))

    def get_rounded_division(self, a, b, rounding):
        if b == 0 :
            return 0
        return round(a/b, rounding)

    def get_min_max_factor(self, a, b, c, d):
        if c == d:
            return 1
        return float((a-b)/(c-d))

    def get_variation_factor(self, a, b):
        if b == 0:
            if a == 0:
                return 0
            elif a < 0:
                return -100000
            else:
                return 100000
        return int(round(((a-b)/abs(b)) * 100, 3))

    def get_reputation(self, company_id):
        if company_id in self.reputation_by_company:
            return self.reputation_by_company[company_id]
        else:
            return 0.5

    def compute_product1_demand(self):
        last_demand = self.external_report.product1_demand
        new_demand = last_demand * leadedge_helper.PRODUCT1_FACTEUR_CROISSANCE * self.revenu_per_capita_factor
        price_factor = 0
        promotion_factor = 0
        for company_id in self.company_scenarios_dict:
            if company_id not in self.company_results_dict:
                continue
            company_scenario = self.company_scenarios_dict[company_id]
            company_result = self.company_results_dict[company_id]
            price_factor += company_scenario.opex_product1_price * company_result.product1_market_share
            promotion_factor += company_scenario.opex_product1_marketing
        self.marketing_by_product[leadedge_helper.PRODUCT1] = promotion_factor
        self.last_price_market_share_by_product[leadedge_helper.PRODUCT1] = price_factor
        price_factor = self.get_power_factor(price_factor, self.external_report.product1_last_price_market_share,
                                             leadedge_helper.PRODUCT1_PRICE_DEMAND_ELASTICITY)
        promotion_factor = self.get_power_factor(promotion_factor, (self.external_report.product1_last_promotions *
                                                                    leadedge_helper.PRODUCT1_FACTEUR_CROISSANCE),
                                                 leadedge_helper.PRODUCT1_PROMOTION_DEMAND_ELASTICITY)
        new_demand *= price_factor * promotion_factor
        self.demand_by_product[leadedge_helper.PRODUCT1] = min(int(last_demand * (1 + leadedge_helper.MAX_DEMAND_PRODUCT1_GROWTH)), int(new_demand))

    def compute_product2_demand(self):
        last_demand = self.external_report.product2_demand
        new_demand = last_demand * leadedge_helper.PRODUCT2_FACTEUR_CROISSANCE * self.revenu_per_capita_factor
        price_factor = 0
        promotion_factor = 0
        for company_id in self.company_scenarios_dict:
            if company_id not in self.company_results_dict:
                continue
            company_scenario = self.company_scenarios_dict[company_id]
            company_result = self.company_results_dict[company_id]
            price_factor += company_scenario.opex_product2_price * company_result.product2_market_share
            promotion_factor += company_scenario.opex_product2_marketing
        self.marketing_by_product[leadedge_helper.PRODUCT2] = promotion_factor
        self.last_price_market_share_by_product[leadedge_helper.PRODUCT2] = price_factor
        price_factor = self.get_power_factor(price_factor, self.external_report.product2_last_price_market_share,
                                             leadedge_helper.PRODUCT2_PRICE_DEMAND_ELASTICITY)
        promotion_factor = self.get_power_factor(promotion_factor, (self.external_report.product2_last_promotions *
                                                                    leadedge_helper.PRODUCT2_FACTEUR_CROISSANCE),
                                                 leadedge_helper.PRODUCT2_PROMOTION_DEMAND_ELASTICITY)
        new_demand *= price_factor * promotion_factor
        self.demand_by_product[leadedge_helper.PRODUCT2] = min(int(last_demand * (1 + leadedge_helper.MAX_DEMAND_PRODUCT2_GROWTH)),
                                                               int(new_demand))

    def compute_product3_demand(self):
        last_demand = self.external_report.product3_demand
        new_demand = last_demand * leadedge_helper.PRODUCT3_FACTEUR_CROISSANCE * self.revenu_per_capita_factor
        price_factor = 0
        promotion_factor = 0
        for company_id in self.company_scenarios_dict:
            if company_id not in self.company_results_dict:
                continue
            company_scenario = self.company_scenarios_dict[company_id]
            company_result = self.company_results_dict[company_id]
            price_factor += company_scenario.opex_product3_price * company_result.product3_market_share
            promotion_factor += company_scenario.opex_product3_marketing
        self.marketing_by_product[leadedge_helper.PRODUCT3] = promotion_factor
        self.last_price_market_share_by_product[leadedge_helper.PRODUCT3] = price_factor

        price_factor = self.get_power_factor(price_factor, self.external_report.product3_last_price_market_share,
                                             leadedge_helper.PRODUCT3_PRICE_DEMAND_ELASTICITY)

        promotion_factor = self.get_power_factor(promotion_factor, (self.external_report.product3_last_promotions *
                                                                    leadedge_helper.PRODUCT3_FACTEUR_CROISSANCE),
                                                 leadedge_helper.PRODUCT3_PROMOTION_DEMAND_ELASTICITY)
        new_demand *= price_factor * promotion_factor
        self.demand_by_product[leadedge_helper.PRODUCT3] = min(int(last_demand * (1 + leadedge_helper.MAX_DEMAND_PRODUCT3_GROWTH)),
                                                               int(new_demand))

    def compute_product1_attractions(self):
        prices = [company_scenario.opex_product1_price for company_scenario in self.round_scenario.scenarios]
        self.qualities1 = [company_scenario.capex_product1_quality for company_scenario in self.round_scenario.scenarios]
        marketing = [company_scenario.opex_product1_marketing for company_scenario in self.round_scenario.scenarios]
        min_prices = np.min(prices)
        max_prices = np.max(prices)
        min_qualities = np.min(self.qualities1)
        max_qualities = np.max(self.qualities1)
        min_marketing = np.min(marketing)
        max_marketing = np.max(marketing)
        reputations = list(self.reputation_by_company.values())
        if len(reputations) == 0:
            min_reputation = 0
            max_reputation = 0
        else:
            min_reputation = np.min(reputations)
            max_reputation = np.max(reputations)
        self.attraction_by_company[leadedge_helper.PRODUCT1] = {
            company_scenario.company_id: (0.25 * self.get_min_max_factor(max_prices, company_scenario.opex_product1_price, max_prices, min_prices))
                                         + (0.25 * self.get_min_max_factor(company_scenario.capex_product1_quality, min_qualities, max_qualities, min_qualities))
                                         + (0.25 * self.get_min_max_factor(company_scenario.opex_product1_marketing, min_marketing, max_marketing, min_marketing))
                                         + (0.25 * self.get_min_max_factor(self.get_reputation(company_scenario.company_id), min_reputation, max_reputation, min_reputation))
            for company_scenario in self.round_scenario.scenarios
        }

    def compute_product2_attractions(self):
        prices = [company_scenario.opex_product2_price for company_scenario in self.round_scenario.scenarios]
        self.qualities2 = [company_scenario.capex_product2_quality for company_scenario in
                     self.round_scenario.scenarios]
        marketing = [company_scenario.opex_product2_marketing for company_scenario in
                     self.round_scenario.scenarios]
        min_prices = np.min(prices)
        max_prices = np.max(prices)
        min_qualities = np.min(self.qualities2)
        max_qualities = np.max(self.qualities2)
        min_marketing = np.min(marketing)
        max_marketing = np.max(marketing)
        reputations = list(self.reputation_by_company.values())
        if len(reputations) == 0:
            min_reputation = 0
            max_reputation = 0
        else:
            min_reputation = np.min(reputations)
            max_reputation = np.max(reputations)
        self.attraction_by_company[leadedge_helper.PRODUCT2] = {
            company_scenario.company_id: (0.25 * self.get_min_max_factor(max_prices,
                                                                         company_scenario.opex_product2_price,
                                                                         max_prices, min_prices))
                                         + (0.25 * self.get_min_max_factor(company_scenario.capex_product2_quality,
                                                                           min_qualities, max_qualities, min_qualities))
                                         + (0.25 * self.get_min_max_factor(company_scenario.opex_product2_marketing,
                                                                           min_marketing, max_marketing, min_marketing))
                                         + (0.25 * self.get_min_max_factor(
                self.get_reputation(company_scenario.company_id), min_reputation, max_reputation,
                min_reputation))
            for company_scenario in self.round_scenario.scenarios
        }

    def compute_product3_attractions(self):
        prices = [company_scenario.opex_product3_price for company_scenario in self.round_scenario.scenarios]
        self.qualities3 = [company_scenario.capex_product3_quality for company_scenario in
                     self.round_scenario.scenarios]
        marketing = [company_scenario.opex_product3_marketing for company_scenario in
                     self.round_scenario.scenarios]
        min_prices = np.min(prices)
        max_prices = np.max(prices)
        min_qualities = np.min(self.qualities3)
        max_qualities = np.max(self.qualities3)
        min_marketing = np.min(marketing)
        max_marketing = np.max(marketing)
        reputations = list(self.reputation_by_company.values())
        if len(reputations) == 0:
            min_reputation = 0
            max_reputation = 0
        else:
            min_reputation = np.min(reputations)
            max_reputation = np.max(reputations)
        self.attraction_by_company[leadedge_helper.PRODUCT3] = {
            company_scenario.company_id: (0.25 * self.get_min_max_factor(max_prices,
                                                                         company_scenario.opex_product3_price,
                                                                         max_prices, min_prices))
                                         + (0.25 * self.get_min_max_factor(company_scenario.capex_product3_quality,
                                                                           min_qualities, max_qualities, min_qualities))
                                         + (0.25 * self.get_min_max_factor(company_scenario.opex_product3_marketing,
                                                                           min_marketing, max_marketing, min_marketing))
                                         + (0.25 * self.get_min_max_factor(
                self.get_reputation(company_scenario.company_id), min_reputation, max_reputation,
                min_reputation))
            for company_scenario in self.round_scenario.scenarios
        }

    def compute_market_shares_and_quantities(self):
        for product in self.attraction_by_company:
            product_attractions = self.attraction_by_company[product]
            self.market_shares_by_company[product] = {}
            self.quantities_by_company[product] = {}
            sum_attractions = float(np.sum(list(product_attractions.values())))
            product_demand = self.demand_by_product[product]
            for company_id in product_attractions:
                company_attraction = product_attractions[company_id]
                company_market_share = company_attraction/sum_attractions
                self.market_shares_by_company[product][company_id] = float(round(company_market_share, 3))
                self.quantities_by_company[product][company_id] = int(company_market_share * product_demand)

    def compute_new_total_market_shares(self):
        company_results = list(self.new_company_results_dict.values())
        revenues = [company_result.total_revenues for company_result in company_results]
        product1_revenues = [company_result.product1_revenues for company_result in company_results]
        product2_revenues = [company_result.product2_revenues for company_result in company_results]
        product3_revenues = [company_result.product3_revenues for company_result in company_results]
        sum_of_revenues = float(np.sum(revenues))
        sum_of_product1_revenues = float(np.sum(product1_revenues))
        sum_of_product2_revenues = float(np.sum(product2_revenues))
        sum_of_product3_revenues = float(np.sum(product3_revenues))
        for company_result in company_results:
            old_company_result = self.company_results_dict[company_result.company_id]
            company_result.total_market_share = self.get_rounded_division(company_result.total_revenues, sum_of_revenues, 3)
            company_result.total_market_share_variation = self.get_variation_factor(company_result.total_market_share, old_company_result.total_market_share)
            company_result.product1_market_share = self.get_rounded_division(company_result.product1_revenues, sum_of_product1_revenues, 3)
            company_result.product1_market_share_variation = self.get_variation_factor(company_result.product1_market_share, old_company_result.product1_market_share)
            company_result.product2_market_share = self.get_rounded_division(company_result.product2_revenues, sum_of_product2_revenues, 3)
            company_result.product2_market_share_variation = self.get_variation_factor(company_result.product2_market_share, old_company_result.product2_market_share)
            company_result.product3_market_share = self.get_rounded_division(company_result.product3_revenues, sum_of_product3_revenues, 3)
            company_result.product3_market_share_variation = self.get_variation_factor(company_result.product3_market_share, old_company_result.product3_market_share)

    def compute_new_results(self):
        for company_id in self.company_scenarios_dict:
            company_scenario = self.company_scenarios_dict[company_id]
            old_company_result = self.company_results_dict[company_id]
            new_company_result = self.create_new_company_result(company_id, company_scenario, old_company_result)
            self.new_company_results_dict[company_id] = new_company_result
        self.compute_new_total_market_shares()

    def get_sold_quantities_by_product(self, company_id, company_scenario, old_company_result):
        product1_quantity = min(self.quantities_by_company[leadedge_helper.PRODUCT1][company_id],
                                company_scenario.opex_product1_sales_quantity,
                                old_company_result.product1_inventory +
                                old_company_result.product1_capacity + company_scenario.capex_product1_new_units * leadedge_helper.CAR_PER_UNIT)
        product2_quantity = min(self.quantities_by_company[leadedge_helper.PRODUCT2][company_id],
                                company_scenario.opex_product2_sales_quantity,
                                old_company_result.product2_inventory +
                                old_company_result.product2_capacity + company_scenario.capex_product2_new_units * leadedge_helper.CAR_PER_UNIT)
        product3_quantity = min(self.quantities_by_company[leadedge_helper.PRODUCT3][company_id],
                                company_scenario.opex_product3_sales_quantity,
                                old_company_result.product3_inventory +
                                old_company_result.product3_capacity + company_scenario.capex_product3_new_units * leadedge_helper.CAR_PER_UNIT)
        return product1_quantity, product2_quantity, product3_quantity

    def update_products_kpis(self, company_id, new_company_result, company_scenario, old_company_result ):
        product1_quantity, product2_quantity, product3_quantity = self.get_sold_quantities_by_product(company_id,
                                                                                                      company_scenario,
                                                                                                      old_company_result)
        new_company_result.product1_revenues = product1_quantity * company_scenario.opex_product1_price
        new_company_result.product2_revenues = product2_quantity * company_scenario.opex_product2_price
        new_company_result.product3_revenues = product3_quantity * company_scenario.opex_product3_price
        new_company_result.total_revenues = new_company_result.product1_revenues + new_company_result.product2_revenues + new_company_result.product3_revenues

        new_company_result.product1_cogs = product1_quantity * \
                                           (company_scenario.opex_product1_unit_cost + company_scenario.opex_product1_supply_cost + company_scenario.capex_product1_quality)
        new_company_result.product2_cogs = product2_quantity * \
                                           (company_scenario.opex_product2_unit_cost + company_scenario.opex_product2_supply_cost + company_scenario.capex_product2_quality)
        new_company_result.product3_cogs = product3_quantity * \
                                           (company_scenario.opex_product3_unit_cost + company_scenario.opex_product3_supply_cost + company_scenario.capex_product3_quality)
        new_company_result.total_cogs = new_company_result.product1_cogs + new_company_result.product2_cogs + new_company_result.product3_cogs
        if (product1_quantity + product2_quantity + product3_quantity) == 0:
            new_company_result.unit_margin = 0
        else:
            new_company_result.unit_margin = round((new_company_result.total_revenues - new_company_result.total_cogs) / (product1_quantity + product2_quantity + product3_quantity),3)

        new_company_result.product1_market_share = self.market_shares_by_company[leadedge_helper.PRODUCT1][company_id]
        new_company_result.product2_market_share = self.market_shares_by_company[leadedge_helper.PRODUCT2][company_id]
        new_company_result.product3_market_share = self.market_shares_by_company[leadedge_helper.PRODUCT3][company_id]
        new_company_result.product1_sold_quantities = product1_quantity
        new_company_result.product2_sold_quantities = product2_quantity
        new_company_result.product3_sold_quantities = product3_quantity
        new_company_result.product1_capacity = old_company_result.product1_capacity + company_scenario.capex_product1_new_units * leadedge_helper.CAR_PER_UNIT
        new_company_result.product2_capacity = old_company_result.product2_capacity + company_scenario.capex_product2_new_units * leadedge_helper.CAR_PER_UNIT
        new_company_result.product3_capacity = old_company_result.product3_capacity + company_scenario.capex_product3_new_units * leadedge_helper.CAR_PER_UNIT
        new_company_result.total_capacity = new_company_result.product1_capacity + new_company_result.product2_capacity + new_company_result.product3_capacity

        new_company_result.product1_produced_capacity = min(new_company_result.product1_capacity,  company_scenario.opex_product1_prod_quantity)
        new_company_result.product2_produced_capacity = min(new_company_result.product2_capacity,  company_scenario.opex_product2_prod_quantity)
        new_company_result.product3_produced_capacity = min(new_company_result.product3_capacity,  company_scenario.opex_product3_prod_quantity)

        new_company_result.product1_inventory = max(0, (
                old_company_result.product1_inventory + new_company_result.product1_produced_capacity) - product1_quantity)
        new_company_result.product2_inventory = max(0, (
                old_company_result.product2_inventory + new_company_result.product2_produced_capacity) - product2_quantity)
        new_company_result.product3_inventory = max(0, (
                old_company_result.product3_inventory + new_company_result.product3_produced_capacity) - product3_quantity)
        new_company_result.inventory_value = (new_company_result.product1_inventory * company_scenario.opex_product1_price) + (new_company_result.product2_inventory * company_scenario.opex_product2_price) +(new_company_result.product3_inventory * company_scenario.opex_product3_price)
        new_company_result.marketing = company_scenario.opex_product1_marketing + company_scenario.opex_product2_marketing + company_scenario.opex_product3_marketing
        new_company_result.capex = company_scenario.capex_rd_budget + company_scenario.capex_product1_quality + company_scenario.capex_product2_quality + company_scenario.capex_product3_quality + (company_scenario.capex_product1_new_units + company_scenario.capex_product2_new_units + company_scenario.capex_product3_new_units) * leadedge_helper.EQUIPMENT_VALUE_PER_UNIT
        new_company_result.quality_budget = company_scenario.capex_product1_quality + company_scenario.capex_product2_quality + company_scenario.capex_product3_quality
        new_company_result.total_production = new_company_result.product1_produced_capacity + new_company_result.product2_produced_capacity + new_company_result.product3_produced_capacity
        new_company_result.tu = self.get_rounded_division(new_company_result.total_production, new_company_result.total_capacity, 3)

    def update_rebuts_results(self, new_company_result, company_scenario, old_company_result):
        min_qualities1 = np.min(self.qualities1)
        max_qualities1 = np.max(self.qualities1)
        min_qualities2 = np.min(self.qualities2)
        max_qualities2 = np.max(self.qualities2)
        min_qualities3 = np.min(self.qualities3)
        max_qualities3 = np.max(self.qualities3)
        quality_factor1 = self.get_min_max_factor(max_qualities1, company_scenario.capex_product1_quality, max_qualities1, min_qualities1)
        quality_factor2 = self.get_min_max_factor(max_qualities2, company_scenario.capex_product2_quality,
                                                  max_qualities2, min_qualities2)
        quality_factor3 = self.get_min_max_factor(max_qualities3, company_scenario.capex_product3_quality,
                                                  max_qualities3, min_qualities3)
        new_company_result.product1_rebuts_ratio = float(round((quality_factor1*0.04) + 0.01, 3))
        new_company_result.product2_rebuts_ratio = float(round((quality_factor2*0.04) + 0.01, 3))
        new_company_result.product3_rebuts_ratio = float(round((quality_factor3*0.04) + 0.01, 3))
        ratios = [new_company_result.product1_rebuts_ratio, new_company_result.product2_rebuts_ratio, new_company_result.product3_rebuts_ratio]
        new_company_result.rebuts = float(round(np.mean(ratios), 3))

    def update_hr_results(self, new_company_result, company_scenario, old_company_result):
        new_units = company_scenario.capex_product1_new_units + company_scenario.capex_product2_new_units + company_scenario.capex_product3_new_units
        new_company_result.number_of_cadres = old_company_result.number_of_cadres + new_units * leadedge_helper.CADRES_PER_UNIT
        new_company_result.number_of_technicians = old_company_result.number_of_technicians + new_units * leadedge_helper.TECHNICIANS_PER_UNIT
        new_company_result.number_of_ouvriers = old_company_result.number_of_ouvriers + new_units * leadedge_helper.OUVRIERS_PER_UNIT
        new_company_result.number_of_employees = new_company_result.number_of_cadres + new_company_result.number_of_technicians + new_company_result.number_of_ouvriers

        old_payroll_per_cadre = old_company_result.cadres_payrolls / old_company_result.number_of_cadres
        old_payroll_per_technician = old_company_result.technicians_payrolls / old_company_result.number_of_technicians
        old_payroll_per_ouvrier = old_company_result.ouvriers_payrolls / old_company_result.number_of_ouvriers
        new_payroll_per_cadre = old_payroll_per_cadre * (1+company_scenario.opex_salary_increase)
        new_payroll_per_technician = old_payroll_per_technician * (1 + company_scenario.opex_salary_increase)
        new_payroll_per_ouvrier = old_payroll_per_ouvrier * (1 + company_scenario.opex_salary_increase)
        new_company_result.cadres_payrolls = new_payroll_per_cadre * new_company_result.number_of_cadres
        new_company_result.technicians_payrolls = new_payroll_per_technician * new_company_result.number_of_technicians
        new_company_result.ouvriers_payrolls = new_payroll_per_ouvrier * new_company_result.number_of_ouvriers
        new_company_result.total_payroll = (new_company_result.cadres_payrolls + new_company_result.technicians_payrolls + new_company_result.ouvriers_payrolls)
        new_company_result.bonus_budget = company_scenario.opex_bonus_budget

    def update_finance_results(self, new_company_result, company_scenario, old_company_result):
        new_units = company_scenario.capex_product1_new_units + company_scenario.capex_product2_new_units + company_scenario.capex_product3_new_units
        new_company_result.gross_property_plant_and_equipment = old_company_result.gross_property_plant_and_equipment * (1 - leadedge_helper.EQUIPMENT_AMORTIZATION_RATIO) + new_units * leadedge_helper.EQUIPMENT_VALUE_PER_UNIT
        new_company_result.intellectual_property = old_company_result.intellectual_property * (1 - leadedge_helper.RD_AMORTIZATION_RATIO)
        new_company_result.depreciation_and_amortization = old_company_result.gross_property_plant_and_equipment * leadedge_helper.EQUIPMENT_AMORTIZATION_RATIO + old_company_result.intellectual_property * leadedge_helper.RD_AMORTIZATION_RATIO
        new_company_result.training_budget = company_scenario.opex_training_budget
        new_company_result.rd_expenses = company_scenario.capex_rd_budget
        new_company_result.total_rd_expenses = old_company_result.total_rd_expenses + new_company_result.rd_expenses
        new_company_result.intellectual_property = new_company_result.intellectual_property + new_company_result.rd_expenses
        new_company_result.sga_expenses = company_scenario.opex_bonus_budget + new_company_result.total_payroll + company_scenario.opex_training_budget
        new_company_result.gross_profit = new_company_result.total_revenues - new_company_result.total_cogs
        new_company_result.opex = new_company_result.marketing + new_company_result.sga_expenses + new_company_result.depreciation_and_amortization
        new_company_result.operating_profit = new_company_result.gross_profit - new_company_result.opex
        new_company_result.ebitda = new_company_result.gross_profit - new_company_result.marketing - new_company_result.sga_expenses
        new_company_result.previous_net_cash = old_company_result.net_cash
        new_company_result.inventory_net_cash = max(0, new_company_result.inventory_value - old_company_result.inventory_value)
        new_company_result.cash_for_equipment = new_units * leadedge_helper.EQUIPMENT_VALUE_PER_UNIT

    def update_debt(self, new_company_result, company_scenario, old_company_result):
        new_company_result.long_term_debt_interest_payment = old_company_result.long_term_remaining_debt * leadedge_helper.LONG_TERM_DEBT_INTEREST
        new_company_result.long_term_debt_nominal_payment = old_company_result.long_term_remaining_debt / 6.
        new_company_result.long_term_remaining_debt = old_company_result.long_term_remaining_debt - new_company_result.long_term_debt_nominal_payment + company_scenario.finance_long_term_debt
        new_company_result.short_term_debt_interest_payment = old_company_result.short_term_remaining_debt * leadedge_helper.SHORT_TERM_DEBT_INTEREST
        new_company_result.short_term_debt_nominal_payment = old_company_result.short_term_remaining_debt / 2.
        new_company_result.short_term_remaining_debt = old_company_result.short_term_remaining_debt - new_company_result.short_term_debt_nominal_payment + company_scenario.finance_short_term_debt
        new_company_result.total_interest = new_company_result.long_term_debt_interest_payment + new_company_result.short_term_debt_interest_payment
        new_company_result.debt = new_company_result.long_term_remaining_debt + new_company_result.short_term_remaining_debt
        new_company_result.taxes_expenses = max(0, (new_company_result.operating_profit - new_company_result.total_interest) * leadedge_helper.CORPORATE_TAX_RATE)
        new_company_result.net_profit = new_company_result.operating_profit - new_company_result.total_interest - new_company_result.taxes_expenses
        new_company_result.payout_ratio = company_scenario.finance_payout_ratio
        new_company_result.dividends_payment = max(0, new_company_result.net_profit * company_scenario.finance_payout_ratio)
        new_company_result.retained_earnings = old_company_result.retained_earnings + new_company_result.net_profit - new_company_result.dividends_payment
        new_company_result.net_cash_operations = new_company_result.net_profit + new_company_result.depreciation_and_amortization - new_company_result.inventory_net_cash
        new_company_result.net_cash_investing = new_company_result.cash_for_equipment + new_company_result.rd_expenses
        new_company_result.net_cash_long_term_debt = -company_scenario.finance_long_term_debt + new_company_result.long_term_debt_nominal_payment
        new_company_result.net_cash_short_term_debt = -company_scenario.finance_short_term_debt + new_company_result.short_term_debt_nominal_payment
        new_company_result.net_cash_financing = new_company_result.net_cash_long_term_debt + new_company_result.net_cash_short_term_debt + new_company_result.dividends_payment
        new_company_result.net_cash = new_company_result.previous_net_cash + new_company_result.net_cash_operations - new_company_result.net_cash_investing - new_company_result.net_cash_financing

    def update_market_kpis(self, new_company_result, company_scenario, old_company_result):
        theo_market_cap = round(new_company_result.dividends_payment / (
                    leadedge_helper.COST_OF_EQUITY), 3)
        new_company_result.market_cap = float(round(np.sum([0.30*theo_market_cap, 0.7*old_company_result.market_cap]), 3))
        new_company_result.enterprise_value = new_company_result.market_cap + new_company_result.debt
        new_company_result.leverage = round(new_company_result.debt / new_company_result.enterprise_value, 3)
        new_company_result.wacc = round((new_company_result.market_cap * leadedge_helper.COST_OF_EQUITY + new_company_result.debt * leadedge_helper.COST_OF_DEBT) / new_company_result.enterprise_value, 3)
        new_company_result.debt_ebitda = round(new_company_result.debt / new_company_result.ebitda, 3)
        if new_company_result.total_revenues == 0:
            new_company_result.bfr_ca = 0
        else:
            new_company_result.bfr_ca = round((new_company_result.inventory_value - new_company_result.taxes_expenses)/new_company_result.total_revenues, 3)
        new_company_result.total_assets = new_company_result.net_cash + new_company_result.inventory_value + new_company_result.gross_property_plant_and_equipment + new_company_result.intellectual_property
        new_company_result.total_liabilities = new_company_result.long_term_remaining_debt + new_company_result.short_term_remaining_debt
        new_company_result.total_equity = leadedge_helper.COMMON_STOCK + new_company_result.retained_earnings
        new_company_result.total_equity_and_liabilities = new_company_result.total_equity + new_company_result.total_liabilities

    def update_reputation(self, new_company_result, company_scenario, old_company_result):
        leverage_factor = 1
        if new_company_result.leverage != 0:
            leverage_factor = 1.0 / new_company_result.leverage
        actual_reputation = (0.25 / leverage_factor) + (0.25 * company_scenario.finance_payout_ratio) + (0.25 * new_company_result.total_payroll / new_company_result.enterprise_value) + (0.25 * new_company_result.rd_expenses / new_company_result.enterprise_value)
        new_company_result.reputation = float((actual_reputation + old_company_result.reputation) / 2.)

    def update_variations(self, new_company_result, old_company_result):
        new_company_result.total_revenues_variation = self.get_variation_factor(new_company_result.total_revenues, old_company_result.total_revenues)
        new_company_result.product1_cogs_variation = self.get_variation_factor(new_company_result.product1_cogs, old_company_result.product1_cogs)
        new_company_result.product2_cogs_variation = self.get_variation_factor(new_company_result.product2_cogs, old_company_result.product2_cogs)
        new_company_result.product3_cogs_variation = self.get_variation_factor(new_company_result.product3_cogs, old_company_result.product3_cogs)
        new_company_result.total_cogs_variation = self.get_variation_factor(new_company_result.total_cogs, old_company_result.total_cogs)
        new_company_result.unit_margin_variation = self.get_variation_factor(new_company_result.unit_margin, old_company_result.unit_margin)
        new_company_result.product1_inventory_variation = self.get_variation_factor(new_company_result.product1_inventory, old_company_result.product1_inventory)
        new_company_result.product2_inventory_variation = self.get_variation_factor(new_company_result.product2_inventory, old_company_result.product2_inventory)
        new_company_result.product3_inventory_variation = self.get_variation_factor(new_company_result.product3_inventory, old_company_result.product3_inventory)
        new_company_result.total_capacity_variation = self.get_variation_factor(new_company_result.total_capacity, old_company_result.total_capacity)
        new_company_result.tu_variation = self.get_variation_factor(new_company_result.tu, old_company_result.tu)
        new_company_result.inventory_value_variation = self.get_variation_factor(new_company_result.inventory_value, old_company_result.inventory_value)
        new_company_result.marketing_variation = self.get_variation_factor(new_company_result.marketing, old_company_result.marketing)
        new_company_result.capex_variation = self.get_variation_factor(new_company_result.capex, old_company_result.capex)
        new_company_result.quality_budget_variation = self.get_variation_factor(new_company_result.quality_budget, old_company_result.quality_budget)
        new_company_result.rebuts_variation = self.get_variation_factor(new_company_result.rebuts, old_company_result.rebuts)
        new_company_result.total_payroll_variation = self.get_variation_factor(new_company_result.total_payroll, old_company_result.total_payroll)
        new_company_result.gross_property_plant_and_equipment_variation = self.get_variation_factor(new_company_result.gross_property_plant_and_equipment , old_company_result.gross_property_plant_and_equipment)
        new_company_result.depreciation_and_amortization_variation = self.get_variation_factor(new_company_result.depreciation_and_amortization , old_company_result.depreciation_and_amortization)
        new_company_result.sga_expenses_variation = self.get_variation_factor(new_company_result.sga_expenses, old_company_result.sga_expenses)
        new_company_result.gross_profit_variation = self.get_variation_factor(new_company_result.gross_profit, old_company_result.gross_profit)
        new_company_result.opex_variation = self.get_variation_factor(new_company_result.opex, old_company_result.opex)
        new_company_result.operating_profit_variation = self.get_variation_factor(new_company_result.operating_profit, old_company_result.operating_profit)
        new_company_result.ebitda_variation = self.get_variation_factor(new_company_result.ebitda, old_company_result.ebitda)
        new_company_result.inventory_net_cash_variation = self.get_variation_factor(new_company_result.inventory_net_cash, old_company_result.inventory_net_cash)
        new_company_result.cash_for_equipment_variation = self.get_variation_factor(new_company_result.cash_for_equipment, old_company_result.cash_for_equipment)
        new_company_result.rd_expenses_variation = self.get_variation_factor(new_company_result.rd_expenses, old_company_result.rd_expenses)
        new_company_result.total_rd_expenses_variation = self.get_variation_factor( new_company_result.total_rd_expenses, old_company_result.total_rd_expenses)
        new_company_result.intellectual_property_variation = self.get_variation_factor( new_company_result.intellectual_property, old_company_result.intellectual_property)
        new_company_result.total_interest_variation = self.get_variation_factor(new_company_result.total_interest, old_company_result.total_interest)
        new_company_result.debt_variation = self.get_variation_factor(new_company_result.debt, old_company_result.debt)
        new_company_result.taxes_expenses_variation = self.get_variation_factor(new_company_result.taxes_expenses, old_company_result.taxes_expenses)
        new_company_result.net_profit_variation = self.get_variation_factor(new_company_result.net_profit, old_company_result.net_profit)
        new_company_result.payout_ratio_variation = self.get_variation_factor(new_company_result.payout_ratio, old_company_result.payout_ratio)
        new_company_result.dividends_payment_variation = self.get_variation_factor(new_company_result.dividends_payment, old_company_result.dividends_payment)
        new_company_result.net_cash_operations_variation = self.get_variation_factor(new_company_result.net_cash_operations, old_company_result.net_cash_operations)
        new_company_result.net_cash_investing_variation = self.get_variation_factor(new_company_result.net_cash_investing, old_company_result.net_cash_investing)
        new_company_result.net_cash_long_term_debt_variation = self.get_variation_factor(new_company_result.net_cash_long_term_debt, old_company_result.net_cash_long_term_debt)
        new_company_result.net_cash_short_term_debt_variation = self.get_variation_factor(new_company_result.net_cash_short_term_debt, old_company_result.net_cash_short_term_debt)
        new_company_result.net_cash_financing_variation = self.get_variation_factor(new_company_result.net_cash_financing, old_company_result.net_cash_financing)
        new_company_result.net_cash_variation = self.get_variation_factor(new_company_result.net_cash, old_company_result.net_cash)
        new_company_result.market_cap_variation = self.get_variation_factor(new_company_result.market_cap, old_company_result.market_cap)
        new_company_result.enterprise_value_variation = self.get_variation_factor(new_company_result.enterprise_value, old_company_result.enterprise_value)
        new_company_result.leverage_variation = self.get_variation_factor(new_company_result.leverage, old_company_result.leverage)
        new_company_result.wacc_variation = self.get_variation_factor(new_company_result.wacc, old_company_result.wacc)
        new_company_result.debt_ebitda_variation = self.get_variation_factor(new_company_result.debt_ebitda, old_company_result.debt_ebitda)
        new_company_result.bfr_ca_variation = self.get_variation_factor(new_company_result.bfr_ca, old_company_result.bfr_ca)
        new_company_result.training_budget_variation = self.get_variation_factor(new_company_result.training_budget, old_company_result.training_budget)
        new_company_result.bonus_budget_variation = self.get_variation_factor(new_company_result.bonus_budget, old_company_result.bonus_budget)
        new_company_result.total_assets_variation = self.get_variation_factor(new_company_result.total_assets, old_company_result.total_assets)
        new_company_result.total_liabilities_variation = self.get_variation_factor(new_company_result.total_liabilities, old_company_result.total_liabilities)
        new_company_result.total_equity_variation = self.get_variation_factor(new_company_result.total_equity, old_company_result.total_equity)
        new_company_result.total_equity_and_liabilities_variation = self.get_variation_factor(new_company_result.total_equity_and_liabilities, old_company_result.total_equity_and_liabilities)
        new_company_result.long_term_remaining_debt_variation = self.get_variation_factor(new_company_result.long_term_remaining_debt, old_company_result.long_term_remaining_debt)
        new_company_result.short_term_remaining_debt_variation = self.get_variation_factor(new_company_result.short_term_remaining_debt, old_company_result.short_term_remaining_debt)
        new_company_result.total_production_variation = self.get_variation_factor(new_company_result.total_production, old_company_result.total_production)
        new_company_result.retained_earnings_variation = self.get_variation_factor(new_company_result.retained_earnings, old_company_result.retained_earnings)
        new_company_result.number_of_employees_variation = self.get_variation_factor(new_company_result.number_of_employees, old_company_result.number_of_employees)

    def create_new_company_result(self, company_id, company_scenario, old_company_result):
        new_company_result = LECompanyResult(company_id, self.round_scenario.id)
        self.update_products_kpis(company_id, new_company_result, company_scenario, old_company_result)
        self.update_rebuts_results(new_company_result, company_scenario, old_company_result)
        self.update_hr_results(new_company_result, company_scenario, old_company_result)
        self.update_finance_results(new_company_result, company_scenario, old_company_result)
        self.update_debt(new_company_result, company_scenario, old_company_result)
        self.update_market_kpis(new_company_result, company_scenario, old_company_result)
        self.update_reputation(new_company_result, company_scenario, old_company_result)
        self.update_variations(new_company_result, old_company_result)


        return new_company_result



