from app.main.data_model.games.finedge.fe_order import FEOrder
from app.main.data_model.games.finedge.fe_portfolio import FEPortfolio
from app.main.data_model.games.finedge.fe_portfolio_indicator import FEPortfolioIndicator
from app.main.util.json_objects.games.finedge.asset_class_dto import FEAssetClassDTO
from app.main.util.json_objects.games.finedge.asset_dto import FEAssetDTO

from app.main.util.json_objects.games.finedge.indicator_dto import FEIndicatorDTO
from app.main.util.json_objects.games.finedge.market_dto import FEMarketDTO
from app.main.util.json_objects.games.finedge.historical_data_dto import FEHistoricalDataDTO
from app.main.util.json_objects.games.finedge.market_view_dto import FEMarketViewDTO
from app.main.util.json_objects.games.finedge.order_dto import FEOrderDTO
from datetime import datetime

from app.main.util.json_objects.games.finedge.performance_dto import FEPerformanceDTO
from app.main.util.json_objects.games.finedge.market_performance_dto import FEMarketPerformanceDTO
from app.main.util.json_objects.games.finedge.portfolio_indicator_dto import FELastPortfolioIndicatorDTO
from app.main.util.json_objects.games.finedge.position_dto import FEPositionDTO
from app.main.util.json_objects.games.finedge.ranking_dto import FERankingDTO
from app.main.util.json_objects.games.finedge.portfolio_dto import FEPortfolioDTO
from app.main.util.service_helpers.common.user_service_helper import UserServiceHelper
import app.main.util.helpers as helpers


