import { IBaseDataServicesRepository } from '../../domain/repositories';
import {
  DataServicesConstructorEntity,
  ResponseEntity,
  ApiURLEntity,
  RequestMethodEntity,
  ManagerParamsEntity,
  BaseEntity,
} from '../../domain/entities';

import { DEFAULT_METHOD, REQUEST_ACTION } from '../../infrastructure/constants';
import axios, { AxiosResponse } from 'axios';

export abstract class BaseRemoteDataServices<E extends BaseEntity = BaseEntity> implements IBaseDataServicesRepository {
  protected baseUrl?: string;
  protected URLs: ApiURLEntity;
  protected methods: RequestMethodEntity;
  protected moduleKey: string | undefined;

  constructor(params: DataServicesConstructorEntity) {
    const moduleKeyEncrypt = params.moduleKey;

    this.moduleKey = moduleKeyEncrypt;

    this.baseUrl = params.baseUrl;
    this.URLs = {
      ...(this.makeDefaultURL(params.apiUrl ?? '') ?? {}),
      ...(params.urls ?? {}),
    };
    this.methods = {
      ...(DEFAULT_METHOD ?? {}),
      ...(params.requestMethods ?? {}),
    };
  }

  protected makeApiUrl(variable = {} as any, url = '' as string): string {
    return url
      .split('/')
      .map((item: string) => {
        if (item.includes(':')) return variable[item.slice(1)];
        return item;
      })
      .join('/');
  }

  protected makeIds(payload: E[]): string[] {
    const ids: string[] = [];
    for (const entity of payload) {
      if (entity.id) ids.push(entity.id.toString());
    }
    return ids;
  }

  protected makeId(payload: E): string {
    if (payload.id) return payload.id;
    return '';
  }

  protected makeHeader(action: string) {
    return {
      'ex-model-key': this.moduleKey,
      'ex-model-action': action,
    };
  }

  protected makeDefaultURL(apiUrl: string): ApiURLEntity {
    return {
      getManyUrl: `${apiUrl}`,
      getOneUrl: `${apiUrl}/:id`,
      createUrl: `${apiUrl}`,
      editUrl: `${apiUrl}/:id`,

      deleteUrl: `${apiUrl}/:id`,
      batchDeleteUrl: `${apiUrl}/batch-delete`,

      activateUrl: `${apiUrl}/:id/active`,
      batchActivateUrl: `${apiUrl}/batch-active`,

      deactivateUrl: `${apiUrl}/:id/inactive`,
      batchDeactivateUrl: `${apiUrl}/batch-inactive`,

      confirmProcessDataUrl: `${apiUrl}/:id/confirm`,
      batchConfirmProcessDataUrl: `${apiUrl}/batch-confirm`,

      cancelProcessDataUrl: `${apiUrl}/:id/cancel`,
      batchCancelProcessDataUrl: `${apiUrl}/batch-cancel`,

      confirmProcessTransactionUrl: `${apiUrl}/:id/confirm-data`,
      batchConfirmProcessTransactionUrl: `${apiUrl}/batch-confirm-data`,

      cancelProcessTransactionUrl: `${apiUrl}/:id/cancel`,
      batchCancelProcessTransactionUrl: `${apiUrl}/batch-cancel`,

      rollbackProcessTransactionUrl: `${apiUrl}/:id/confirm-rollback`,
      batchRollbackProcessTransactionUrl: `${apiUrl}/batch-confirm-rollback`,

      holdProcessTransactionUrl: `${apiUrl}/:id/confirm-hold`,
      batchHoldProcessTransactionUrl: `${apiUrl}/batch-confirm-hold`,
    };
  }

  //custom request
  async handleCustomRequest(manager: ManagerParamsEntity): Promise<void> {
    try {
      const response: AxiosResponse = await axios.request({
        ...(manager.config ?? {}),
        baseURL: this.baseUrl,
        params: {
          ...(manager?.config?.params ?? {}),
        },
      });

      if (manager.onSuccess) manager.onSuccess(response as ResponseEntity);
    } catch (error) {
      if (manager.onFailed) manager.onFailed(error as ResponseEntity);
    }
  }

