import { Injectable } from '@nestjs/common';
import { OnEvent } from '@nestjs/event-emitter';
import { Ticket, TicketValidationState, Tour } from '@prisma/client';
import { PubSub } from 'graphql-subscriptions';
import {
  decodeOffsetCursor,
  encodeCursor,
} from 'src/core/base/pagination.util';
import { TicketService } from '../feat-business-objects/entities/ticket/ticket.service';
import { TourService } from '../feat-business-objects/entities/tour/tour.service';
import { TicketLifecycleService } from '../feat-tickets/ticket-lifecycle.service';
import {
  TicketSystemEventTypes,
  TicketSystemValidatedEvent,
} from '../feat-tickets/ticket-system.events';
import { DocumentInfoArgs, TicketFilterArgs } from './ticket-arg.types';
import {
  PaginatedTicketObjectType,
  TicketStateKeyFiguresObjectType,
  TicketStateMetaObjectType,
} from './ticket.object-type';
import { TicketRepository } from './ticket.repository';

@Injectable()
export class TicketSystemService {
  public readonly pubSub = new PubSub();

  @OnEvent(TicketSystemEventTypes.Validated, { async: true })
  notifyOnTicketsValidated(event: TicketSystemValidatedEvent): void {
    this.pubSub.publish('ticketsUpdated', {
      ticketsUpdated: { count: event.count },
    });
  }

  constructor(
    private readonly repository: TicketRepository,
    private readonly ticketService: TicketService,
    private readonly tourService: TourService,
    private readonly lifecycle: TicketLifecycleService,
  ) {}

  async updateDocumentInfo(ticketId: string, documentInfo: DocumentInfoArgs) {
    return this.lifecycle.validateDocumentInfoUpdate(ticketId, documentInfo);
  }

  findById(id: string): Promise<Ticket> {
    return this.ticketService.findById(id);
  }

  findAll(): Promise<Ticket[]> {
    return this.ticketService.findAll() as Promise<Ticket[]>;
  }

  searchTickets(query: string): Promise<Ticket[]> {
    return this.ticketService.search(query);
  }

  async paginateTicketsByState(
    state: TicketValidationState,
    filter: TicketFilterArgs,
    take?: number,
    cursor?: string,
  ): Promise<PaginatedTicketObjectType> {
    const skip = cursor ? decodeOffsetCursor(cursor) : 0;
    const { tickets, total } = await this.ticketService.findByState(
      state,
      filter,
      take,
      skip,
    );

    const prevPage = skip - take < 0 ? 0 : skip - take;
    const nextPage = skip + take < total ? skip + take : skip;

    return {
      nodes: tickets,
      prevCursor: encodeCursor(prevPage),
      endCursor: encodeCursor(nextPage),
      hasNextPage: nextPage !== skip,
      totalCount: total,
    };
  }

  // TODO: obsolete
  async getTicketStateMeta(
    ticketState: TicketValidationState,
  ): Promise<TicketStateMetaObjectType> {
    return this.repository.calculateTicketStateMeta(ticketState);
  }

  // TODO: obsolete
  getTicketStateKeyFigures(
    ticketState: TicketValidationState,
  ): Promise<TicketStateKeyFiguresObjectType> {
    return this.repository.calculateTicketStateKeyFigures(ticketState);
  }

  findToursByTicketId(ticketId: string): Promise<Tour[]> {
    return this.tourService.findByTicketId(ticketId);
  }

  findStatesByTicketId(ticketId: string): Promise<TicketValidationState[]> {
    return this.ticketService
      .findStateHistoryByTicketId(ticketId)
      .then((states) => states.map(({ state }) => state));
  }
}