class FinEdgeServiceHelper:

    def __init__(self):
        pass

    @staticmethod
    def create_order(order_data):
        asset_id = order_data['asset_id']
        portfolio_id = order_data['portfolio_id']
        market_date = datetime.now()
        order_quantity = order_data['quantity']
        order_price = order_data['price']
        order_direction = order_data['direction']
        order_type = order_data['order_type']
        order = FEOrder(asset_id, portfolio_id, market_date, order_quantity, order_price, order_direction, order_type)
        return order

    @staticmethod
    def get_order_type_name(order_type):
        types = ['Market', 'Limit', 'Stop']
        return types[order_type]

    @staticmethod
    def create_order_dto(order):
        order_dto = FEOrderDTO()
        order_dto.id = order.id
        order_dto.portfolio_id = order.portfolio_id
        order_dto.asset_id = order.asset_id
        order_dto.quantity = order.quantity
        order_dto.price = order.price
        order_dto.order_type = order.order_type
        order_dto.direction = order.direction
        order_dto.market_date = order.market_date
        order_dto.execution_date = order.execution_date
        order_dto.status = order.status
        order_dto.order_type_name = FinEdgeServiceHelper.get_order_type_name(order.order_type)
        return order_dto

    @staticmethod
    def create_order_dtos(orders):
        order_dtos = []
        if orders is None:
            return order_dtos
        for order in orders:
            order_dto = FinEdgeServiceHelper.create_order_dto(order)
            order_dtos.append(order_dto)
        return order_dtos

    @staticmethod
    def create_fe_portfolio(participant_id, game_session_id, start_date, end_date):
        portfolio = FEPortfolio(participant_id, game_session_id, start_date, end_date)
        return portfolio

    @staticmethod
    def create_asset_dto(asset):
        asset_dto = FEAssetDTO()
        asset_dto.id = asset.id
        asset_dto.name = asset.name
        asset_dto.ticker = asset.ticker
        asset_dto.sector = asset.sector
        asset_dto.market_id = asset.market_id
        asset_dto.asset_class_id = asset.asset_class_id
        return asset_dto

    @staticmethod
    def create_asset_dtos(assets):
        asset_dtos = []
        if assets is None:
            return asset_dtos
        for asset in assets:
            asset_dto = FinEdgeServiceHelper.create_asset_dto(asset)
            asset_dtos.append(asset_dto)
        return asset_dtos

    @staticmethod
    def create_ranking_dto(ranking):
        ranking_dto = FERankingDTO()
        ranking_dto.id = ranking.id
        ranking_dto.market_date = ranking.market_date
        ranking_dto.portfolio_id = ranking.portfolio_id
        ranking_dto.valuation = ranking.valuation
        ranking_dto.progression = ranking.progression
        ranking_dto.performance = ranking.performance
        ranking_dto.sharp_ratio = ranking.sharp_ratio
        ranking_dto.ranking = ranking.ranking
        ranking_dto.historical_rankings = {}
        user = ranking.portfolio.user
        ranking_dto.user = UserServiceHelper.create_user_dto(user)
        return ranking_dto

    @staticmethod
    def create_ranking_dtos(rankings, historical_rankings, historical_yields):
        ranking_dtos = {}
        if rankings is None:
            return ranking_dtos
        for ranking in rankings:
            ranking_dto = FinEdgeServiceHelper.create_ranking_dto(ranking)
            ranking_dtos[ranking_dto.portfolio_id] = ranking_dto
        for historical_ranking in historical_rankings:
            if historical_ranking.portfolio_id not in ranking_dtos.keys():
                continue
            ranking_dto = ranking_dtos[historical_ranking.portfolio_id]
            str_market_date = historical_ranking.market_date.strftime("%Y-%m-%d")
            ranking_dto.historical_rankings[str_market_date] = {'ranking': int(historical_ranking.ranking)}
        for historical_yield in historical_yields:
            if historical_yield.portfolio_id not in ranking_dtos.keys():
                continue
            ranking_dto = ranking_dtos[historical_yield.portfolio_id]
            str_market_date = historical_yield.market_date.strftime("%Y-%m-%d")
            if str_market_date not in ranking_dto.historical_rankings.keys():
                continue #ranking_dto.historical_rankings[str_market_date] = {}
            ranking_dto.historical_rankings[str_market_date]['yield'] = float(historical_yield.value)/10000.
        return ranking_dtos.values()

    @staticmethod
    def create_market_dto(market):
        market_dto = FEMarketDTO()
        market_dto.id = market.id
        market_dto.name = market.name
        market_dto.description = market.description
        return market_dto

    @staticmethod
    def create_market_dtos(markets):
        market_dtos = []
        if markets is None:
            return market_dtos
        for market in markets:
            market_dto = FinEdgeServiceHelper.create_market_dto(market)
            market_dtos.append(market_dto)
        return market_dtos

    @staticmethod
    def create_asset_class_dto(asset_class):
        asset_class_dto = FEAssetClassDTO()
        asset_class_dto.id = asset_class.id
        asset_class_dto.name = asset_class.name
        asset_class_dto.description = asset_class.description
        return asset_class_dto

    @staticmethod
    def create_asset_class_dtos(asset_classes):
        asset_class_dtos = []
        if asset_classes is None:
            return asset_class_dtos
        for asset_class in asset_classes:
            asset_class_dto = FinEdgeServiceHelper.create_asset_class_dto(asset_class)
            asset_class_dtos.append(asset_class_dto)
        return asset_class_dtos

    @classmethod
    def create_indicator_dto(cls, indicator):
        indicator_dto = FEIndicatorDTO()
        indicator_dto.id = indicator.id
        indicator_dto.name = indicator.name
        return indicator_dto

    @classmethod
    def create_indicator_dtos(cls, indicators):
        indicator_dtos = []
        if indicators is None:
            return indicator_dtos
        for indicator in indicators:
            indicator_dto = FinEdgeServiceHelper.create_indicator_dto(indicator)
            indicator_dtos.append(indicator_dto)
        return indicator_dtos

    @staticmethod
    def create_position_dto(position):
        position_dto = FEPositionDTO()
        position_dto.id = position.id
        position_dto.asset_id = position.asset_id
        position_dto.portfolio_id = position.portfolio_id
        position_dto.market_date= position.market_date
        position_dto.quantity = position.quantity
        position_dto.price = position.price
        position_dto.variation = position.variation
        position_dto.valuation = position.valuation
        position_dto.profit_and_loss = position.profit_and_loss
        position_dto.volatility = position.volatility
        position_dto.var95 = position.var95
        position_dto.var99 = position.var99
        position_dto.weight = position.weight
        position_dto.close_price = position.close_price
        return position_dto

    @staticmethod
    def create_position_dtos(positions):
        position_dtos = []
        if positions is None:
            return position_dtos
        for position in positions:
            position_dto = FinEdgeServiceHelper.create_position_dto(position)
            position_dtos.append(position_dto)
        return position_dtos

    @staticmethod
    def create_performance_dto(performance):
        performance_dto = FEPerformanceDTO()
        performance_dto.id = performance.id
        performance_dto.asset_id = performance.asset_id
        performance_dto.market_date = performance.market_date
        performance_dto.last = performance.last
        performance_dto.open = performance.open
        performance_dto.high = performance.high
        performance_dto.low = performance.low
        performance_dto.volume = performance.volume
        performance_dto.variation = performance.variation
        performance_dto.bid = performance.bid
        performance_dto.ask = performance.ask
        return performance_dto

    @staticmethod
    def create_intradays_dtos(performances):
        performance_dtos = []
        for performance in performances:
            performance_dto = FinEdgeServiceHelper.create_performance_dto(performance)
            performance_dtos.append(performance_dto)
        return performance_dtos

    @staticmethod
    def create_performance_dtos(performances):
        performance_dtos = {}
        if performances is None:
            return []
        for performance in performances:
            performance_dto = FinEdgeServiceHelper.create_performance_dto(performance)
            performance_dtos[performance_dto.asset_id] = performance_dto
        return list(performance_dtos.values())

    @staticmethod
    def create_historical_data_dto(historical_data):
        historical_data_dto = FEHistoricalDataDTO()
        historical_data_dto.id = historical_data.id
        historical_data_dto.asset_id = historical_data.asset_id
        historical_data_dto.market_date = historical_data.market_date
        historical_data_dto.close = historical_data.close
        historical_data_dto.volume = historical_data.volume
        historical_data_dto.variation = historical_data.variation
        historical_data_dto.volatility = historical_data.volatility
        return historical_data_dto

    @classmethod
    def create_historical_data_dtos(self, historical_datas):
        historical_data_dtos = []
        if historical_datas is None:
            return historical_data_dtos
        for historical_data in historical_datas:
            historical_data_dto = FinEdgeServiceHelper.create_historical_data_dto(historical_data)
            historical_data_dtos.append(historical_data_dto)
        return historical_data_dtos

    @staticmethod
    def create_market_view_dto(market_view):
        market_view_dto = FEMarketViewDTO()
        market_view_dto.id = market_view.id
        market_view_dto.market_id = market_view.market_id
        market_view_dto.market_date = market_view.market_date
        market_view_dto.close = market_view.close
        market_view_dto.volume = market_view.volume
        market_view_dto.variation = market_view.variation
        return market_view_dto

    @classmethod
    def create_market_view_dtos(self, market_views):
        market_view_dtos = []
        if market_views is None:
            return market_view_dtos
        for market_view in market_views:
            market_view_dto = FinEdgeServiceHelper.create_market_view_dto(market_view)
            market_view_dtos.append(market_view_dto)
        return market_view_dtos

    @staticmethod
    def create_portfolio_indicator_dto(last_portfolio_indicator):
        last_portfolio_indicator_dto = FELastPortfolioIndicatorDTO()
        last_portfolio_indicator_dto.id = last_portfolio_indicator.id
        last_portfolio_indicator_dto.portfolio_id = last_portfolio_indicator.portfolio_id
        last_portfolio_indicator_dto.indicator_id = last_portfolio_indicator.indicator_id
        last_portfolio_indicator_dto.market_date = last_portfolio_indicator.market_date
        last_portfolio_indicator_dto.value = last_portfolio_indicator.value
        last_portfolio_indicator_dto.variation = last_portfolio_indicator.variation
        return last_portfolio_indicator_dto

    @staticmethod
    def create_portfolio_indicators_dtos(portfolio_indicators):
        portfolio_indicators_dtos = []
        if portfolio_indicators is None:
            return portfolio_indicators_dtos
        for portfolio_indicator in portfolio_indicators:
            last_portfolio_indicators_dto = FinEdgeServiceHelper.create_portfolio_indicator_dto(portfolio_indicator)
            portfolio_indicators_dtos.append(last_portfolio_indicators_dto)
        return portfolio_indicators_dtos

    @staticmethod
    def create_portfolio_dto(portfolio, portfolio_last_indicators):
        portfolio_dto = FEPortfolioDTO()
        portfolio_dto.id = portfolio.id
        portfolio_dto.user_id = portfolio.user_id
        portfolio_dto.game_session_id = portfolio.game_session_id
        portfolio_last_indicators_dtos = FinEdgeServiceHelper.create_portfolio_indicators_dtos(portfolio_last_indicators)
        portfolio_dto.last_indicators = portfolio_last_indicators_dtos
        return portfolio_dto

    @staticmethod
    def create_correlation_matrix_data(correlation_matrix, column_names):
        correlation_data = []
        for i in range(len(column_names)):
            for j in range(i + 1, len(column_names)):
                correlation_data.append([column_names[i], column_names[j], float(correlation_matrix[i, j])])
        return correlation_data

    @staticmethod
    def compute_benchmark_portfolio_yields_and_perfs(portfolio_yields, benchmark_yields):
        result_ptf = {}
        result_ben = {}
        date_format = "%Y-%m-%d"
        for portfolio_yield in portfolio_yields:
            market_date = portfolio_yield.market_date.strftime(date_format)
            ptf_value = portfolio_yield.value / 10000.
            result_ptf[market_date] = {'ptf_yield': portfolio_yield.variation, 'ptf_perf': ptf_value}
        bench_value = 100
        for benchmark_yield in benchmark_yields:
            market_date = benchmark_yield.market_date.strftime(date_format)
            if market_date not in result_ptf:
                continue
            bench_value *= (1 + benchmark_yield.variation)
            result_ben[market_date] = {
                'bench_yield': benchmark_yield.variation,
                'bench_perf': bench_value}
            print(f"Adding to result_ben: Date={market_date}, bench_yield={benchmark_yield.variation}, bench_perf={bench_value}")

        dates = list(set(result_ptf.keys()).intersection(set(result_ben.keys())))
        print("Dates common to result_ptf and result_ben:", dates)
        return {d: {
            'ptf_yield': result_ptf[d]['ptf_yield'],
            'ptf_perf': result_ptf[d]['ptf_perf'],
            'bench_yield': result_ben[d]['bench_yield'],
            'bench_perf': result_ben[d]['bench_perf']} for d in dates}

    @staticmethod
    def compute_portfolio_concentrations(last_positions):
        asset_class_concentration = {}
        sector_concentration = {}
        asset_concentration = {}
        if last_positions is None:
            return asset_class_concentration, sector_concentration, asset_concentration
        for position in last_positions:
            if position.weight <= helpers.FINEDGE_MIN_POSITION:
                continue
            asset = position.asset
            asset_class_id = asset.asset_class_id
            sector = asset.sector
            if asset_class_id not in asset_class_concentration:
                asset_class_concentration[asset_class_id] = 0
            if sector not in sector_concentration:
                sector_concentration[sector] = 0
            asset_class_concentration[asset_class_id] += position.weight
            sector_concentration[sector] += position.weight
            asset_concentration[asset.id] = position.weight
        return asset_class_concentration, sector_concentration, asset_concentration

    @staticmethod
    def create_fe_portfolio_indicators(participant_portfolio):
        ptf_indicators = []
        ptf_id = participant_portfolio.id
        today = datetime.today()
        for indicator_id in range(1, 11):
            fe_ptf_indicator = FEPortfolioIndicator(ptf_id, indicator_id, today, 0, 0)
            if indicator_id in [1, 2]:
                fe_ptf_indicator.value = 1000000
            ptf_indicators.append(fe_ptf_indicator)
        return ptf_indicators

    @staticmethod
    def create_market_intradays_dtos(market_performances):
        market_performance_dtos = []
        for market_performance in market_performances:
            market_performance_dto = FinEdgeServiceHelper.create_market_performance_dto(market_performance)
            market_performance_dtos.append(market_performance_dto)
        return market_performance_dtos

    @staticmethod
    def create_market_performance_dto(market_performance):
        market_performance_dto = FEMarketPerformanceDTO()
        market_performance_dto.id = market_performance.id
        market_performance_dto.market_id = market_performance.market_id
        market_performance_dto.market_date = market_performance.market_date
        market_performance_dto.last = market_performance.last
        market_performance_dto.open = market_performance.open
        market_performance_dto.high = market_performance.high
        market_performance_dto.low = market_performance.low
        market_performance_dto.volume = market_performance.volume
        market_performance_dto.variation = market_performance.variation
        market_performance_dto.bid = market_performance.bid
        market_performance_dto.ask = market_performance.ask
        return market_performance_dto