  async handleGetOne(manager: ManagerParamsEntity): Promise<void> {
    try {
      const response: AxiosResponse = await axios.request({
        baseURL: this.baseUrl,
        url: this.makeApiUrl(manager.variableURL, this.URLs.getOneUrl),
        method: this.methods.getOneMethod,
        ...(manager.config ?? {}),
        headers: this.makeHeader(REQUEST_ACTION.VIEW),
      });

      if (manager.onSuccess) manager.onSuccess(response as ResponseEntity);
    } catch (error) {
      if (manager.onFailed) manager.onFailed(error as ResponseEntity);
    }
  }

  async handleGetMany(manager: ManagerParamsEntity): Promise<void> {
    try {
      const response: AxiosResponse = await axios.request({
        baseURL: this.baseUrl,
        url: this.makeApiUrl(manager.variableURL, this.URLs.getManyUrl),
        method: this.methods.getManyMethod,
        ...(manager.config ?? {}),
        params: {
          ...(manager?.config?.params ?? {}),
        },
        headers: { ...this.makeHeader(REQUEST_ACTION.VIEW), ...(manager?.config?.headers ?? {}) },
      });

      if (manager.onSuccess) manager.onSuccess(response as ResponseEntity);
    } catch (error) {
      if (manager.onFailed) manager.onFailed(error as ResponseEntity);
    }
  }

  async handleCreate(manager: ManagerParamsEntity): Promise<void> {
    try {
      const response: AxiosResponse = await axios.request({
        baseURL: this.baseUrl,
        url: this.makeApiUrl(manager.variableURL, this.URLs.createUrl),
        method: this.methods.createMethod,
        // headers: HeaderPost,
        ...(manager.config ?? {}),
        params: {
          ...(manager?.config?.params ?? {}),
        },
        headers: this.makeHeader(REQUEST_ACTION.CREATE),
      });

      if (manager.onSuccess) manager.onSuccess(response as ResponseEntity);
    } catch (error) {
      if (manager.onFailed) manager.onFailed(error as ResponseEntity);
    }
  }

  async handleEdit(manager: ManagerParamsEntity): Promise<void> {
    try {
      const response: AxiosResponse = await axios.request({
        baseURL: this.baseUrl,
        url: this.makeApiUrl(manager.variableURL, this.URLs.editUrl),
        method: this.methods.editMethod,
        // headers: HeaderPost,
        ...(manager.config ?? {}),
        params: {
          ...(manager?.config?.params ?? {}),
        },
        headers: this.makeHeader(REQUEST_ACTION.EDIT),
      });

      if (manager.onSuccess) manager.onSuccess(response as ResponseEntity);
    } catch (error) {
      if (manager.onFailed) manager.onFailed(error as ResponseEntity);
    }
  }

  async handleDelete(manager: ManagerParamsEntity): Promise<void> {
    try {
      const response: AxiosResponse = await axios.request({
        baseURL: this.baseUrl,
        url: this.makeApiUrl(manager.variableURL, this.URLs.deleteUrl),
        method: this.methods.deleteMethod,
        ...(manager.config ?? {}),
        params: {
          ...(manager?.config?.params ?? {}),
        },
        headers: this.makeHeader(REQUEST_ACTION.DELETE),
      });

      if (manager.onSuccess) manager.onSuccess(response as ResponseEntity);
    } catch (error) {
      if (manager.onFailed) manager.onFailed(error as ResponseEntity);
    }
  }

  async handleBatchDelete(manager: ManagerParamsEntity): Promise<void> {
    try {
      const response: AxiosResponse = await axios.request({
        baseURL: this.baseUrl,
        url: this.makeApiUrl(manager.variableURL, this.URLs.batchDeleteUrl),
        method: this.methods.batchDeleteMethod,
        // data: { ids: this.makeIds(manager.config?.data) },
        ...(manager.config ?? {}),
        params: {
          ...(manager?.config?.params ?? {}),
        },
        headers: this.makeHeader(REQUEST_ACTION.DELETE),
      });

      if (manager.onSuccess) manager.onSuccess(response as ResponseEntity);
    } catch (error) {
      if (manager.onFailed) manager.onFailed(error as ResponseEntity);
    }
  }

