import os.path as path
from datetime import datetime

import pytest

from src.batch.common.batch_context import BatchContext
from src.batch.ultmarc.utmp_tables.table_mapper.concrete import com_inst_mapper
from src.db.database import Database
from tests.testing_utility import (assert_table_results,
                                   create_db_data_from_csv,
                                   create_delete_sql_with_parameter,
                                   create_insert_sql_with_parameter,
                                   create_ultmarc_table_mapper_sut,
                                   create_ultmarc_test_data_from_csv)


class TestComInstMapper:
    """レイアウト区分101: COM_施設"""

    db: Database
    batch_context: BatchContext
    test_file_path: str = path.dirname(__file__)

    smallint_columns = [
        'bed_num',
        'prmit_bed_num_other',
        'prmit_bed_num_mental',
        'prmit_bed_num_tuber',
        'prmit_bed_num_infection',
        'prmit_bed_num_sum',
        'prmit_bed_num_gen',
        'prmit_bed_num_rcup'
    ]

    @pytest.fixture(autouse=True, scope='function')
    def pre_test(self, database: Database):
        """テスト実行前後処理"""
        self.batch_context = BatchContext.get_instance()
        # setup
        self.db = database
        self.db.connect()
        self.db.begin()

        # testing
        yield

        # teardown
        self.db.rollback()
        self.db.disconnect()

    def test_insert_record(self):
        """
        Cases:
            COM_施設テーブルにレコードを登録する
        Arranges:
            - CSVデータを用意し、読み込む
            - 追加対象となるレコードを削除する
        Expects:
            - 登録内容が期待値と一致すること
        """

        # Arrange
        # 処理日設定
        self.batch_context.syor_date = datetime.strftime(datetime.now(), '%Y/%m/%d')
        # テスト用のCSVを読み込む
        test_dat_file = create_ultmarc_test_data_from_csv(path.join(self.test_file_path, 'com_inst_insert.csv'))
        # 一旦全データをDBから削除
        delete_sql, delete_parameter = create_delete_sql_with_parameter('src05.com_inst', {'1': '1'})
        self.db.execute(delete_sql, delete_parameter)
        delete_sql, delete_parameter = create_delete_sql_with_parameter('src05.com_inst_trt_course', {'1': '1'})
        self.db.execute(delete_sql, delete_parameter)
        delete_sql, delete_parameter = create_delete_sql_with_parameter('src05.com_spcare_med_office_dat', {'1': '1'})
        self.db.execute(delete_sql, delete_parameter)

        # Act
        for line_number, line in enumerate(test_dat_file, start=1):
            sut: com_inst_mapper.ComInstMapper = create_ultmarc_table_mapper_sut(line, self.db)
            assert type(sut) is com_inst_mapper.ComInstMapper, f'{line_number}行目:マッパークラスが期通りか'

            sut.make_query()
            sut.execute_queries()

        # Assert
        # 期待値ファイルを読み込む(施設)
        expect_data_list = create_db_data_from_csv(path.join(self.test_file_path, 'expect_com_inst_insert.csv'))
        primary_keys = [f"'{primary_key['dcf_dsf_inst_cd']}'" for primary_key in expect_data_list]
        actual_select_sql = f"SELECT * FROM src05.com_inst WHERE dcf_dsf_inst_cd IN ({','.join(primary_keys)})"
        actual_data_list = self.db.execute_select(actual_select_sql)
        # 期待値検査
        ignore_columns = ['regist_ymd', 'sys_update_date', 'sys_regist_date'] + self.smallint_columns
        assert_table_results(actual_data_list, expect_data_list, ignore_col_name=ignore_columns)
        # 動的日付項目の個別確認
        line_number = 0
        for actual_row, expect_row in zip(actual_data_list, expect_data_list):
            line_number += 1
            for actual_col_name, expect_col_name in zip(actual_row, expect_row):
                if actual_col_name in ignore_columns:
                    if actual_col_name in self.smallint_columns:
                        if expect_row[expect_col_name] is not None and len(expect_row[expect_col_name]) > 0:
                            assert actual_row[actual_col_name] == int(expect_row[expect_col_name]), f'{line_number}行目:{actual_col_name}が、期待値と一致すること'
                    else:
                        assert actual_row[actual_col_name] >= expect_row[expect_col_name], f'{line_number}行目:{actual_col_name}が、期待値以降であること'

        # 期待値ファイルを読み込む(施設診療科目)
        expect_data_list = create_db_data_from_csv(path.join(self.test_file_path, 'expect_com_trt_insert.csv'))
        actual_record_count = self.db.execute_select('SELECT COUNT(*) AS count_num FROM src05.com_inst_trt_course')[0]['count_num']
        assert actual_record_count == len(expect_data_list), 'DBのレコード件数が期待値の件数と一致すること'
        primary_keys_dcf_dsf_inst_cd = [{'dcf_dsf_inst_cd': columns['dcf_dsf_inst_cd']} for columns in expect_data_list]
        primary_keys_trt_course_cd = [{'trt_course_cd': columns['trt_course_cd']} for columns in expect_data_list]
        actual_data_list = []
        sp_field_select_sql = """\
        SELECT * FROM src05.com_inst_trt_course
        WHERE
                dcf_dsf_inst_cd = :dcf_dsf_inst_cd
            AND trt_course_cd = :trt_course_cd\
        """
        for primary_keys_dcf_dsf_inst_cd, primary_keys_trt_course_cd in zip(primary_keys_dcf_dsf_inst_cd, primary_keys_trt_course_cd):
            sp_trt_data = self.db.execute_select(
                sp_field_select_sql,
                {**primary_keys_dcf_dsf_inst_cd, **primary_keys_trt_course_cd})
            assert len(sp_trt_data) == 1, '1件取得できていること'
            actual_data_list.append(sp_trt_data[0])

        # 期待値検査
        ignore_columns = ['regist_ymd', 'sys_update_date', 'sys_regist_date']
        assert_table_results(actual_data_list, expect_data_list, ignore_col_name=ignore_columns)
        # 動的日付項目の個別確認
        line_number = 0
        for actual_row, expect_row in zip(actual_data_list, expect_data_list):
            line_number += 1
            for actual_col_name, expect_col_name in zip(actual_row, expect_row):
                if actual_col_name in ignore_columns:
                    assert actual_row[actual_col_name] >= expect_row[expect_col_name], f'{line_number}行目:{actual_col_name}が、期待値以降であること'

        # 期待値ファイルを読み込む(特養医務室データ)
        expect_data_list = create_db_data_from_csv(path.join(self.test_file_path, 'expect_com_spcare_insert.csv'))
        actual_record_count = self.db.execute_select('SELECT COUNT(*) AS count_num FROM src05.com_spcare_med_office_dat')[0]['count_num']
        assert actual_record_count == len(expect_data_list), 'DBのレコード件数が期待値の件数と一致すること'
        primary_keys = [f"'{primary_key['dcf_chld_inst_cd']}'" for primary_key in expect_data_list]
        actual_select_sql = f"SELECT * FROM src05.com_spcare_med_office_dat WHERE dcf_chld_inst_cd IN ({','.join(primary_keys)})"
        actual_data_list = self.db.execute_select(actual_select_sql)
        # 期待値検査
        ignore_columns = ['regist_ymd', 'sys_update_date', 'sys_regist_date']
        assert_table_results(actual_data_list, expect_data_list, ignore_col_name=ignore_columns)
        # 動的日付項目の個別確認
        line_number = 0
        for actual_row, expect_row in zip(actual_data_list, expect_data_list):
            line_number += 1
            for actual_col_name, expect_col_name in zip(actual_row, expect_row):
                if actual_col_name in ignore_columns:
                    assert actual_row[actual_col_name] >= expect_row[expect_col_name], f'{line_number}行目:{actual_col_name}が、期待値以降であること'

    def test_update_record(self):
        """
        Cases:
            COM_施設テーブルのレコードを更新する
        Arranges:
            - CSVデータを用意し、読み込む
            - 更新対象となるレコードを登録する
        Expects:
            - 登録内容が期待値と一致すること
        """

        # Arrange
        # 処理日設定
        self.batch_context.syor_date = datetime.strftime(datetime.now(), '%Y/%m/%d')
        # テスト用のCSVを読み込む
        test_dat_file = create_ultmarc_test_data_from_csv(path.join(self.test_file_path, 'com_inst_update.csv'))
        # 一旦全データをDBから削除
        delete_sql, delete_parameter = create_delete_sql_with_parameter('src05.com_inst', {'1': '1'})
        self.db.execute(delete_sql, delete_parameter)
        delete_sql, delete_parameter = create_delete_sql_with_parameter('src05.com_inst_trt_course', {'1': '1'})
        self.db.execute(delete_sql, delete_parameter)
        delete_sql, delete_parameter = create_delete_sql_with_parameter('src05.com_spcare_med_office_dat', {'1': '1'})
        self.db.execute(delete_sql, delete_parameter)

        # テストデータをDBに登録
        # DBデータを読み込む(施設)
        test_sql_data_list = create_db_data_from_csv(path.join(self.test_file_path, 'db_com_inst_before_update.csv'))
        for test_data in test_sql_data_list:
            insert_sql, insert_parameter = create_insert_sql_with_parameter(
                'src05.com_inst',
                test_data.keys(),
                test_data.values()
            )
            self.db.execute(insert_sql, insert_parameter)
        # DBデータを読み込む(施設診療科目)
        test_sql_data_list = create_db_data_from_csv(path.join(self.test_file_path, 'db_com_trt_before_update.csv'))
        for test_data in test_sql_data_list:
            insert_sql, insert_parameter = create_insert_sql_with_parameter(
                'src05.com_inst_trt_course',
                test_data.keys(),
                test_data.values()
            )
            self.db.execute(insert_sql, insert_parameter)
        # DBデータを読み込む(特養医務室)
        test_sql_data_list = create_db_data_from_csv(path.join(self.test_file_path, 'db_com_spcare_before_update.csv'))
        for test_data in test_sql_data_list:
            insert_sql, insert_parameter = create_insert_sql_with_parameter(
                'src05.com_spcare_med_office_dat',
                test_data.keys(),
                test_data.values()
            )
            self.db.execute(insert_sql, insert_parameter)

        # Act
        for line_number, line in enumerate(test_dat_file, start=1):
            sut: com_inst_mapper.ComInstMapper = create_ultmarc_table_mapper_sut(line, self.db)
            assert type(sut) is com_inst_mapper.ComInstMapper, f'{line_number}行目:マッパークラスが期通りか'

            sut.make_query()
            sut.execute_queries()

        # Assert
        # 期待値ファイルを読み込む(施設)
        expect_data_list = create_db_data_from_csv(path.join(self.test_file_path, 'expect_com_inst_update.csv'))
        primary_keys = [f"'{primary_key['dcf_dsf_inst_cd']}'" for primary_key in expect_data_list]
        actual_select_sql = f"SELECT * FROM src05.com_inst WHERE dcf_dsf_inst_cd IN ({','.join(primary_keys)})"
        actual_data_list = self.db.execute_select(actual_select_sql)
        # 期待値検査
        # 期待値検査
        ignore_columns = ['regist_ymd', 'sys_update_date', 'sys_regist_date'] + self.smallint_columns
        assert_table_results(actual_data_list, expect_data_list, ignore_col_name=ignore_columns)
        # 動的日付項目の個別確認
        line_number = 0
        for actual_row, expect_row in zip(actual_data_list, expect_data_list):
            line_number += 1
            for actual_col_name, expect_col_name in zip(actual_row, expect_row):
                if actual_col_name in ignore_columns:
                    if expect_row[expect_col_name] is None:
                        assert actual_row[actual_col_name] is None, f'{line_number}行目:{actual_col_name}が、登録されていないこと'
                    else:
                        if actual_col_name in self.smallint_columns:
                            if expect_row[expect_col_name] is not None and len(expect_row[expect_col_name]) > 0:
                                assert actual_row[actual_col_name] == int(expect_row[expect_col_name]), f'{line_number}行目:{actual_col_name}が、期待値と一致すること'
                        else:
                            assert actual_row[actual_col_name] >= expect_row[expect_col_name], f'{line_number}行目:{actual_col_name}が、期待値以降であること'

        # 期待値ファイルを読み込む(施設診療科目)
        expect_data_list = create_db_data_from_csv(path.join(self.test_file_path, 'expect_com_trt_update.csv'))
        actual_record_count = self.db.execute_select('SELECT COUNT(*) AS count_num FROM src05.com_inst_trt_course')[0]['count_num']
        assert actual_record_count == len(expect_data_list), 'DBのレコード件数が期待値の件数と一致すること'
        primary_keys_dcf_dsf_inst_cd = [{'dcf_dsf_inst_cd': columns['dcf_dsf_inst_cd']} for columns in expect_data_list]
        primary_keys_trt_course_cd = [{'trt_course_cd': columns['trt_course_cd']} for columns in expect_data_list]
        actual_data_list = []
        sp_field_select_sql = """\
        SELECT * FROM src05.com_inst_trt_course
        WHERE
                dcf_dsf_inst_cd = :dcf_dsf_inst_cd
            AND trt_course_cd = :trt_course_cd\
        """
        for primary_keys_dcf_dsf_inst_cd, primary_keys_trt_course_cd in zip(primary_keys_dcf_dsf_inst_cd, primary_keys_trt_course_cd):
            sp_trt_data = self.db.execute_select(
                sp_field_select_sql,
                {**primary_keys_dcf_dsf_inst_cd, **primary_keys_trt_course_cd})
            assert len(sp_trt_data) == 1, '1件取得できていること'
            actual_data_list.append(sp_trt_data[0])

        # 期待値検査
        ignore_columns = ['regist_ymd', 'update_ymd', 'sys_update_date', 'sys_regist_date']
        assert_table_results(actual_data_list, expect_data_list, ignore_col_name=ignore_columns)
        # 動的日付項目の個別確認
        line_number = 0
        for actual_row, expect_row in zip(actual_data_list, expect_data_list):
            line_number += 1
            for actual_col_name, expect_col_name in zip(actual_row, expect_row):
                if actual_col_name in ignore_columns:
                    if expect_row[expect_col_name] is None:
                        assert actual_row[actual_col_name] is None, f'{line_number}行目:{actual_col_name}が、登録されていないこと'
                    else:
                        assert actual_row[actual_col_name] >= expect_row[expect_col_name], f'{line_number}行目:{actual_col_name}が、期待値以降であること'

        # 期待値ファイルを読み込む(特養医務室データ)
        expect_data_list = create_db_data_from_csv(path.join(self.test_file_path, 'expect_com_spcare_update.csv'))
        primary_keys = [f"'{primary_key['dcf_chld_inst_cd']}'" for primary_key in expect_data_list]
        actual_select_sql = f"SELECT * FROM src05.com_spcare_med_office_dat WHERE dcf_chld_inst_cd IN ({','.join(primary_keys)})"
        actual_data_list = self.db.execute_select(actual_select_sql)

        # 期待値検査
        ignore_columns = ['regist_ymd', 'update_ymd', 'delete_ymd', 'sys_update_date', 'sys_regist_date']
        assert_table_results(actual_data_list, expect_data_list, ignore_col_name=ignore_columns)
        # 動的日付項目の個別確認
        line_number = 0
        for actual_row, expect_row in zip(actual_data_list, expect_data_list):
            line_number += 1
            for actual_col_name, expect_col_name in zip(actual_row, expect_row):
                if actual_col_name in ignore_columns:
                    if expect_row[expect_col_name] is None:
                        assert actual_row[actual_col_name] is None, f'{line_number}行目:{actual_col_name}が、登録されていないこと'
                    else:
                        assert actual_row[actual_col_name] >= expect_row[expect_col_name], f'{line_number}行目:{actual_col_name}が、期待値以降であること'

    def test_logical_delete(self):
        """
        Cases:
            COM_施設テーブルのレコードを1件論理削除する
        Arranges:
            - CSVデータを用意し、読み込む
            - 削除対象となるレコードを登録する
        Expects:
            - 登録内容が期待値と一致すること
        """

        # Arrange
        # 処理日設定
        self.batch_context.syor_date = datetime.strftime(datetime.now(), '%Y/%m/%d')
        # テスト用のCSVを読み込む
        test_dat_file = create_ultmarc_test_data_from_csv(path.join(self.test_file_path, 'com_inst_delete.csv'))
        # 一旦全データをDBから削除
        delete_sql, delete_parameter = create_delete_sql_with_parameter('src05.com_inst', {'1': '1'})
        self.db.execute(delete_sql, delete_parameter)
        delete_sql, delete_parameter = create_delete_sql_with_parameter('src05.com_inst_trt_course', {'1': '1'})
        self.db.execute(delete_sql, delete_parameter)
        delete_sql, delete_parameter = create_delete_sql_with_parameter('src05.com_spcare_med_office_dat', {'1': '1'})
        self.db.execute(delete_sql, delete_parameter)
        # テストデータをDBに登録
        # DBデータを読み込む(施設)
        test_sql_data_list = create_db_data_from_csv(path.join(self.test_file_path, 'db_com_inst_before_delete.csv'))
        for test_data in test_sql_data_list:
            insert_sql, insert_parameter = create_insert_sql_with_parameter(
                'src05.com_inst',
                test_data.keys(),
                test_data.values()
            )
            self.db.execute(insert_sql, insert_parameter)
        # DBデータを読み込む(施設診療科目)
        test_sql_data_list = create_db_data_from_csv(path.join(self.test_file_path, 'db_com_trt_before_delete.csv'))
        for test_data in test_sql_data_list:
            insert_sql, insert_parameter = create_insert_sql_with_parameter(
                'src05.com_inst_trt_course',
                test_data.keys(),
                test_data.values()
            )
            self.db.execute(insert_sql, insert_parameter)
        # DBデータを読み込む(特養医務室)
        test_sql_data_list = create_db_data_from_csv(path.join(self.test_file_path, 'db_com_spcare_before_delete.csv'))
        for test_data in test_sql_data_list:
            insert_sql, insert_parameter = create_insert_sql_with_parameter(
                'src05.com_spcare_med_office_dat',
                test_data.keys(),
                test_data.values()
            )
            self.db.execute(insert_sql, insert_parameter)

        # Act
        for line_number, line in enumerate(test_dat_file, start=1):
            sut: com_inst_mapper.ComInstMapper = create_ultmarc_table_mapper_sut(line, self.db)
            assert type(sut) is com_inst_mapper.ComInstMapper, f'{line_number}行目:マッパークラスが期通りか'
            sut.make_query()
            sut.execute_queries()

        # Assert
        # 期待値ファイルを読み込む(施設)
        expect_data_list = create_db_data_from_csv(path.join(self.test_file_path, 'expect_com_inst_delete.csv'))
        primary_keys = [f"'{primary_key['dcf_dsf_inst_cd']}'" for primary_key in expect_data_list]
        actual_select_sql = f"SELECT * FROM src05.com_inst WHERE dcf_dsf_inst_cd IN ({','.join(primary_keys)})"
        actual_data_list = self.db.execute_select(actual_select_sql)
        # 期待値検査
        ignore_columns = ['regist_ymd', 'sys_update_date', 'sys_regist_date'] + self.smallint_columns
        assert_table_results(actual_data_list, expect_data_list, ignore_col_name=ignore_columns)
        # 動的日付項目の個別確認
        line_number = 0
        for actual_row, expect_row in zip(actual_data_list, expect_data_list):
            line_number += 1
            for actual_col_name, expect_col_name in zip(actual_row, expect_row):
                if actual_col_name in ignore_columns:
                    if expect_row[expect_col_name] is None:
                        assert actual_row[actual_col_name] is None, f'{line_number}行目:{actual_col_name}が、登録されていないこと'
                    else:
                        if actual_col_name in self.smallint_columns:
                            if expect_row[expect_col_name] is not None and len(expect_row[expect_col_name]) > 0:
                                assert actual_row[actual_col_name] == int(expect_row[expect_col_name]), f'{line_number}行目:{actual_col_name}が、期待値と一致すること'
                        else:
                            assert actual_row[actual_col_name] >= expect_row[expect_col_name], f'{line_number}行目:{actual_col_name}が、期待値以降であること'

        # 期待値ファイルを読み込む(施設診療科目)
        expect_data_list = create_db_data_from_csv(path.join(self.test_file_path, 'expect_com_trt_delete.csv'))
        primary_keys_dcf_dsf_inst_cd = [{'dcf_dsf_inst_cd': columns['dcf_dsf_inst_cd']} for columns in expect_data_list]
        primary_keys_trt_course_cd = [{'trt_course_cd': columns['trt_course_cd']} for columns in expect_data_list]
        actual_data_list = []
        sp_field_select_sql = """\
        SELECT * FROM src05.com_inst_trt_course
        WHERE
                dcf_dsf_inst_cd = :dcf_dsf_inst_cd
            AND trt_course_cd = :trt_course_cd\
        """
        for primary_keys_dcf_dsf_inst_cd, primary_keys_trt_course_cd in zip(primary_keys_dcf_dsf_inst_cd, primary_keys_trt_course_cd):
            sp_trt_data = self.db.execute_select(
                sp_field_select_sql,
                {**primary_keys_dcf_dsf_inst_cd, **primary_keys_trt_course_cd})
            assert len(sp_trt_data) == 1, '1件取得できていること'
            actual_data_list.append(sp_trt_data[0])

        # 期待値検査
        ignore_columns = ['regist_ymd', 'update_ymd', 'sys_update_date', 'sys_regist_date']
        assert_table_results(actual_data_list, expect_data_list, ignore_col_name=ignore_columns)
        # 動的日付項目の個別確認
        line_number = 0
        for actual_row, expect_row in zip(actual_data_list, expect_data_list):
            line_number += 1
            for actual_col_name, expect_col_name in zip(actual_row, expect_row):
                if actual_col_name in ignore_columns:
                    if expect_row[expect_col_name] is None:
                        assert actual_row[actual_col_name] is None, f'{line_number}行目:{actual_col_name}が、登録されていないこと'
                    else:
                        assert actual_row[actual_col_name] >= expect_row[expect_col_name], f'{line_number}行目:{actual_col_name}が、期待値以降であること'

        # 期待値ファイルを読み込む(特養医務室データ)
        expect_data_list = create_db_data_from_csv(path.join(self.test_file_path, 'expect_com_spcare_delete.csv'))
        primary_keys = [f"'{primary_key['dcf_chld_inst_cd']}'" for primary_key in expect_data_list]
        actual_select_sql = f"SELECT * FROM src05.com_spcare_med_office_dat WHERE dcf_chld_inst_cd IN ({','.join(primary_keys)})"
        actual_data_list = self.db.execute_select(actual_select_sql)
        # 期待値検査
        ignore_columns = ['regist_ymd', 'update_ymd', 'delete_ymd', 'sys_update_date', 'sys_regist_date']
        assert_table_results(actual_data_list, expect_data_list, ignore_col_name=ignore_columns)
        # 動的日付項目の個別確認
        line_number = 0
        for actual_row, expect_row in zip(actual_data_list, expect_data_list):
            line_number += 1
            for actual_col_name, expect_col_name in zip(actual_row, expect_row):
                if actual_col_name in ignore_columns:
                    if expect_row[expect_col_name] is None:
                        assert actual_row[actual_col_name] is None, f'{line_number}行目:{actual_col_name}が、登録されていないこと'
                    else:
                        assert actual_row[actual_col_name] >= expect_row[expect_col_name], f'{line_number}行目:{actual_col_name}が、期待値以降であること'
