from src.aws.s3 import ConfigBucket, VjskReceiveBucket
from src.batch.common.batch_context import BatchContext
from src.batch.common.calendar_file import CalendarFile
from src.batch.vjsk.vjsk_data_load_manager import VjskDataLoadManager
from src.batch.vjsk.vjsk_recv_file_mapper import VjskReceiveFileMapper
from src.error.exceptions import BatchOperationException
from src.logging.get_logger import get_logger

logger = get_logger('V実消化データ取込')
batch_context = BatchContext.get_instance()
vjsk_recv_bucket = VjskReceiveBucket()
vjsk_mapper = VjskReceiveFileMapper()


def exec():
    """V実消化データ取込処理"""
    logger.debug('exec start')

    # 非営業日なら何もせず終了
    if batch_context.is_not_business_day:
        logger.debug('非営業日なので処理をスキップ')
        return

    # 卸在庫データ取込対象日であれば、卸在庫データ処理対象フラグを立てる
    logger.debug('卸在庫データ取込対象日であるかを判定')
    batch_context.is_vjsk_stock_import_day = _determine_today_is_stockslipdata_target()
    logger.debug(f'判定結果 : {batch_context.is_vjsk_stock_import_day}')
    if batch_context.is_vjsk_stock_import_day:
        logger.info('卸在庫データ取込対象日です')

    # V実消化データファイル受領チェック
    logger.debug('V実消化データファイル受領チェック：開始')
    received_s3_files = []
    try:
        # S3バケットにある受領済のV実消化データファイルの存在チェックをする
        received_s3_files = _check_received_files()

    except BatchOperationException as e:
        logger.debug('受領したV実消化データファイルに未受領もものがあります')
        raise e
    logger.debug('V実消化データファイル受領チェック：終了')

    # データベース取込
    logger.debug('V実消化データ取込：開始')
    try:
        # S3バケットにある受領済のV実消化データファイルをデータベースに登録する
        _import_file_to_db(received_s3_files)
    except Exception as e:
        logger.debug(f'データベース登録失敗 {e}')
        raise e

    # V実消化データ受領ファイルバックアップ退避
    logger.debug('V実消化データ受領ファイルバックアップ退避：開始')
    try:
        # 取込が完了したS3バケットにある受領ファイルをバックアップ用S3バケットに移動する
        vjsk_recv_bucket.backup_dat_file(received_s3_files, batch_context.syor_date)

    except BatchOperationException as e:
        logger.debug('V実消化データ受領ファイルのバックアップ退避が失敗しました')
        raise e
    logger.debug('V実消化データ受領ファイルバックアップ退避：終了')

    logger.debug('exec done')


def _check_if_file_exists(src_list: list, condkey: str) -> bool:
    logger.debug(f"_check_if_file_exists start (src_list : {src_list} , condkey : {condkey})")
    # ファイル接頭辞と拡張子が一致するかで判定する
    ret = False
    pref = vjsk_mapper.get_file_prefix(condkey)
    suff = vjsk_mapper.get_file_suffix(condkey)

    for idx, elem in enumerate(src_list):
        buf = elem.get("filename")
        filename = buf[buf.rfind("/") + 1:]
        if filename.startswith(pref) and filename.endswith(suff):
            ret = True
            break

    logger.debug(f"_check_if_file_exists done (return : {ret})")
    return ret


