import { ENV_OPTIONAL_NUMBER_KEYS_MAPPING, ErrorCodes, NotFoundError, SEQUELIZE_INSTANCE_TOKEN } from '@humand-packages/common';
import { Sequelize } from 'sequelize-typescript';
import { Inject, Service } from 'typedi';
import { BaseRepositoryAdapter } from '../../../../../dbAdapters/baseRepositoryAdapter';
import { InstanceDAO } from '../../../../instances/infrastructure/dataAccessObjects/instanceDAO';
import { UserInterface } from '../../../../users/business/models/userInterface';
import TimeTrackingKiosksFilters from '../../../business/interfaces/timeTrackingKioskFilters';
import TimeTrackingKiosksOptions from '../../../business/interfaces/timeTrackingKioskOptions';
import { TimeTrackingKiosk, TimeTrackingKioskForCreation } from '../../../business/models/timeTrackingKiosk';
import { TimeTrackingKioskDAO } from '../../dataAccessObjects/kiosk';
import { mapKioskDAOToModel, mapKioskModelToDAO } from '../../mappers/timeTrackingKioskMapper';
import TimeTrackingKioskRepositoryPort from '../../ports/TimeTrackingKioskRepositoryPort';

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

    public list = async (
        user: UserInterface,
        filters: TimeTrackingKiosksFilters,
        options: TimeTrackingKiosksOptions,
    ): Promise<{ count: number; rows: TimeTrackingKiosk[] }> => {
        const where = Object.fromEntries(Object.entries(filters).filter(([_, value]) => value !== undefined));
        const { count, rows } = await TimeTrackingKioskDAO.findAndCountAll({
            distinct: true,
            limit: options.limit,
            offset: options.offset,
            order: [['name', 'ASC']],
            where: { ...where, instanceId: user.instanceId },
        });
        return { count, rows: rows.map(mapKioskDAOToModel) };
    };

    public getById = async (instanceId: number, id: number): Promise<TimeTrackingKiosk | null> => {
        const kioskDAO = await TimeTrackingKioskDAO.findByPk(id);
        return kioskDAO && kioskDAO.instanceId === instanceId ? mapKioskDAOToModel(kioskDAO) : null;
    };

    public getByIdOrFail = async (instanceId: number, id: number): Promise<TimeTrackingKiosk> => {
        const kioskDAO = await TimeTrackingKioskDAO.findByPk(id);
        if (!kioskDAO || kioskDAO.instanceId !== instanceId) {
            throw new NotFoundError('Kiosk not found', ErrorCodes.NOT_FOUND);
        }
        return mapKioskDAOToModel(kioskDAO);
    };

    public create = async (user: UserInterface, kiosk: TimeTrackingKioskForCreation): Promise<TimeTrackingKiosk> => {
        const kioskDAO = new TimeTrackingKioskDAO({
            ...kiosk,
            createdBy: user.id,
            instanceId: user.instanceId,
        });
        await kioskDAO.save();
        return mapKioskDAOToModel(kioskDAO);
    };

    public update = async (updatedKiosk: TimeTrackingKiosk): Promise<TimeTrackingKiosk> => {
        const kioskDAO = mapKioskModelToDAO(updatedKiosk);
        await kioskDAO.save();
        return mapKioskDAOToModel(kioskDAO);
    };

    public eliminate = async (id: number, instanceId: number): Promise<void> => {
        await TimeTrackingKioskDAO.destroy({
            where: {
                id,
                instanceId,
            },
        });
    };

    public getByIdIncludeInstance = async (id: number): Promise<TimeTrackingKiosk | null> => {
        const kiosk = await TimeTrackingKioskDAO.findOne({
            include: [
                {
                    model: InstanceDAO,
                    required: true,
                },
            ],
            where: {
                id,
            },
        });
        return kiosk ? mapKioskDAOToModel(kiosk) : null;
    };

    public getByBotAppId = async (botAppId: number, instanceId: number): Promise<TimeTrackingKiosk | null> => {
        const kiosk = await TimeTrackingKioskDAO.findOne({
            where: {
                botAppId,
                instanceId,
            },
        });
        return kiosk ? mapKioskDAOToModel(kiosk) : null;
    };

    public removeKiosksFromSite = async (siteId: number, instanceId: number): Promise<void> => {
        return await this.getTransaction(async () => {
            await TimeTrackingKioskDAO.update(
                {
                    siteId: null,
                },
                { where: { siteId, instanceId } },
            );
        });
    };
}
