import { Injectable, Logger } from '@nestjs/common';
import { DatabaseListen } from '../../constants';
import { EventBus } from '@nestjs/cqrs';
import { ChangeDocEvent } from '../../domain/events/change-doc.event';
import { ConfigService } from '@nestjs/config';
import { apm } from 'src/core/apm';
import * as Nano from 'nano';

@Injectable()
export class CouchService {
  constructor(
    private eventBus: EventBus,
    private configService: ConfigService,
  ) {}

  get nanoInstance() {
    const couchConfiguration = this.configService.get<string>('COUCHDB_CONFIG');
    return Nano(couchConfiguration);
  }

  async onModuleInit() {
    // return;
    const nano = this.nanoInstance;
    for (const database of DatabaseListen) {
      const db = nano.db.use(database);
      db.changesReader.start({ includeDocs: true }).on('change', (change) => {
        Logger.verbose(
          `Receive Data from ${database}: ${change?.id}`,
          'CouchService',
        );
        this.changeDoc(change, database);
      });

      // transaction
      Logger.log(`start listen database ${database}`, 'CouchService');
    }
  }

  private changeDoc(data, database) {
    this.eventBus.publish(
      new ChangeDocEvent({
        id: data.id,
        database: database,
        data: data.doc,
      }),
    );
  }

  public async createDoc(data, database) {
    try {
      const nano = this.nanoInstance;
      const db = nano.use(database);
      return await db.insert(data);
    } catch (error) {
      console.log(error);
      apm.captureError(error);
    }
  }

  public async deleteDoc(data, database) {
    try {
      const nano = this.nanoInstance;
      const db = nano.use(database);
      const result = await db.get(data.id);
      await db.destroy(data.id, result._rev);
    } catch (error) {
      console.log(error);
      apm.captureError(error);
    }
  }

  public async updateDoc(data, database) {
    try {
      const nano = this.nanoInstance;
      const db = nano.use(database);
      const result = await db.get(data.id);
      await db.insert({
        ...data,
        _rev: result._rev,
      });
    } catch (error) {
      console.log(error);
      apm.captureError(error);
    }
  }

  public async getDoc(id: string, database: string) {
    try {
      const nano = this.nanoInstance;
      const db = nano.use(database);
      const result = await db.get(id);
      return result;
    } catch (error) {
      console.log(error);
      apm.captureError(error);
      return null;
    }
  }

  public async totalTodayTransactions(database = 'transaction') {
    try {
      const nano = this.nanoInstance;
      const db = nano.use<any>(database);

      // Get today's start timestamp (midnight)
      const today = new Date();
      today.setHours(0, 0, 0, 0);
      const todayTimestamp = today.getTime();

      // Query for documents created today
      const selector = {
        created_at: {
          $gte: todayTimestamp,
        },
      };

      const result = await db.find({
        selector: selector,
        fields: ['_id', 'payment_total_pay'],
        limit: 10000,
      });

      return result.docs.reduce(
        (sum, doc) => sum + (doc.payment_total_pay || 0),
        0,
      );
    } catch (error) {
      console.log(error);
      apm.captureError(error);
      return 0;
    }
  }

  getUnixTimestampLast7Days() {
    const date = new Date();
    date.setDate(date.getDate() - 4);
    date.setHours(0, 0, 0, 0);
    return date.getTime();
  }

  public async clearTransactions() {
    const nano = this.nanoInstance;
    const transaction = nano.use('transaction');

    const expiredDate = this.getUnixTimestampLast7Days();

    const selectorPayment = {
      created_at: {
        $lt: expiredDate,
      },
    };

    const transactions = await transaction.find({
      selector: selectorPayment,
      fields: ['_id', '_rev'],
      limit: 100000,
    });

    const { docs } = transactions;
    console.log(docs.length);
    const deletedDocs = {
      docs: docs.map((doc) => ({
        _id: doc._id,
        _rev: doc._rev,
        _deleted: true,
      })),
    };
    await transaction.bulk(deletedDocs);
  }
}
