import { ChangeDetectorRef, Component, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable, forkJoin } from 'rxjs';
import { FormControl } from '@angular/forms';
import { map, startWith } from 'rxjs/operators';
import { DatePipe } from '@angular/common';
import { JoyrideService } from 'ngx-joyride';
import { MatDialog } from "@angular/material/dialog";
import { InputDialogComponent } from "./input-dialog/input-dialog.component";
import { ProgressBarMode } from '@angular/material/progress-bar';
import { DeleteModalComponent } from "../../../../shared/delete_modal/delete-modal/delete-modal.component";
import { ToastrService } from "ngx-toastr";
import { ConfigService } from '@services/config.service';

@Component({
  selector: 'app-task-logs',
  templateUrl: './task-logs.component.html',
  styleUrls: ['./task-logs.component.css']
})
export class TaskLogsComponent implements OnInit {
  baseUrl: string;
  mercureUrl: string;
  traces: any[] = [];
  groupedTraces: any[] = [];
  commands: any[] = [];
  clients: any[] = [];
  length: number = 0;
  itemsPerPage: number = 20;
  page: number = 0;
  loading: boolean = true;
  pageSizeOptions: number[] = [10, 20, 30, 50];
  datePipe: DatePipe = new DatePipe('es-ES');
  mode: ProgressBarMode = 'buffer';
  progress = 0;
  bufferValue = 0;

  columns = [
    {
      columnDef: 'id',
      header: 'ID',
      cell: (trace: any) => `${trace.id}`,
    },
    {
      columnDef: 'command',
      header: 'Comando',
      cell: (trace: any) => `${trace.command}`
    },
    {
      columnDef: 'client',
      header: 'Client',
      cell: (trace: any) => `${trace.client?.name}`
    },
    {
      columnDef: 'status',
      header: 'Estado',
      cell: (trace: any) => `${trace.status}`
    },
    {
      columnDef: 'jobId',
      header: 'Hilo de trabajo',
      cell: (trace: any) => `${trace.jobId}`
    },
    {
      columnDef: 'input',
      header: 'Input',
      cell: (trace: any) => `${trace.input}`
    },
    {
      columnDef: 'output',
      header: 'Logs',
      cell: (trace: any) => `${trace.output}`
    },
    {
      columnDef: 'executedAt',
      header: 'Programación de ejecución',
      cell: (trace: any) => `${this.datePipe.transform(trace.executedAt, 'dd/MM/yyyy hh:mm:ss')}`,
    },
    {
      columnDef: 'finishedAt',
      header: 'Finalización',
      cell: (trace: any) => `${this.datePipe.transform(trace.finishedAt, 'dd/MM/yyyy hh:mm:ss')}`,
    },
  ];
  displayedColumns = [...this.columns.map(column => column.columnDef)];

  filters: { [key: string]: string } = {};
  filteredClients!: Observable<any[]>;
  clientControl = new FormControl();
  filteredCommands!: Observable<any[]>;
  commandControl = new FormControl();

  constructor(private http: HttpClient,
    private joyrideService: JoyrideService,
    private dialog: MatDialog,
    private cdr: ChangeDetectorRef,
    private configService: ConfigService,
    private toastService: ToastrService
  ) {
    this.baseUrl = this.configService.apiUrl;
    this.mercureUrl = this.configService.mercureUrl;
  }

  ngOnInit(): void {
    this.loadTraces();
    this.loadCommands();
    //this.loadClients();
    this.filteredCommands = this.commandControl.valueChanges.pipe(
      startWith(''),
      map(value => (typeof value === 'string' ? value : value?.name)),
      map(name => (name ? this._filterCommands(name) : this.commands.slice()))
    );
    this.filteredClients = this.clientControl.valueChanges.pipe(
      startWith(''),
      map(value => (typeof value === 'string' ? value : value?.name)),
      map(name => (name ? this._filterClients(name) : this.clients.slice()))
    );

    const eventSource = new EventSource(`${this.mercureUrl}?topic=`
      + encodeURIComponent(`traces`));

    eventSource.onmessage = (event) => {
      const data = JSON.parse(event.data);
      if (data && data['@id']) {
        this.updateTracesStatus(data['@id'], data.status, data.progress);
      }
    }
  }

  private updateTracesStatus(clientUuid: string, newStatus: string, progress: Number): void {
    const traceIndex = this.traces.findIndex(trace => trace['@id'] === clientUuid);
    if (traceIndex !== -1) {
      const updatedTraces = [...this.traces];

      updatedTraces[traceIndex] = {
        ...updatedTraces[traceIndex],
        status: newStatus,
        progress: progress
      };

      this.traces = updatedTraces;
      this.cdr.detectChanges();

      console.log(`Estado actualizado para la traza ${clientUuid}: ${newStatus}`);
    } else {
      console.warn(`Traza con UUID ${clientUuid} no encontrado en la lista.`);
    }
  }