  async handleConfirmProcessData(manager: ManagerParamsEntity): Promise<void> {
    try {
      const response: AxiosResponse = await axios.request({
        baseURL: this.baseUrl,
        url: this.makeApiUrl(manager.variableURL, this.URLs.confirmProcessDataUrl),
        method: this.methods.confirmProcessDataMethod,
        ...(manager.config ?? {}),
        params: {
          ...(manager?.config?.params ?? {}),
        },
        headers: this.makeHeader(REQUEST_ACTION.CONFIRM_DATA),
      });

      if (manager.onSuccess) manager.onSuccess(response as ResponseEntity);
    } catch (error) {
      if (manager.onFailed) manager.onFailed(error as ResponseEntity);
    }
  }

  async handleBatchConfirmProcessData(manager: ManagerParamsEntity): Promise<void> {
    try {
      const response: AxiosResponse = await axios.request({
        baseURL: this.baseUrl,
        url: this.makeApiUrl(manager.variableURL, this.URLs.batchConfirmProcessDataUrl),
        method: this.methods.batchConfirmProcessDataMethod,
        // data: { ids: this.makeIds(manager.config?.data) },
        ...(manager.config ?? {}),
        params: {
          ...(manager?.config?.params ?? {}),
        },
        headers: this.makeHeader(REQUEST_ACTION.CONFIRM_DATA),
      });

      if (manager.onSuccess) manager.onSuccess(response as ResponseEntity);
    } catch (error) {
      if (manager.onFailed) manager.onFailed(error as ResponseEntity);
    }
  }

  async handleCancelProcessData(manager: ManagerParamsEntity): Promise<void> {
    try {
      const response: AxiosResponse = await axios.request({
        baseURL: this.baseUrl,
        url: this.makeApiUrl(manager.variableURL, this.URLs.cancelProcessDataUrl),
        method: this.methods.cancelProcessDataMethod,
        ...(manager.config ?? {}),
        params: {
          ...(manager?.config?.params ?? {}),
        },
        headers: this.makeHeader(REQUEST_ACTION.CANCEL_DATA),
      });

      if (manager.onSuccess) manager.onSuccess(response as ResponseEntity);
    } catch (error) {
      if (manager.onFailed) manager.onFailed(error as ResponseEntity);
    }
  }

  async handleBatchCancelProcessData(manager: ManagerParamsEntity): Promise<void> {
    try {
      const response: AxiosResponse = await axios.request({
        baseURL: this.baseUrl,
        url: this.makeApiUrl(manager.variableURL, this.URLs.batchCancelProcessDataUrl),
        method: this.methods.batchCancelProcessDataMethod,
        // data: { ids: this.makeIds(manager.config?.data) },
        ...(manager.config ?? {}),
        params: {
          ...(manager?.config?.params ?? {}),
        },
        headers: this.makeHeader(REQUEST_ACTION.CANCEL_DATA),
      });

      if (manager.onSuccess) manager.onSuccess(response as ResponseEntity);
    } catch (error) {
      if (manager.onFailed) manager.onFailed(error as ResponseEntity);
    }
  }

  async handleActivate(manager: ManagerParamsEntity): Promise<void> {
    try {
      const response: AxiosResponse = await axios.request({
        baseURL: this.baseUrl,
        url: this.makeApiUrl(manager.variableURL, this.URLs.activateUrl),
        method: this.methods.activateMethod,
        ...(manager.config ?? {}),
        params: {
          ...(manager?.config?.params ?? {}),
        },
        headers: this.makeHeader(REQUEST_ACTION.CONFIRM_DATA),
      });

      if (manager.onSuccess) manager.onSuccess(response as ResponseEntity);
    } catch (error) {
      if (manager.onFailed) manager.onFailed(error as ResponseEntity);
    }
  }

  async handleBatchActivate(manager: ManagerParamsEntity): Promise<void> {
    try {
      const response: AxiosResponse = await axios.request({
        baseURL: this.baseUrl,
        url: this.makeApiUrl(manager.variableURL, this.URLs.batchActivateUrl),
        method: this.methods.batchActivateMethod,
        // data: { ids: this.makeIds(manager.config?.data) },
        ...(manager.config ?? {}),
        params: {
          ...(manager?.config?.params ?? {}),
        },
        headers: this.makeHeader(REQUEST_ACTION.CONFIRM_DATA),
      });

      if (manager.onSuccess) manager.onSuccess(response as ResponseEntity);
    } catch (error) {
      if (manager.onFailed) manager.onFailed(error as ResponseEntity);
    }
  }

