import { ENV_OPTIONAL_NUMBER_KEYS_MAPPING, SEQUELIZE_INSTANCE_TOKEN } from '@humand-packages/common';
import { Op, WhereOptions } from 'sequelize';
import { Sequelize } from 'sequelize-typescript';
import { Inject, Service } from 'typedi';
import { BaseRepositoryAdapter } from '../../../../../../dbAdapters/baseRepositoryAdapter';
import { TimeTrackingUserSetting, TimeTrackingUserSettingForCreation } from '../../../../business/models/timeTracking/timeTrackingUserSetting';
import { TimeTrackingUserSettingDAO } from '../../../dataAccessObjects/timeTracking/timeTrackingUserSetting';
import {
    mapTimeTrackingUserSettingDAOToModel,
    mapTimeTrackingUserSettingModelToDAO,
} from '../../../mappers/timeTracking/timeTrackingUserSettingMapper';
import TimeTrackingUserSettingRepositoryPort from '../../../ports/timeTracking/timeTrackingUserSettingRepositoryPort';

@Service()
export default class TimeTrackingUserSettingRepositoryAdapter extends BaseRepositoryAdapter implements TimeTrackingUserSettingRepositoryPort {
    constructor(
        @Inject(ENV_OPTIONAL_NUMBER_KEYS_MAPPING.BULK_CREATE_CHUNK_SIZE) bulkCreateChunkSize: number,
        @Inject(SEQUELIZE_INSTANCE_TOKEN) sequelize: Sequelize,
    ) {
        super(bulkCreateChunkSize, sequelize);
    }

    public getByInstanceIdAndUserId = async (instanceId: number, userId: number): Promise<TimeTrackingUserSetting | null> => {
        const userSetting = await TimeTrackingUserSettingDAO.findOne({
            where: { instanceId, userId },
        });
        return userSetting ? mapTimeTrackingUserSettingDAOToModel(userSetting) : null;
    };

    public getByInstanceIdAndKioskPin = async (instanceId: number, kioskPin: string): Promise<TimeTrackingUserSetting | null> => {
        const userSetting = await TimeTrackingUserSettingDAO.findOne({
            where: { instanceId, kioskPin },
        });
        return userSetting ? mapTimeTrackingUserSettingDAOToModel(userSetting) : null;
    };

    public create = async (userSetting: TimeTrackingUserSetting): Promise<TimeTrackingUserSetting> => {
        const userSettingDAO = await TimeTrackingUserSettingDAO.create(userSetting);
        return mapTimeTrackingUserSettingDAOToModel(userSettingDAO);
    };

    public update = async (userSetting: TimeTrackingUserSetting): Promise<TimeTrackingUserSetting> => {
        const userSettingDAO = mapTimeTrackingUserSettingModelToDAO(userSetting);
        await userSettingDAO.save();
        return mapTimeTrackingUserSettingDAOToModel(userSettingDAO);
    };

    public delete = async (id: number): Promise<void> => {
        await TimeTrackingUserSettingDAO.destroy({ where: { id } });
    };

    public existsByInstanceIdAndKioskPin = async (instanceId: number, kioskPin: string, excludeUserId?: number): Promise<boolean> => {
        const where: WhereOptions<TimeTrackingUserSettingDAO> = {
            instanceId,
            kioskPin,
            ...(excludeUserId && {
                userId: { [Op.ne]: excludeUserId },
            }),
        };
        const userSetting = await TimeTrackingUserSettingDAO.findOne({ where });
        return !!userSetting;
    };

    public getByInstanceIdAndKioskPinList = async (instanceId: number, kioskPinList: string[]): Promise<TimeTrackingUserSetting[]> => {
        const userSettings = await TimeTrackingUserSettingDAO.findAll({ where: { instanceId, kioskPin: { [Op.in]: kioskPinList } } });
        return userSettings.map(mapTimeTrackingUserSettingDAOToModel);
    };

    public bulkUpsert = async (userSettings: TimeTrackingUserSettingForCreation[]): Promise<TimeTrackingUserSetting[]> => {
        const userSettingsDAO = await TimeTrackingUserSettingDAO.bulkCreate(userSettings, {
            conflictAttributes: ['instanceId', 'userId'],
            conflictWhere: {
                deletedAt: null,
            },
            updateOnDuplicate: ['kioskPin'],
        });
        return userSettingsDAO.map(mapTimeTrackingUserSettingDAOToModel);
    };

    public bulkNullifyKioskPin = async (instanceId: number, userIds: number[]): Promise<void> => {
        await TimeTrackingUserSettingDAO.update({ kioskPin: null }, { where: { instanceId, userId: userIds } });
    };

    public deleteUserSettings = async (userIds: number[]): Promise<void> => {
        await TimeTrackingUserSettingDAO.destroy({ where: { userId: userIds } });
    };

    public getByInstanceIdAndUserIds = async (instanceId: number, userIds: number[]): Promise<TimeTrackingUserSetting[]> => {
        const userSettings = await TimeTrackingUserSettingDAO.findAll({ where: { instanceId, userId: userIds } });
        return userSettings.map(mapTimeTrackingUserSettingDAOToModel);
    };

    public getByInstanceId = async (instanceId: number): Promise<TimeTrackingUserSetting[]> => {
        const userSettings = await TimeTrackingUserSettingDAO.findAll({ where: { instanceId } });
        return userSettings.map(mapTimeTrackingUserSettingDAOToModel);
    };
}
