import { ENV_OPTIONAL_NUMBER_KEYS_MAPPING, ErrorCodes, NotFoundError, SEQUELIZE_INSTANCE_TOKEN } from '@humand-packages/common';
import moment from 'moment';
import { Op } from 'sequelize';
import { Sequelize } from 'sequelize-typescript';
import { Inject, Service } from 'typedi';
import { BaseRepositoryAdapter } from '../../../../../dbAdapters/baseRepositoryAdapter';
import { TimeTrackingKioskLink, TimeTrackingKioskLinkForCreation } from '../../../business/models/timeTrackingKioskLink';
import { TimeTrackingKioskLinkStatus } from '../../../business/types/kioskLinkStatus';
import { TimeTrackingKioskLinkDAO } from '../../dataAccessObjects/kioskLink';
import { mapKioskLinkDAOToModel, mapKioskLinkModelToDAO } from '../../mappers/timeTrackingKioskLinkMapper';
import TimeTrackingKioskLinkRepositoryPort from '../../ports/TimeTrackingKioskLinkRepositoryPort';

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

    public create = async (link: TimeTrackingKioskLinkForCreation): Promise<TimeTrackingKioskLink> => {
        const linkDAO = new TimeTrackingKioskLinkDAO({
            ...link,
        });
        await linkDAO.save();
        return mapKioskLinkDAOToModel(linkDAO);
    };

    public findActiveNonExpiredLinkByCode = async (code: string): Promise<TimeTrackingKioskLink | null> => {
        const link = await TimeTrackingKioskLinkDAO.findOne({
            where: {
                code,
                expirationDate: {
                    [Op.ne]: null,
                    [Op.gt]: moment().toDate(),
                },
                status: {
                    [Op.in]: [TimeTrackingKioskLinkStatus.ACTIVE, TimeTrackingKioskLinkStatus.WAITING_FOR_LINK],
                },
            },
        });
        return link ? mapKioskLinkDAOToModel(link) : null;
    };

    public findActiveLinksByCode = async (code: string): Promise<TimeTrackingKioskLink[]> => {
        const links = await TimeTrackingKioskLinkDAO.findAll({
            order: [['expirationDate', 'DESC']],
            where: {
                code,
                expirationDate: {
                    [Op.ne]: null,
                },
                status: {
                    [Op.in]: [TimeTrackingKioskLinkStatus.ACTIVE],
                },
            },
        });
        return links.map(mapKioskLinkDAOToModel);
    };

    public updateLinksStatus = async (status: string, linkIds: number[]) => {
        await TimeTrackingKioskLinkDAO.update(
            { status },
            {
                where: {
                    id: linkIds,
                },
            },
        );
    };

    public update = async (link: TimeTrackingKioskLink): Promise<TimeTrackingKioskLink> => {
        const linkDAO = mapKioskLinkModelToDAO(link);
        await linkDAO.save();
        return mapKioskLinkDAOToModel(linkDAO);
    };

    public findByIdAndCode = async (id: number, code: string): Promise<TimeTrackingKioskLink> => {
        const linkDAO = await TimeTrackingKioskLinkDAO.findOne({
            where: {
                code,
                id,
            },
        });
        if (!linkDAO) {
            throw new NotFoundError('Code not found', ErrorCodes.NOT_FOUND);
        }
        return mapKioskLinkDAOToModel(linkDAO);
    };

    public findWaitingLinkByKioskId = async (kioskId: number, instanceId: number): Promise<TimeTrackingKioskLink | null> => {
        const link = await TimeTrackingKioskLinkDAO.findOne({
            where: {
                instanceId,
                kioskId,
                status: {
                    [Op.in]: [TimeTrackingKioskLinkStatus.WAITING_FOR_LINK],
                },
            },
        });
        return link ? mapKioskLinkDAOToModel(link) : null;
    };
}
