import { Component, computed, inject, OnInit, signal } from '@angular/core';

import { ModalService } from '../../../../../../core/services/modal.service';
import { ToastService } from '../../../../../../core/services/toast.service';
import { IconButtonComponent } from '../../../../../../shared/components/icon-button/icon-button.component';
import { MenuContentSectionComponent } from '../../../../components/menu-content-section/menu-content-section.component';
import { TicketComponent } from './components/ticket/ticket.component';
import { TicketResponse, TicketService } from './ticket.service';

@Component({
  selector: 'app-tickets-page',
  imports: [IconButtonComponent, TicketComponent, MenuContentSectionComponent],
  providers: [TicketService],
  templateUrl: './tickets-page.html',
  styleUrl: './tickets-page.scss',
})
export class TicketsPage implements OnInit {
  private readonly ticketService = inject(TicketService);
  private readonly toastService = inject(ToastService);
  private readonly modalService = inject(ModalService);

  protected readonly tickets = signal<TicketResponse[]>([]);
  protected readonly activeTickets = computed(() =>
    this.tickets().filter((ticket) => !this.isInactive(ticket)),
  );
  protected readonly inactiveTickets = computed(() =>
    this.tickets().filter((ticket) => this.isInactive(ticket)),
  );
  protected readonly selectedIds = signal<Set<number>>(new Set());
  protected readonly isLoading = signal(true);
  protected readonly isGeneratingPdf = signal(false);
  protected readonly allSelected = computed(
    () =>
      this.activeTickets().length > 0 && this.selectedIds().size === this.activeTickets().length,
  );

  async ngOnInit(): Promise<void> {
    try {
      this.tickets.set(await this.ticketService.withCustomLoading().getTickets());
    } catch {
      this.toastService.danger('Hubo un error al cargar los tickets');
    } finally {
      this.isLoading.set(false);
    }
  }

  protected toggleAll(selected: boolean): void {
    this.selectedIds.set(
      selected ? new Set(this.activeTickets().map((ticket) => ticket.id)) : new Set(),
    );
  }

  protected toggleTicket(ticketId: number, selected: boolean): void {
    this.selectedIds.update((current) => {
      const next = new Set(current);
      selected ? next.add(ticketId) : next.delete(ticketId);
      return next;
    });
  }

  protected isSelected(ticketId: number): boolean {
    return this.selectedIds().has(ticketId);
  }

  protected isInactive(ticket: TicketResponse): boolean {
    return ticket.is_expired || ticket.is_used;
  }

  protected formatDate(ticket: TicketResponse): string | null {
    const value = ticket.starts_at ?? ticket.expires_at;
    if (!value) return null;

    const date = new Date(value);
    if (Number.isNaN(date.getTime())) return null;

    return new Intl.DateTimeFormat('es-AR', {
      day: '2-digit',
      month: 'long',
      timeZone: 'UTC',
    }).format(date);
  }

  protected viewQr(ticket: TicketResponse): void {
    this.modalService.openQr({
      title: ticket.name,
      id: ticket.id,
      date: this.formatDate(ticket) ?? '',
      ticket: ticket.ticket,
    });
  }

  protected async downloadTicket(ticket: TicketResponse): Promise<void> {
    await this.downloadTickets([ticket]);
  }

  protected async downloadSelected(): Promise<void> {
    const tickets = this.activeTickets().filter((ticket) => this.selectedIds().has(ticket.id));

    if (!tickets.length) {
      this.toastService.danger('Seleccioná al menos un ticket para descargar.');
      return;
    }

    await this.downloadTickets(tickets);
  }

  protected async shareTicket(ticket: TicketResponse): Promise<void> {
    await this.shareTickets([ticket]);
  }

  protected async shareSelected(): Promise<void> {
    const tickets = this.activeTickets().filter((ticket) => this.selectedIds().has(ticket.id));

    if (!tickets.length) {
      this.toastService.danger('Seleccioná al menos un ticket para compartir.');
      return;
    }

    await this.shareTickets(tickets);
  }

  private async downloadTickets(tickets: TicketResponse[]): Promise<void> {
    const pdf = await this.generateTicketsPdf(tickets);
    if (pdf) this.downloadPdf(pdf, this.pdfFilename(tickets));
  }

  private async shareTickets(tickets: TicketResponse[]): Promise<void> {
    const pdf = await this.generateTicketsPdf(tickets);
    if (!pdf) return;

    try {
      const filename = this.pdfFilename(tickets);
      const file = new File([pdf], filename, { type: 'application/pdf' });

      if (navigator.canShare?.({ files: [file] })) {
        await navigator.share({ files: [file] });
        return;
      }

      this.downloadPdf(pdf, filename);
      this.toastService.success('Tu navegador no permite compartir archivos; descargamos el PDF.');
    } catch (error) {
      if (error instanceof DOMException && error.name === 'AbortError') return;

      this.toastService.danger('No se pudo compartir el PDF de los tickets.');
    }
  }

  private async generateTicketsPdf(tickets: TicketResponse[]): Promise<Blob | null> {
    if (this.isGeneratingPdf()) return null;

    this.isGeneratingPdf.set(true);

    try {
      return await this.ticketService
        .withCustomLoading()
        .downloadPdf(tickets.map((ticket) => ticket.id));
    } catch {
      this.toastService.danger('No se pudo generar el PDF de los tickets.');
      return null;
    } finally {
      this.isGeneratingPdf.set(false);
    }
  }

  private pdfFilename(tickets: TicketResponse[]): string {
    return `tickets_${tickets.map((ticket) => ticket.id).join('_')}.pdf`;
  }

  private downloadPdf(pdf: Blob, filename: string): void {
    const url = URL.createObjectURL(pdf);
    const link = document.createElement('a');
    link.href = url;
    link.download = filename;
    document.body.appendChild(link);
    link.click();
    link.remove();
    window.setTimeout(() => URL.revokeObjectURL(url), 0);
  }
}
