const { EventStoreDBClient, isCommandError } = require("@eventstore/db-client");
const Projection = require("../entity/projection").Projection;
const config = require(`../../../config/config.default.js`).config;

const Logger = require('../../../utils/logger').Logger;
let logger = new Logger(__filename);


/**
 * projection client for admin projection
 */
class ProjectionClient {

  systemProjections = [
    'by_category',
    'by_event_type',
    'by_correlation_id',
    'stream_by_category',
    'streams'
  ];

  constructor() {
    try {
      this.client = EventStoreDBClient.connectionString(config.event_store.connectUrl);
    } catch (error) {
      logger.error(error);
    }

  }

  async create(name, projection) {
    try {
      const projectionStance = new Projection(name, projection);
      await this.client.createProjection(name, projectionStance.send());
      return { 'code': 200, 'message': "create projection success!" };
    } catch (err) {
      if (!isCommandError(err) || !err.message.includes("Conflict")) throw err;
      console.log(`${name} already exists`);
      return { 'code': 1001, 'message': `projection ${name} already exists` };
    }
  }


  async restart() {
    await this.client.restartSubsystem();
  }

  async enable(projectionName) {
    try {
      return await this.client.enableProjection(projectionName);
    } catch (err) {
      if (!isCommandError(err) || !err.message.includes("NotFound")) throw err;
      console.log(`${projectionName} does not exist`);
    }
  }

  async enableSystem(systemProjection) {
    try {
      if (!this.systemProjections.find(systemProjection)) {
        console.log(`${systemProjection} is not system projection.`);
        return false;
      }
      await this.client.enableProjection(systemProjection);
    } catch (err) {
      if (!isCommandError(err) || !err.message.includes("NotFound")) throw err;
      console.log(`${projectionName} does not exist`);
    }
  }

  async disable(pNameOrType) {
    try {
      await this.client.disableProjection(pNameOrType);
    } catch (err) {
      if (!isCommandError(err) || !err.message.includes("NotFound")) throw err;
      console.log(`${pNameOrType} does not exist`);
    }
  }

  async delete(projectionName) {
    try {
      await this.client.disableProjection(projectionName);
      await this.client.deleteProjection(projectionName);
      return { 'code': 200, 'message': `delete projections ${projectionName} success` };
    } catch (err) {
      if (!isCommandError(err) || !err.message.includes("NotFound")) throw err;
      console.log(`${projectionName} does not exist`);
      return { 'code': 202, 'message': `${projectionName} does not exist` };
    }
  }

  async abort(projectionName) {
    try {
      return await this.client.abortProjection(projectionName);
    } catch (err) {
      if (!isCommandError(err) || !err.message.includes("NotFound")) throw err;
      console.log(`${projectionName} does not exist`);
    }
  }

  async reset(projectionName) {
    try {
      return await this.client.resetProjection(projectionName);
    } catch (err) {
      if (!isCommandError(err) || !err.message.includes("NotFound")) throw err;
      console.log(`${projectionName} does not exist`);
    }
  }

  async update(projectionName, projection) {
    try {
      await this.client.updateProjection(projectionName, projection);
      return { 'code': 200, 'message': `update projections ${projectionName} success` };
    } catch (err) {
      if (!isCommandError(err) || !err.message.includes("NotFound")) throw err;
      console.log(`${projectionName} does not exist`);
      return { 'code': 202, 'message': `${projectionName} does not exist` };
    }
  }

  async getList() {
    let results = [];
    const projections = await this.client.listProjections();
    for (const { name, status, checkpointStatus, progress } of projections) {
      console.log(name, status, checkpointStatus, progress);
      let projection = { name, status, checkpointStatus, progress };
      results.push(projection);
    }
    return results;
  }

  async getStatus(projectionName) {
    try {
      const projectionResult = await this.client.getProjectionStatus(projectionName);
      const result = {
        'status': projectionResult.status,
        'name': projectionResult.name,
        'projectionStatus': projectionResult.projectionStatus
      }
      return { 'code': 200, 'result': result }
    } catch (err) {
      if (!isCommandError(err) || !err.message.includes("NotFound")) throw err;
      console.log(`${projectionName} does not exist`);
      return { 'code': 204, 'message': `${projectionName} does not exist` };
    }
  }

  async getState(name) {
    return await this.client.getProjectionState(name);
  }

  async getResult(name) {
    return await this.client.getProjectionResult(name);
  }
}

exports.ProjectionClient = ProjectionClient;