  private _filterClients(name: string): any[] {
    const filterValue = name.toLowerCase();
    return this.clients.filter(client => client.name.toLowerCase().includes(filterValue));
  }

  private _filterCommands(name: string): any[] {
    const filterValue = name.toLowerCase();
    return this.commands.filter(command => command.name.toLowerCase().includes(filterValue));
  }

  displayFnClient(client: any): string {
    return client && client.name ? client.name : '';
  }

  displayFnCommand(command: any): string {
    return command && command.name ? command.name : '';
  }

  onOptionCommandSelected(selectedCommand: any): void {
    this.filters['command.id'] = selectedCommand.id;
    this.loadTraces();
  }

  onOptionClientSelected(selectedClient: any): void {
    this.filters['client.id'] = selectedClient.id;
    this.loadTraces();
  }

  openInputModal(inputData: any): void {
    this.dialog.open(InputDialogComponent, {
      width: '700px',
      data: { input: inputData }
    });
  }

  cancelTrace(trace: any): void {
    this.dialog.open(DeleteModalComponent, {
      width: '300px',
      data: { name: trace.jobId },
    }).afterClosed().subscribe((result) => {
      if (result) {
        this.http.post(`${this.baseUrl}/traces/server/${trace.uuid}/cancel`, {}).subscribe({
          next: () => {
            this.toastService.success('Transmision de imagen cancelada');
            this.loadTraces();
          },
          error: (error) => {
            this.toastService.error(error.error['hydra:description']);
            console.error(error.error['hydra:description']);
          }
        });
      }
    });
  }

  loadTraces(): void {
    this.loading = true;
    const url = `${this.baseUrl}/traces?page=${this.page + 1}&itemsPerPage=${this.itemsPerPage}`;
    const params = { ...this.filters };
    if (params['status'] === undefined) {
      delete params['status'];
    }
    this.http.get<any>(url, { params }).subscribe(
      (data) => {
        this.traces = data['hydra:member'];
        this.length = data['hydra:totalItems'];
        this.groupedTraces = this.groupByCommandId(this.traces);
        this.loading = false;
      },
      (error) => {
        console.error('Error fetching traces', error);
        this.loading = false;
      }
    );
  }

  loadCommands() {
    this.loading = true;
    this.http.get<any>(`${this.baseUrl}/commands?&page=1&itemsPerPage=10000`).subscribe(
      response => {
        this.commands = response['hydra:member'];
        console.log(this.commands);
        this.loading = false;
      },
      error => {
        console.error('Error fetching commands:', error);
        this.loading = false;
      }
    );
  }

  loadClients() {
    this.loading = true;
    this.http.get<any>(`${this.baseUrl}/clients?&page=1&itemsPerPage=10000`).subscribe(
      response => {
        const clientIds = response['hydra:member'].map((client: any) => client['@id']);
        const clientDetailsRequests: Observable<any>[] = clientIds.map((id: string) => this.http.get<any>(`${this.baseUrl}${id}`));
        forkJoin(clientDetailsRequests).subscribe(
          (clients: any[]) => {
            this.clients = clients;
            this.loading = false;
          },
          (error: any) => {
            console.error('Error fetching client details:', error);
            this.loading = false;
          }
        );
      },
      (error: any) => {
        console.error('Error fetching clients:', error);
        this.loading = false;
      }
    );
  }

  resetFilters() {
    this.loading = true;
    this.filters = {};
    this.loadTraces();
  }

  groupByCommandId(traces: any[]): any[] {
    const grouped: { [key: string]: any[] } = {};

    traces.forEach(trace => {
      const commandId = trace.command.id;
      if (!grouped[commandId]) {
        grouped[commandId] = [];
      }
      grouped[commandId].push(trace);
    });

    return Object.keys(grouped).map(key => ({
      commandId: key,
      traces: grouped[key]
    }));
  }

  onPageChange(event: any): void {
    this.page = event.pageIndex;
    this.itemsPerPage = event.pageSize;
    this.length = event.length;
    this.loadTraces();
  }

  iniciarTour(): void {
    this.joyrideService.startTour({
      steps: [
        'titleStep',
        'resetFiltersStep',
        'clientSelectStep',
        'commandSelectStep',
        'tableStep',
        'paginationStep'
      ],
      showPrevButton: true,
      themeColor: '#3f51b5'
    });
  }

}
