import Joi from "joi";

import { QueryCriteriaService } from "@/contexts/common/application/services";
import { IServerError } from "@/contexts/common/domain/errors";
import { ExpressController } from "@/contexts/common/infrastructure/express";
import { ListUsersResult, ListUsersUseCase } from "@/contexts/users/application";
import { User } from "@/contexts/users/domain";
import {
  ICollection,
  IListResponse_DTO,
  IListUsers_Response_DTO,
  IQueryCriteria,
  Result,
  RuleValidator,
} from "@shared/contexts";
import { IUserContext } from "../../../User.context";
import { IListUsersPresenter } from "./presenter";

export class ListUsersController extends ExpressController {
  private useCase: ListUsersUseCase;
  private presenter: IListUsersPresenter;
  private context: IUserContext;

  constructor(
    props: {
      useCase: ListUsersUseCase;
      presenter: IListUsersPresenter;
    },
    context: IUserContext
  ) {
    super();

    const { useCase, presenter } = props;
    this.useCase = useCase;
    this.presenter = presenter;
    this.context = context;
  }

  protected validateQuery(query): Result<any> {
    const schema = Joi.object({
      page: Joi.number().optional(),
      limit: Joi.number().optional(),
      $sort_by: Joi.string().optional(),
      $filters: Joi.string().optional(),
      q: Joi.string().optional(),
    }).optional();

    return RuleValidator.validate(schema, query);
  }

  async executeImpl() {
    const queryOrError = this.validateQuery(this.req.query);
    if (queryOrError.isFailure) {
      return this.clientError(queryOrError.error.message);
    }

    const queryParams = queryOrError.object;

    try {
      const queryCriteria: IQueryCriteria = QueryCriteriaService.parse(queryParams);

      const result: ListUsersResult = await this.useCase.execute({
        queryCriteria,
      });

      if (result.isFailure) {
        return this.clientError(result.error.message);
      }

      const users = <ICollection<User>>result.object;

      return this.ok<IListResponse_DTO<IListUsers_Response_DTO>>(
        this.presenter.mapArray(users, this.context, {
          page: queryCriteria.pagination.offset,
          limit: queryCriteria.pagination.limit,
        })
      );
    } catch (e: unknown) {
      return this.fail(e as IServerError);
    }
  }
}