  async handleDeactivate(manager: ManagerParamsEntity): Promise<void> {
    try {
      const response: AxiosResponse = await axios.request({
        baseURL: this.baseUrl,
        url: this.makeApiUrl(manager.variableURL, this.URLs.deactivateUrl),
        method: this.methods.deactivateMethod,
        ...(manager.config ?? {}),
        params: {
          ...(manager?.config?.params ?? {}),
        },
        headers: this.makeHeader(REQUEST_ACTION.CONFIRM_DATA),
      });

      if (manager.onSuccess) manager.onSuccess(response as ResponseEntity);
    } catch (error) {
      if (manager.onFailed) manager.onFailed(error as ResponseEntity);
    }
  }

  async handleBatchDeactivate(manager: ManagerParamsEntity): Promise<void> {
    try {
      const response: AxiosResponse = await axios.request({
        baseURL: this.baseUrl,
        url: this.makeApiUrl(manager.variableURL, this.URLs.batchDeactivateUrl),
        method: this.methods.batchDeactivateMethod,
        // data: { ids: this.makeIds(manager.config?.data) },
        ...(manager.config ?? {}),
        params: {
          ...(manager?.config?.params ?? {}),
        },
        headers: this.makeHeader(REQUEST_ACTION.CONFIRM_DATA),
      });

      if (manager.onSuccess) manager.onSuccess(response as ResponseEntity);
    } catch (error) {
      if (manager.onFailed) manager.onFailed(error as ResponseEntity);
    }
  }

  async handleConfirmProcessTransaction(manager: ManagerParamsEntity): Promise<void> {
    try {
      const response: AxiosResponse = await axios.request({
        baseURL: this.baseUrl,
        url: this.makeApiUrl(manager.variableURL, this.URLs.confirmProcessTransactionUrl),
        method: this.methods.confirmProcessTransactionMethod,
        ...(manager.config ?? {}),
        params: {
          ...(manager?.config?.params ?? {}),
        },
        headers: this.makeHeader(REQUEST_ACTION.CONFIRM_PROCESS_TRANSACTION),
      });

      if (manager.onSuccess) manager.onSuccess(response as ResponseEntity);
    } catch (error) {
      if (manager.onFailed) manager.onFailed(error as ResponseEntity);
    }
  }

  async handleBatchConfirmProcessTransaction(manager: ManagerParamsEntity): Promise<void> {
    try {
      const response: AxiosResponse = await axios.request({
        baseURL: this.baseUrl,
        url: this.makeApiUrl(manager.variableURL, this.URLs.batchConfirmProcessTransactionUrl),
        method: this.methods.batchConfirmProcessTransactionMethod,
        // data: { ids: this.makeIds(manager.config?.data) },
        ...(manager.config ?? {}),
        params: {
          ...(manager?.config?.params ?? {}),
        },
        headers: this.makeHeader(REQUEST_ACTION.CONFIRM_PROCESS_TRANSACTION),
      });

      if (manager.onSuccess) manager.onSuccess(response as ResponseEntity);
    } catch (error) {
      if (manager.onFailed) manager.onFailed(error as ResponseEntity);
    }
  }

  async handleCancelProcessTransaction(manager: ManagerParamsEntity): Promise<void> {
    try {
      const response: AxiosResponse = await axios.request({
        baseURL: this.baseUrl,
        url: this.makeApiUrl(manager.variableURL, this.URLs.cancelProcessTransactionUrl),
        method: this.methods.cancelProcessTransactionMethod,
        ...(manager.config ?? {}),
        params: {
          ...(manager?.config?.params ?? {}),
        },
        headers: this.makeHeader(REQUEST_ACTION.CANCEL_PROCESS_TRANSACTION),
      });

      if (manager.onSuccess) manager.onSuccess(response as ResponseEntity);
    } catch (error) {
      if (manager.onFailed) manager.onFailed(error as ResponseEntity);
    }
  }

