import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { ErrorService } from '../../error/error.service';
import {
  applyFiltersToBuilder,
  applySortingToBuilder,
  paginationResult,
} from '../../utils';
import { Repository } from 'typeorm';
import { ContactEntity } from './entities/contact.entity';
import { UpdateContactDto } from './dto/update-contact.dto';
import { ReplyContactDto, SenderType } from './dto/reply-contact.dto';
import { SmsService } from '../../sms/sms.service';
import { MailService } from '../../mail/mail.service';

@Injectable()
export class ContactService {
  constructor(
    @InjectRepository(ContactEntity)
    private contactRepository: Repository<ContactEntity>,
    private error: ErrorService,
    private sms: SmsService,
    private mail: MailService,
  ) {}

  /**
   * -------------------------------------------------------
   */
  async getAll(page = 1, limit = 20, filters = null, isAnswred, sorts = null) {
    let builder = this.contactRepository
      .createQueryBuilder('contact')
      .select([
        'contact.id',
        'contact.name',
        'contact.subject',
        'contact.mobile',
        'contact.createdAt',
      ])
      .take(limit)
      .skip((page - 1) * limit);

    builder.addSelect('contact.answer IS NOT NULL', 'isAnswered');
    builder.addSelect('contact.isReade', 'isReaded');

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

    builder = applyFiltersToBuilder(builder, filters);

    // Custom condition
    if (isAnswred === '1') {
      builder.andWhere('contact.answer IS NOT NULL');
    } else if (isAnswred === '0') {
      builder.andWhere('contact.answer IS NULL');
    }

    const [items, totalItems] = await builder.getManyAndCount();

    return {
      items,
      pagination: paginationResult(page, limit, totalItems),
    };
  }

  /**
   * -------------------------------------------------------
   */
  async getById(id: number) {
    return await this.contactRepository
      .createQueryBuilder('contact')
      .where({ id })
      .addSelect('contact.answer IS NOT NULL', 'isAnswered')
      .addSelect('contact.isReade', 'isReaded')
      .getOne();
  }

  /**
   * -------------------------------------------------------
   */
  async getRelatedById(id: number) {
    const contact = await this.getById(id);
    if (!contact) {
      this.error.unprocessableEntity(['پیام مورد نظر یافت نشد']);
    }

    const builder = this.contactRepository.createQueryBuilder('contact');

    const orWhere = [];
    if (contact.email) {
      orWhere.push('contact.email = :email');
    }
    if (contact.mobile) {
      orWhere.push('contact.mobile = :mobile');
    }

    if (orWhere.length > 0) {
      builder.orWhere(`(${orWhere.join(' OR ')})`, {
        email: contact.email,
        mobile: contact.mobile,
      });
    }

    builder.andWhere('contact.id != :id', { id });
    const items = await builder.getMany();
    return { items };
  }

  /**
   * -------------------------------------------------------
   */
  async statistic() {
    const read = await this.contactRepository.count({ isReade: true });
    const unRead = await this.contactRepository.count({ isReade: false });
    const noAnswer = await this.contactRepository.count({ answer: null });

    return {
      read,
      unRead,
      noAnswer,
      total: read + unRead,
    };
  }

  /**
   * -------------------------------------------------------
   */
  async updateById(id: number, dto: UpdateContactDto) {
    return await this._update(id, { isReade: dto.isReaded });
  }

  /**
   * -------------------------------------------------------
   */
  async replyById(id: number, dto: ReplyContactDto) {
    const contact = await this.getById(id);

    if (!contact) {
      this.error.unprocessableEntity(['شناسه پیام ورودی یافت نشد']);
    }
    if (dto.senderType === SenderType.SMS && !contact.mobile) {
      this.error.unprocessableEntity([
        'امکان ارسال پاسخ از طریق SMS وجود ندارد به این دلیل که کاربر شماره موبایلی برای این پیام ثبت نکرده است',
      ]);
    }
    if (dto.senderType === SenderType.MAIL && !contact.email) {
      this.error.unprocessableEntity([
        'امکان ارسال پاسخ از طریق ایمیل وجود ندارد به این دلیل که کاربر ایمیلی برای این پیام ثبت نکرده است',
      ]);
    }

    await this._update(id, { answer: dto.answer });

    // Sending the answer via sms
    if (
      dto.senderType === SenderType.SMS ||
      (dto.senderType === SenderType.ALL && contact.mobile)
    ) {
      await this.sms.send(contact.mobile, dto.answer.replace(/<[^>]+>/g, ''));
    }

    // Sending the answer via email
    if (
      dto.senderType === SenderType.MAIL ||
      (dto.senderType === SenderType.ALL && contact.email)
    ) {
      await this.mail.sendReplyMessage(
        contact.email,
        contact.message,
        dto.answer,
      );
    }

    return true;
  }

  /**
   * -------------------------------------------------------
   */
  private async _update(id: number, values: any) {
    return await this.contactRepository
      .createQueryBuilder()
      .update()
      .set({ ...values, updatedAt: new Date() })
      .where({ id })
      .execute();
  }
}
