from collections import OrderedDict
from src.config.objects import TargetObject
from src.converter.convert_strategy import ConvertStrategyFactory


class CSVStringConverter:
    def __init__(self, target_object: TargetObject, sf_object_jsons: dict) -> None:
        self.__target_object = target_object
        # メモリ節約のため、ジェネレータ化
        self.__sf_object_jsons = (i for i in sf_object_jsons)
        self.__convert_strategy_factory = ConvertStrategyFactory()

    def convert(self) -> str:
        csv_data = self.__convert_to_csv(self.__sf_object_jsons)
        return csv_data

    def __extract_necessary_props_from(self, sf_object_json) -> dict:
        del sf_object_json['attributes']
        return {k.upper(): v for k, v in sf_object_json.items()}

    def __convert_to_csv(self, sf_object_jsons) -> list:
        try:
            columns = self.__target_object.columns
            csv_data = [columns]
            for i, json_object in enumerate(sf_object_jsons, 1):
                json_object = self.__extract_necessary_props_from(json_object)
                csv_row = []
                for column in columns:
                    column_name = column.upper()
                    column_value = self.__get_column_value(json_object, column_name)

                    convert_strategy = self.__convert_strategy_factory.create(column_value)
                    converted_value = convert_strategy.convert_value(column_value)

                    csv_row.append(converted_value)

                csv_data.append(csv_row)
            return csv_data

        except Exception as e:
            raise Exception(
                f'CSV変換に失敗しました カラム名：[{column}] 行番号: [{i}] エラー内容:[{e}]')

    def __get_column_value(self, json_object: dict, column_name: str) -> str:
        # 参照を辿らない通常の項目の場合、カラム名が一致するためそのまま取得
        if '.' not in column_name:
            return json_object[column_name]

        # カラム名に`.`が含まれている場合、オブジェクトの参照を辿って終端を取得する
        relationship_columns = column_name.split('.')
        return self.__get_column_value_by_relationship(json_object, relationship_columns)

    def __get_column_value_by_relationship(self, json_object: dict, relationship_columns: str, recurs: int = 0) -> str:
        # 参照関係の終端を取得しきるまで再帰的に深掘りする
        # REVIEW: 参照の終端の項目型が住所型の場合、レスポンスが辞書型になるため大抵の場合Noneになる
        relationship_name = relationship_columns[recurs]
        relationship_item = json_object.get(relationship_name)

        # 項目が取得できなかったらNoneを返す
        if relationship_item is None:
            return None

        # 参照が辿りきれていない場合、再帰的に深掘りする
        if type(relationship_item) == dict or type(relationship_item) == OrderedDict:
            # 取り回しを良くするために、辞書のキーをアッパーケースにしておく
            relationship_item_upper = {k.upper(): v for k, v in relationship_item.items()}
            return self.__get_column_value_by_relationship(relationship_item_upper, relationship_columns, recurs + 1)

        # 終端のデータを取得
        return relationship_item