  async handleBatchCancelProcessTransaction(manager: ManagerParamsEntity): Promise<void> {
    try {
      const response: AxiosResponse = await axios.request({
        baseURL: this.baseUrl,
        url: this.makeApiUrl(manager.variableURL, this.URLs.batchCancelProcessTransactionUrl),
        method: this.methods.batchCancelProcessTransactionMethod,
        // data: { ids: this.makeIds(manager.config?.data) },
        ...(manager.config ?? {}),
        params: {
          ...(manager?.config?.params ?? {}),
        },
        headers: this.makeHeader(REQUEST_ACTION.CANCEL_PROCESS_TRANSACTION),
      });

      if (manager.onSuccess) manager.onSuccess(response as ResponseEntity);
    } catch (error) {
      if (manager.onFailed) manager.onFailed(error as ResponseEntity);
    }
  }

  async handleRollbackProcessTransaction(manager: ManagerParamsEntity): Promise<void> {
    try {
      const response: AxiosResponse = await axios.request({
        baseURL: this.baseUrl,
        url: this.makeApiUrl(manager.variableURL, this.URLs.rollbackProcessTransactionUrl),
        method: this.methods.rollbackProcessTransactionMethod,
        ...(manager.config ?? {}),
        params: {
          ...(manager?.config?.params ?? {}),
        },
        headers: this.makeHeader(REQUEST_ACTION.CONFIRM_PROCESS_TRANSACTION),
      });

      if (manager.onSuccess) manager.onSuccess(response as ResponseEntity);
    } catch (error) {
      if (manager.onFailed) manager.onFailed(error as ResponseEntity);
    }
  }

  async handleBatchRollbackProcessTransaction(manager: ManagerParamsEntity): Promise<void> {
    try {
      const response: AxiosResponse = await axios.request({
        baseURL: this.baseUrl,
        url: this.makeApiUrl(manager.variableURL, this.URLs.batchRollbackProcessTransactionUrl),
        method: this.methods.batchRollbackProcessTransactionMethod,
        // data: { ids: this.makeIds(manager.config?.data) },
        ...(manager.config ?? {}),
        params: {
          ...(manager?.config?.params ?? {}),
        },
        headers: this.makeHeader(REQUEST_ACTION.CONFIRM_PROCESS_TRANSACTION),
      });

      if (manager.onSuccess) manager.onSuccess(response as ResponseEntity);
    } catch (error) {
      if (manager.onFailed) manager.onFailed(error as ResponseEntity);
    }
  }

  async handleHoldProcessTransaction(manager: ManagerParamsEntity): Promise<void> {
    try {
      const response: AxiosResponse = await axios.request({
        baseURL: this.baseUrl,
        url: this.makeApiUrl(manager.variableURL, this.URLs.holdProcessTransactionUrl),
        method: this.methods.holdProcessTransactionMethod,
        ...(manager.config ?? {}),
        params: {
          action: REQUEST_ACTION.CONFIRM_PROCESS_TRANSACTION,
          ...(manager?.config?.params ?? {}),
        },
      });

      if (manager.onSuccess) manager.onSuccess(response as ResponseEntity);
    } catch (error) {
      if (manager.onFailed) manager.onFailed(error as ResponseEntity);
    }
  }

  async handleBatchHoldProcessTransaction(manager: ManagerParamsEntity): Promise<void> {
    try {
      const response: AxiosResponse = await axios.request({
        baseURL: this.baseUrl,
        url: this.makeApiUrl(manager.variableURL, this.URLs.batchHoldProcessTransactionUrl),
        method: this.methods.batchHoldProcessTransactionMethod,
        // data: { ids: this.makeIds(manager.config?.data) },
        ...(manager.config ?? {}),
        params: {
          action: REQUEST_ACTION.CONFIRM_PROCESS_TRANSACTION,
          ...(manager?.config?.params ?? {}),
        },
      });

      if (manager.onSuccess) manager.onSuccess(response as ResponseEntity);
    } catch (error) {
      if (manager.onFailed) manager.onFailed(error as ResponseEntity);
    }
  }
}
