# from src.batch.vjsk.vjsk_recv_file_manager import VjskDatFile
from src.batch.vjsk.vjsk_recv_file_mapper import VjskReceiveFileMapper
from src.db.database import Database
from src.error.exceptions import BatchOperationException
from src.logging.get_logger import get_logger

logger = get_logger('V実消化データ取込(DB登録)')
mapper = VjskReceiveFileMapper()


class VjskDataLoadManager:
    def __init__(self):
        pass

    def _import_to_db(src_file_name: str, condkey: str):
        logger.debug(f"_import_to_db start (src_file_name : {src_file_name}, condkey : {condkey})")

        db = Database.get_instance()
        data_name = mapper.get_data_name(condkey)
        table_name_org = mapper.get_org_table(condkey)
        table_name_src = mapper.get_src_table(condkey)
        upsert_sql = mapper.get_upsert_sql(condkey)

        try:
            db.connect()
            db.execute("SET SESSION sql_mode = 'TRADITIONAL';")

            # orgをtruncate
            db.execute(f"TRUNCATE TABLE {table_name_org};")

            # orgにload　※warningが発生すれば異常終了させる
            sql = f"""\
                LOAD DATA LOCAL INFILE :src_file_name
                INTO TABLE {table_name_org}
                FIELDS TERMINATED BY '\\t'
                ENCLOSED BY '\"'
                IGNORE 1 LINES;
                """
            db.begin()
            result = db.execute(sql, {"src_file_name": src_file_name})
            logger.info(f'{data_name}tsvファイルを{table_name_org}にLOAD : 件数({result.rowcount})')
            db.commit()

            # org→srcにinsert select
            db.begin()
            logger.debug(upsert_sql)
            db.execute(upsert_sql)
            # MEMO: insert+selectの結果件数は、LOAD結果と必ず等しいので、executeの結果件数はログ出力しない
            # MEMO: insert+select 実質10件なのに、result.rowcountは20件になってしまう　※sqlalchemyの仕様
            # MEMO: https://docs.sqlalchemy.org/en/14/core/connections.html#sqlalchemy.engine.BaseCursorResult.rowcount
            logger.info(f'{table_name_org}を{table_name_src}にUPSERT')

            db.commit()
        except Exception as e:
            db.rollback()
            raise BatchOperationException(e)
        finally:
            db.disconnect()

        logger.debug("_import_to_db done")
        return

    def _get_tsv_last_row_tab_count(src_file_name: str) -> int:
        # memo: tsvファイルが数百MBに及ぶことを想定して、末尾から1行分を参照する
        # memo: 前提1 行区切りは LF('\n')
        buf_count = 0

        # バイナリモードでファイルオープン
        with open(src_file_name, 'rb') as file:
            # ファイルの末尾から2バイト手前に移動
            file.seek(-2, 2)
            # 改行文字を見つけるまで逆方向に読み進める
            while file.read(1) != b'\n':
                # 1バイト戻って再度読み込み
                file.seek(-2, 1)
            # 末尾行を抽出
            last_line = file.readline().decode().rstrip('\n')
            # 末尾行に含まれるタブ文字の数を抽出
            buf_count = last_line.count('\t')

        return buf_count

    @classmethod
    def load(self, target: dict):
        logger.debug(f'load start target:{target}')

        # S3からローカルストレージにdownloadした登録対象のtsvファイルパスを取得
        local_file_name = target["src_file_path"]

        # tsvファイル末尾行のTABの数が総定数と一致しない場合は例外をスロー
        # TODO: ↓↓↓developへのマージを優先させたいので、未テストのロジックはコメントアウトする
        # tsv_tabs = self._get_tsv_last_row_tab_count(local_file_name)
        # expect_tabs = mapper.get_file_column_separators(target["condkey"])
        # if tsv_tabs != expect_tabs:
        #     msg = f"受領tsvファイルの末尾行のTABの数が総定数と一致しませんでした local_file_name: {local_file_name}"
        #     raise BatchOperationException(msg)
        # TODO: ↑↑↑developへのマージを優先させたいので、未テストのロジックはコメントアウトする

        # データベース登録
        self._import_to_db(local_file_name, target["condkey"])

        logger.debug('load done')
        return