def _check_received_files() -> list:
    """V実消化連携データファイル受領確認処理"""
    logger.debug('_check_received_files start')

    # S3バケット「実消化&アルトマーク V実消化データ受領バケット」にある受領ファイル一覧を取得
    received_s3_files = vjsk_recv_bucket.get_s3_file_list()
    logger.debug(f'ファイル一覧{received_s3_files}')

    # ファイル存在確認 卸在庫データファイル(卸在庫データ処理対象日のみ実施)
    if batch_context.is_vjsk_stock_import_day:
        if not _check_if_file_exists(received_s3_files, vjsk_mapper.CONDKEY_STOCK_SLIP_DATA):
            raise BatchOperationException(f'卸在庫データファイルがありません ファイル一覧:{received_s3_files}')

    # ファイル存在確認 卸販売データ
    if not _check_if_file_exists(received_s3_files, vjsk_mapper.CONDKEY_SLIP_DATA):
        raise BatchOperationException(f'卸販売データファイルがありません ファイル一覧:{received_s3_files}')

    # ファイル存在確認 卸組織変換マスタ
    if not _check_if_file_exists(received_s3_files, vjsk_mapper.CONDKEY_ORG_CNV_MST):
        raise BatchOperationException(f'卸組織変換マスタファイルがありません ファイル一覧:{received_s3_files}')

    # ファイル存在確認 施設統合マスタ
    if not _check_if_file_exists(received_s3_files, vjsk_mapper.CONDKEY_VOP_HCO_MERGE):
        raise BatchOperationException(f'施設統合マスタファイルがありません ファイル一覧:{received_s3_files}')

    # ファイル存在確認 卸マスタ
    if not _check_if_file_exists(received_s3_files, vjsk_mapper.CONDKEY_WHS_MST):
        raise BatchOperationException(f'卸マスタファイルがありません ファイル一覧:{received_s3_files}')

    # ファイル存在確認 卸ホールディングスマスタ
    if not _check_if_file_exists(received_s3_files, vjsk_mapper.CONDKEY_HLD_MST):
        raise BatchOperationException(f'卸ホールディングスマスタファイルがありません ファイル一覧:{received_s3_files}')

    # ファイル存在確認 施設マスタ
    if not _check_if_file_exists(received_s3_files, vjsk_mapper.CONDKEY_FCL_MST):
        raise BatchOperationException(f'施設マスタファイルがありません ファイル一覧:{received_s3_files}')

    # ファイル存在確認 メーカー卸組織展開表
    if not _check_if_file_exists(received_s3_files, vjsk_mapper.CONDKEY_MKR_ORG_HORIZON):
        raise BatchOperationException(f'メーカー卸組織展開表ファイルがありません ファイル一覧:{received_s3_files}')

    # ファイル存在確認 取引区分マスタ
    if not _check_if_file_exists(received_s3_files, vjsk_mapper.CONDKEY_TRAN_KBN_MST):
        raise BatchOperationException(f'取引区分マスタファイルがありません ファイル一覧:{received_s3_files}')

    # ファイル存在確認 製品マスタ
    if not _check_if_file_exists(received_s3_files, vjsk_mapper.CONDKEY_PHM_PRD_MST):
        raise BatchOperationException(f'製品マスタファイルがありません ファイル一覧:{received_s3_files}')

    # ファイル存在確認 製品価格マスタ
    if not _check_if_file_exists(received_s3_files, vjsk_mapper.CONDKEY_PHM_PRICE_MST):
        raise BatchOperationException(f'製品価格マスタファイルがありません ファイル一覧:{received_s3_files}')

    # ファイル存在確認 卸得意先情報マスタ
    if not _check_if_file_exists(received_s3_files, vjsk_mapper.CONDKEY_WHS_CUSTOMER_MST):
        raise BatchOperationException(f'卸得意先情報マスタファイルがありません ファイル一覧:{received_s3_files}')

    # ファイル存在確認 MDBコード変換マスタ
    if not _check_if_file_exists(received_s3_files, vjsk_mapper.CONDKEY_MDB_CONV_MST):
        raise BatchOperationException(f'MDBコード変換マスタファイルがありません ファイル一覧:{received_s3_files}')

    # ファイル存在確認 生物由来データ
    if not _check_if_file_exists(received_s3_files, vjsk_mapper.CONDKEY_BIO_SLIP_DATA):
        raise BatchOperationException(f'生物由来データファイルがありません ファイル一覧:{received_s3_files}')

    # ファイル存在確認 製造ロット番号マスタ
    if not _check_if_file_exists(received_s3_files, vjsk_mapper.CONDKEY_LOT_NUM_MST):
        raise BatchOperationException(f'製造ロット番号マスタファイルがありません ファイル一覧:{received_s3_files}')

    # 想定外ファイルの受領確認 (想定ファイル数、卸在庫データ取込対象日の場合は15、そうでない場合は14)
    naturally_count = 15 if batch_context.is_vjsk_stock_import_day else 14
    if len(received_s3_files) > naturally_count:
        raise BatchOperationException(f'想定数を超える受領ファイルがあります ファイル一覧:{received_s3_files}')

    logger.debug('_check_received_files done')

    return received_s3_files


