import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { ErrorService } from '../../error/error.service';
import {
  applyFiltersToBuilder,
  applySortingToBuilder,
  paginationResult,
} from '../../utils';
import { LogClient, LogEntity } from './entities/log.entity';
import { LogAction, LogInterface } from './log.interface';

@Injectable()
export class LogService {
  constructor(
    @InjectRepository(LogEntity)
    private logRepository: Repository<LogEntity>,
    private error: ErrorService,
  ) {}

  /**
   * -------------------------------------------------------
   *
   */
  async add({
    operatorUserId,
    affectedId,
    type,
    action,
    message,
    client,
    item,
    oldItem,
  }: LogInterface) {
    const newLog = new LogEntity();

    newLog.operatorUserId = operatorUserId || null;
    newLog.affectedId = affectedId || null;
    newLog.type = type;
    newLog.action = action;
    newLog.message = message;
    newLog.client = client || LogClient.panel;
    newLog.createdAt = new Date();
    newLog.updatedAt = new Date();

    if (action === LogAction.update) {
      const diff = {};
      if (item && oldItem) {
        for (const [field, newValue] of Object.entries(item)) {
          if (
            ['createdAt', 'updatedAt', 'previousPassword'].indexOf(field) !== -1
          ) {
            continue;
          }

          const old = oldItem[field];

          if (old != newValue) {
            diff[field] =
              field === 'password'
                ? { old: '***', new: '***' }
                : { old, new: newValue };
          }
        }

        newLog.params = JSON.stringify(diff);
      }
    } else if (item) {
      if (item?.createdAt) delete item.createdAt;
      if (item?.updatedAt) delete item.updatedAt;
      if (item?.password) delete item.password;
      if (item?.previousPassword) delete item.previousPassword;
      newLog.params = JSON.stringify(item);
    }

    try {
      await this.logRepository.save(newLog);
    } catch (e) {
      console.log(e);
    }
  }

  /**
   * -------------------------------------------------------
   *
   */
  async getAll(page = 1, limit = 30, filters = null, sorts = null) {
    let builder = this.logRepository
      .createQueryBuilder('log')
      .leftJoin('log.operatorUser', 'operatorUser')
      // .leftJoin('log.affectedUser', 'affectedUser', `log.type = 'user'`)
      // .leftJoin('log.affectedOrder', 'affectedOrder', `log.type = 'order'`)
      // .leftJoin(
      //   'log.affectedProduct',
      //   'affectedProduct',
      //   `log.type = 'product'`,
      // )
      .select([
        'log.id',
        'log.operatorUserId',
        'log.type',
        'log.action',
        'log.affectedId',
        'log.message',
        'log.client',
        'log.ip',
        'log.createdAt',

        'operatorUser.id',
        'operatorUser.name',
        'operatorUser.surName',
        'operatorUser.mobile',

        // 'affectedUser.id',
        // 'affectedUser.name',
        // 'affectedUser.surName',
        // 'affectedUser.mobile',

        // 'affectedProduct.id',
        // 'affectedProduct.productId',
        // 'affectedProduct.name',
      ]);

    builder.take(limit);
    builder.skip((page - 1) * limit);

    if (sorts) {
      builder = applySortingToBuilder(builder, sorts);
    } else {
      builder.orderBy('log.createdAt', 'DESC');
    }

    builder = applyFiltersToBuilder(builder, filters);

    const [items, totalItems] = await builder.getManyAndCount();
    return {
      items,
      pagination: paginationResult(page, limit, totalItems),
    };
  }

  /**
   * -------------------------------------------------------
   *
   */
  async getById(id: number) {
    const data = await this.logRepository
      .createQueryBuilder('log')
      .leftJoin('log.operatorUser', 'operatorUser')
      .addSelect([
        'operatorUser.id',
        'operatorUser.name',
        'operatorUser.surName',
        'operatorUser.mobile',
      ])
      .where({ id })
      .getOne();

    return { ...data, params: data.params ? JSON.parse(data.params) : null };
  }
}
