import { BaseController, CapabilityName, LoggerPort } from '@humand-packages/common';
import { NextFunction, Request } from 'express';
import { CREATED, NO_CONTENT, OK } from 'http-status-codes';
import { Service } from 'typedi';
import { PermissionsService } from '../../../permissions/business/services/permissionsService';
import { userDAOtoUser } from '../../../users/infrastructure/mappers/usersMapper';
import { RotationAssignmentService } from '../../business/services/shiftManagement/rotationAssignmentService';
import { ShiftRotationService } from '../../business/services/shiftManagement/shiftRotationService';
import { ShiftRotationListItemSC } from '../serializationClasses/shiftManagement/ShiftRotationListItemSC';
import { ShiftRotationSC } from '../serializationClasses/shiftManagement/ShiftRotationSC';
import { ShiftsResponse } from '../types/response';
import { AssignShiftRotationVC } from '../validationClasses/shiftManagement/assignShiftRotationVC';
import { CreateOrUpdateShiftRotationVC } from '../validationClasses/shiftManagement/createOrUpdateShiftRotationVC';
import { ShiftRotationsListFiltersVC } from '../validationClasses/shiftManagement/shiftRotationsListFiltersVC';

@Service()
export class ShiftRotationController extends BaseController {
    constructor(
        private readonly shiftRotationService: ShiftRotationService,
        private readonly rotationAssignmentService: RotationAssignmentService,
        private readonly permissionService: PermissionsService,
        logger: LoggerPort,
    ) {
        super(logger);
    }

    public create = async (_req: Request, res: ShiftsResponse, next: NextFunction): Promise<void> => {
        const { bodyParams, loggedUser, instanceOfLoggedUser } = res.locals;
        const params = bodyParams as CreateOrUpdateShiftRotationVC;
        const user = userDAOtoUser(loggedUser);
        await this.endpointHandlerWithGenerics(next, async () => {
            this.permissionService.validateManageShifts(user);
            const { rotation, shiftsByCode } = await this.shiftRotationService.create(instanceOfLoggedUser.getId(), params);
            res.status(CREATED).json(new ShiftRotationSC().serialize(rotation, { shiftsByCode }));
        });
    };

    public list = async (_req: Request, res: ShiftsResponse, next: NextFunction): Promise<void> => {
        const { loggedUser, instanceOfLoggedUser, queryParams, requiredLimitOffsetPagination: pagination } = res.locals;
        const filters = queryParams as ShiftRotationsListFiltersVC;
        const user = userDAOtoUser(loggedUser);
        await this.endpointHandlerWithGenerics(next, async () => {
            this.permissionService.validateViewShifts(user);
            const result = await this.shiftRotationService.list(instanceOfLoggedUser.getId(), filters, pagination);
            res.status(OK).json(
                new ShiftRotationListItemSC().serializePaginatedList({
                    count: result.count,
                    limit: pagination.limit,
                    offset: pagination.offset,
                    rows: result.rows,
                }),
            );
        });
    };

    public getById = async (req: Request, res: ShiftsResponse, next: NextFunction): Promise<void> => {
        const { loggedUser, instanceOfLoggedUser } = res.locals;
        const id = req.params.id;
        const user = userDAOtoUser(loggedUser);
        await this.endpointHandlerWithGenerics(next, async () => {
            this.permissionService.validateViewShifts(user);
            const { rotation, shiftsByCode } = await this.shiftRotationService.getById(instanceOfLoggedUser.getId(), id);
            res.status(OK).json(new ShiftRotationSC().serialize(rotation, { shiftsByCode }));
        });
    };

    public update = async (req: Request, res: ShiftsResponse, next: NextFunction): Promise<void> => {
        const { bodyParams, loggedUser, instanceOfLoggedUser } = res.locals;
        const id = req.params.id;
        const params = bodyParams as CreateOrUpdateShiftRotationVC;
        const user = userDAOtoUser(loggedUser);
        await this.endpointHandlerWithGenerics(next, async () => {
            this.permissionService.validateManageShifts(user);
            const { rotation, shiftsByCode } = await this.shiftRotationService.update(instanceOfLoggedUser.getId(), id, params);
            res.status(OK).json(new ShiftRotationSC().serialize(rotation, { shiftsByCode }));
        });
    };

    public assign = async (req: Request, res: ShiftsResponse, next: NextFunction): Promise<void> => {
        const { bodyParams, loggedUser, instanceOfLoggedUser } = res.locals;
        const id = req.params.id;
        const params = bodyParams as AssignShiftRotationVC;
        const user = userDAOtoUser(loggedUser);
        await this.endpointHandlerWithGenerics(next, async () => {
            this.permissionService.validateViewShifts(user);
            const hasManageCapability = this.permissionService.hasAnyCapability(user, [CapabilityName.MANAGE_SHIFTS]);
            await this.rotationAssignmentService.assignRotation(instanceOfLoggedUser, user, id, params, hasManageCapability);
            res.status(NO_CONTENT).end();
        });
    };

    public delete = async (req: Request, res: ShiftsResponse, next: NextFunction): Promise<void> => {
        const { loggedUser, instanceOfLoggedUser } = res.locals;
        const id = req.params.id;
        const user = userDAOtoUser(loggedUser);
        await this.endpointHandlerWithGenerics(next, async () => {
            this.permissionService.validateManageShifts(user);
            await this.shiftRotationService.delete(instanceOfLoggedUser.getId(), id);
            res.status(NO_CONTENT).end();
        });
    };
}