def _import_file_to_db(received_s3_files: list):
    """V実消化連携データ取込処理"""
    logger.debug('_import_file_to_db start')

    # S3バケット「実消化&アルトマーク V実消化データ受領バケット」の受領ファイルをローカルストレージにdownloadして辞書化する
    target_dict = {}
    for s3_file_path in received_s3_files:
        file_name = s3_file_path.get('filename')

        # S3バケットにある受領ファイルをローカルストレージにdownloadする
        logger.debug(f"download s3 file start : {file_name}")
        local_file_path = vjsk_recv_bucket.download_data_file(file_name)
        logger.debug(f"download s3 file done : {file_name}")

        # ローカルストレージにdownloadした受領ファイル(tar.gz)を解凍する
        unzip_file_path = vjsk_recv_bucket.unzip_data_file(local_file_path)
        logger.debug(f"unzip done : {unzip_file_path}")

        # データファイル名に該当する辞書アクセス用のキーを取得する
        key = vjsk_mapper.get_condkey_by_s3_file_path(file_name)

        # 想定されたデータファイルであれば辞書登録する
        if key is not None:
            # ※受領ファイル(tar.gz)の書庫構成はtsvファイルが1つだけの前提
            target_dict[key] = {"condkey": key, "src_file_path": unzip_file_path[0]}
    logger.debug(f'取込対象データファイル辞書{target_dict}')

    # DB登録　卸在庫データファイル(卸在庫データ処理対象日のみ実施)
    if batch_context.is_vjsk_stock_import_day:
        VjskDataLoadManager.load(target_dict[vjsk_mapper.CONDKEY_STOCK_SLIP_DATA])

    # DB登録 卸販売データ
    VjskDataLoadManager.load(target_dict[vjsk_mapper.CONDKEY_SLIP_DATA])

    # DB登録 卸組織変換マスタ
    VjskDataLoadManager.load(target_dict[vjsk_mapper.CONDKEY_ORG_CNV_MST])

    # DB登録 施設統合マスタ
    VjskDataLoadManager.load(target_dict[vjsk_mapper.CONDKEY_VOP_HCO_MERGE])

    # DB登録 卸マスタ
    VjskDataLoadManager.load(target_dict[vjsk_mapper.CONDKEY_WHS_MST])

    # DB登録 卸ホールディングスマスタ
    VjskDataLoadManager.load(target_dict[vjsk_mapper.CONDKEY_HLD_MST])

    # DB登録 施設マスタ
    VjskDataLoadManager.load(target_dict[vjsk_mapper.CONDKEY_FCL_MST])

    # DB登録 メーカー卸組織展開表
    VjskDataLoadManager.load(target_dict[vjsk_mapper.CONDKEY_MKR_ORG_HORIZON])

    # DB登録 取引区分マスタ
    VjskDataLoadManager.load(target_dict[vjsk_mapper.CONDKEY_TRAN_KBN_MST])

    # DB登録 製品マスタ
    VjskDataLoadManager.load(target_dict[vjsk_mapper.CONDKEY_PHM_PRD_MST])

    # DB登録 製品価格マスタ
    VjskDataLoadManager.load(target_dict[vjsk_mapper.CONDKEY_PHM_PRICE_MST])

    # DB登録 卸得意先情報マスタ
    VjskDataLoadManager.load(target_dict[vjsk_mapper.CONDKEY_WHS_CUSTOMER_MST])

    # DB登録 MDBコード変換マスタ
    VjskDataLoadManager.load(target_dict[vjsk_mapper.CONDKEY_MDB_CONV_MST])

    # DB登録 生物由来データ
    VjskDataLoadManager.load(target_dict[vjsk_mapper.CONDKEY_BIO_SLIP_DATA])

    # DB登録 製造ロット番号マスタ
    VjskDataLoadManager.load(target_dict[vjsk_mapper.CONDKEY_LOT_NUM_MST])

    logger.debug('_import_file_to_db done')


def _determine_today_is_stockslipdata_target():
    """設定ファイル「V実消化卸在庫データ連携日ファイル」の内容を取得して、処理日付が該当していればTrueを返却する"""
    logger.debug("_determine_today_is_stockslipdata_target start")
    try:
        # 処理日付を取得する
        today = batch_context.syor_date

        # S3バケット上の設定ファイル「V実消化卸在庫データ連携日ファイル」をローカルストレージにdownloadする
        wholesaler_stock_list_file_path = ConfigBucket().download_wholesaler_stock_input_day_list()

        # 設定ファイル「V実消化卸在庫データ連携日ファイル」の定義内容を取得する
        target_days = CalendarFile(wholesaler_stock_list_file_path)

        # 処理日付が、設定ファイル「V実消化卸在庫データ連携日ファイル」の定義に該当するかを判定する
        ret = target_days.compare_date(today)
    except Exception as e:
        logger.error(f'{e}')
        raise e
    logger.debug("_determine_today_is_stockslipdata_target done")
    return ret
