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 { ListDealersResult, ListDealersUseCase } from "@/contexts/sales/application";
import { Dealer } from "@/contexts/sales/domain";
import {
  ICollection,
  IListDealers_Response_DTO,
  IListResponse_DTO,
  IQueryCriteria,
  Result,
  RuleValidator,
} from "@shared/contexts";
import { ISalesContext } from "../../../../Sales.context";
import { IListDealersPresenter } from "./presenter";

export class ListDealersController extends ExpressController {
  private useCase: ListDealersUseCase;
  private presenter: IListDealersPresenter;
  private context: ISalesContext;

  constructor(
    props: {
      useCase: ListDealersUseCase;
      presenter: IListDealersPresenter;
    },
    context: ISalesContext
  ) {
    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: ListDealersResult = await this.useCase.execute({
        queryCriteria,
      });

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

      const dealers = <ICollection<Dealer>>result.object;

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