import {
  BadRequestException,
  HttpStatus,
  Injectable,
  NotFoundException,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import {
  FilterOperator,
  FilterSuffix,
  paginate,
  PaginateQuery,
} from 'nestjs-paginate';
import { Column } from 'nestjs-paginate/lib/helper';
import AppResponse from 'src/response/app-response';
import { In, IsNull, Not, Repository } from 'typeorm';
import { WebBid } from '../entities/wed-bid.entity';
import { plainToClass } from 'class-transformer';
import { EventEmitter2 } from '@nestjs/event-emitter';
import { extractDomain } from 'src/ultils';
import { Bid } from '../entities/bid.entity';
import { CreateWebBidDto } from '../dto/web-bid/create-web-bid';
import { UpdateWebBidDto } from '../dto/web-bid/update-web-bid';
import { Event } from '../utils/events';

@Injectable()
export class WebBidsService {
  constructor(
    @InjectRepository(WebBid)
    readonly webBidRepo: Repository<WebBid>,
    private eventEmitter: EventEmitter2,
  ) {}

  async index(query: PaginateQuery) {
    const filterableColumns: {
      [key in Column<WebBid> | (string & {})]?:
        | (FilterOperator | FilterSuffix)[]
        | true;
    } = {
      id: true,
      lot_id: true,
      model: true,
      out_price: true,
      raw_data: true,
    };

    query.filter = AppResponse.processFilters(query.filter, filterableColumns);

    const data = await paginate(query, this.webBidRepo, {
      sortableColumns: ['id'],
      searchableColumns: ['id'],
      defaultLimit: 15,
      filterableColumns,
      defaultSortBy: [['id', 'DESC']],
      maxLimit: 100,
      relations: {
        scrap_config: true,
      },
    });

    return AppResponse.toPagination<WebBid>(data, true, WebBid);
  }

  async getDataClient() {
    const data = await this.webBidRepo.find({
      where: {
        active: true,
        url: Not(IsNull()),
        children: { status: 'biding' },
      },
      relations: {
        children: { histories: true, web_bid: true, metadata: true },
      },
      order: {
        children: {
          close_time_ts: 'ASC',
        },
      },
    });

    return data;
  }

  async emitAllBidEvent() {
    const data = await this.getDataClient();

    this.eventEmitter.emit(Event.BIDS_UPDATED, data);
  }

  async emitAccountUpdate(id: WebBid['id']) {
    const data = await this.webBidRepo.findOne({
      where: { id, children: { status: 'biding' } },
      relations: { children: { web_bid: true } },
    });

    this.eventEmitter.emit(Event.WEB_UPDATED, data || null);
  }

  async createByUrl(url: string) {
    const originUrl = extractDomain(url);

    let webBid = await this.webBidRepo.findOne({
      where: { origin_url: originUrl },
    });

    if (!webBid) {
      await this.webBidRepo.insert({ origin_url: originUrl });
      webBid = await this.webBidRepo.findOne({
        where: { origin_url: originUrl },
      });
    }

    return webBid;
  }

  async create(data: CreateWebBidDto) {
    const prev = await this.webBidRepo.findOne({
      where: { origin_url: data.origin_url },
    });

    if (prev) {
      throw new BadRequestException(
        AppResponse.toResponse(null, { message: 'Web is already exits' }),
      );
    }

    const result = await this.webBidRepo.save(data);

    if (!result) throw new BadRequestException(null);

    this.emitAllBidEvent();

    return AppResponse.toResponse(plainToClass(WebBid, result));
  }

  async update(id: WebBid['id'], data: UpdateWebBidDto) {
    const prev = await this.webBidRepo.findOne({
      where: { origin_url: data.origin_url },
    });

    if (!prev) {
      throw new NotFoundException(
        AppResponse.toResponse(false, {
          message: 'Web not found',
          status_code: HttpStatus.NOT_FOUND,
        }),
      );
    }

    const result = await this.webBidRepo.update(id, data);

    if (!result) throw new BadRequestException(false);

    if (data.password !== prev.password || data.username !== prev.username) {
      this.emitAccountUpdate(prev.id);
    }
    this.emitAllBidEvent();

    return AppResponse.toResponse(true);
  }

  async delete(id: WebBid['id']) {
    const webBid = await this.webBidRepo.findOne({ where: { id } });

    if (!webBid)
      throw new NotFoundException(
        AppResponse.toResponse(false, {
          message: 'Web is not found',
          status_code: HttpStatus.NOT_FOUND,
        }),
      );

    await this.webBidRepo.delete({ id: webBid.id });

    if (webBid.active) {
      this.emitAllBidEvent();
    }

    return AppResponse.toResponse(true, { message: 'Delete success !' });
  }

  async deletes(ids: Bid['id'][]) {
    const result = await this.webBidRepo.delete({
      id: In(ids),
    });

    if (!result.affected) {
      throw new BadRequestException(
        AppResponse.toResponse(false, {
          message: 'No items have been deleted yet.',
          status_code: HttpStatus.BAD_REQUEST,
        }),
      );
    }

    this.emitAllBidEvent();

    return AppResponse.toResponse(true, { message: 'Delete success !' });
  }
}
