import { BaseAdapter, ErrorCodes, ListOptions, NotFoundError, UserStatus } from '@humand-packages/common';
import { UserDAO } from 'humand-packages/monolith/src/api/modules/users/infrastructure/dataAccessObjects/userDAO';
import { Service } from 'typedi';
import { OrganizationChartsService } from '../../../organizationCharts/business/services/organizationChartsService';
import { FindUserOptions } from '../../../users/business/interfaces/findUserOptions';
import { User } from '../../../users/business/models/user';
import { UsersService } from '../../../users/business/services/usersService';
import { UserListAllFilters } from '../../../users/business/types/listAllFilters';
import { userDAOtoUser } from '../../../users/infrastructure/mappers/usersMapper';
import { UsersServicePort } from '../ports/userServicePort';

@Service()
export class UsersServiceAdapter extends BaseAdapter implements UsersServicePort {
    constructor(
        private readonly usersService: UsersService,
        private readonly orgChart: OrganizationChartsService,
    ) {
        super();
    }

    async getUsers(
        loggedUser: User,
        filters: UserListAllFilters,
        { pagination, sortingOptions }: ListOptions,
        options?: FindUserOptions,
    ): Promise<{ count: number; rows: User[] }> {
        filters.status = filters.status || [UserStatus.ACTIVE, UserStatus.UNCLAIMED];
        const { count, rows } = await this.usersService.listAll(loggedUser, filters, { pagination, sortingOptions }, options);
        return { count, rows: rows.map(userDAOtoUser) };
    }

    async getFilteredIds(loggedUser: User, filters: UserListAllFilters, options?: FindUserOptions): Promise<number[]> {
        filters.status = filters.status || [UserStatus.ACTIVE, UserStatus.UNCLAIMED];
        return this.usersService.getFilteredIds(loggedUser, filters, options);
    }

    // filtered ids + subordinate ids
    async getUserIds(loggedUser: User, filters: UserListAllFilters, hasManageCapability: boolean): Promise<number[]> {
        filters.status = filters.status || [UserStatus.ACTIVE, UserStatus.UNCLAIMED];
        if (!hasManageCapability && !filters.ids?.length) {
            const subordinateIds = await this.orgChart.getSubordinateIdsRecursive(loggedUser.id, loggedUser.getInstanceId());
            if (!subordinateIds.length) return [];
            filters.ids = subordinateIds;
        }
        return await this.usersService.getFilteredIds(loggedUser, filters, { excludeLoggedUser: false });
    }

    async listAllByInstance(instanceId: number, filters: UserListAllFilters, { pagination, sortingOptions }: ListOptions, options?: FindUserOptions) {
        return await this.usersService.listAllByInstance(instanceId, filters, { pagination, sortingOptions }, options);
    }

    async getByIdOrFail(instanceId: number, id: number): Promise<UserDAO> {
        const user = await this.usersService.findByPk(instanceId, id);
        if (!user) throw new NotFoundError('User not found', ErrorCodes.USER_NOT_FOUND);
        return user;
    }

    async getByIdOrNull(instanceId: number, id: number): Promise<UserDAO | null> {
        return this.usersService.findByPkOrNull(instanceId, id);
    }

    async getValidUsersByIds(instanceId: number, userIds: number[]): Promise<number[]> {
        return this.usersService.getValidUsersByIds(instanceId, userIds);
    }

    async listUsers(
        loggedUser: UserDAO | User,
        filters: UserListAllFilters,
        { pagination, sortingOptions }: ListOptions,
        options?: FindUserOptions,
    ): Promise<{ count: number; rows: UserDAO[] }> {
        filters.status = filters.status || [UserStatus.ACTIVE, UserStatus.UNCLAIMED];
        return this.usersService.listAll(loggedUser, filters, { pagination, sortingOptions }, options);
    }
}
