import { MatDialogRef } from '@angular/material/dialog';
import { EquipmentLruUploadComponent } from '../equipment-lru-upload/equipment-lru-upload.component';
import { Component, ElementRef, Inject, OnInit, ViewChild } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { MatTableDataSource } from '@angular/material/table';
import { FunctionsService } from '../../../services/functions.service';
import { TempFileInfo } from '../../../interfaces/temp-file-info.interface';
import { ResourcesService } from '../../../services/resources.service';
import { lastValueFrom } from 'rxjs';
import { GdelService } from '../../../services/gdel.service';
import { PreviewExcelDocumentComponent } from '../preview-Equipmentexcel-document/preview-excel-document.component';
import * as XLSX from 'xlsx';
import { FormControl, Validators } from '@angular/forms';
import { PreviewDocumentsComponent } from '../preview-documents/preview-documents.component';

const ID_MODULE = "S002V01M15IDUP";

export interface Document {
  nombre_del_archivo: string,
  tamano_del_archivo: string,
  archivo_id?: string,
  tipo_archivo?: string,
  fecha_carga?: string
}

// Interfaz para la estructura de headers esperados
interface SheetHeaderStructure {
  [column: string]: string;
}

interface TemplateStructure {
  [sheetName: string]: SheetHeaderStructure;
}

@Component({
  selector: 'app-multiple-corrective-upload',
  standalone: false,
  templateUrl: './multiple-corrective-upload.component.html',
  styleUrl: './multiple-corrective-upload.component.css'
})
export class MultipleCorrectiveUploadComponent {

  public isLoading: boolean = false;
  public hasError: boolean = false;
  public errorMessage: string = '';
  public isUploading: boolean;
  public uploadedFiles: TempFileInfo[] = []; 
  @ViewChild('file') private uploader?: ElementRef;

  moduleControl = new FormControl('', Validators.required);

  public dataSource: MatTableDataSource<Document>;
  public displayedColumns: string[] = ['nombre_del_archivo', 'tamano_del_archivo', 'acciones'];
  
 
  public excelFiles: Document[] = []; 
  public excelDataMap: Map<string, any> = new Map(); 


  public isDragOver: boolean = false;

 
  public readonly EXCEL_EXTENSIONS = ['xlsx', 'xls'];
  public readonly MAX_FILE_SIZE_MB = 10;
  private readonly HEADER_ROW = 6; 

  private readonly TEMPLATE_STRUCTURE: TemplateStructure = {
    'MANTENIMIENTO CORRECTIVO': {
      'B': 'CÓDIGO EQUIVALENTE',
      'D': 'CÓDIGO DEL EQUIPO',
      'F': 'CÓDIGO EQUIVALENTE',
      'H': 'CÓDIGO DEL LRU',
      'J': 'ESTADO DEL EQUIPO',
      'L': 'ESTADO DEL LRU',
      'N': 'TIEMPO DE INMOVILIZACIÓN ESTIMADO',
      'P': 'DESCRIPCIÓN DE LA INTERVENCIÓN',
      'R': 'SÍNTOMAS DEL EQUIPO',
      'T': 'PRIORIDAD',
      'V': 'CLASIFICACIÓN',
      'X': 'ESPECIALIDAD DEL OPERARIO',
      'Z': 'CANTIDAD DE OPERARIOS PREVISTOS',
      'AB': 'ARTÍCULOS PARA UTILIZAR',
      'AD': 'DETALLES ADICIONALES',
      'AF': 'COMENTARIOS',
    },



  };

  private readonly LISTAS_STRUCTURE = {
    row3: {
      'F': 'CLASIFICACIONES',
      'H': 'ESPECIALIDADES',
      'J': 'FRECUENCIAS DE MANTENIMIENTO',
      'L': 'MESES',
      'N': 'PRIORIDADES',
      'P': 'ESTADO DEL EQUIPO',
    },
    row4: {
      'B': 'MAGNITUD',
      'C': 'UNIDAD',
      'D': 'SÍMBOLO'
    }
  };


  private readonly REQUIRED_SHEETS = ['MANTENIMIENTO CORRECTIVO', 'LISTAS']; 

  constructor (
    private dialogRef: MatDialogRef<EquipmentLruUploadComponent>,
    private _gdelService: GdelService,
    private _matDialog: MatDialog,
    private _functionService: FunctionsService,
    private _resourcesService: ResourcesService,
  ) {
    this.isLoading = false;
    this.isUploading = false;
    this.uploadedFiles = [];
    this.dataSource = new MatTableDataSource();
  }

  ngOnInit(): void {
    this.loadExcelFiles();
  }

  cancelar(): void {
    this.dialogRef.close();
  }

  openFileUploader(): void {
    this.uploader?.nativeElement.click();
  }


  onDragOver(event: DragEvent): void {
    event.preventDefault();
    event.stopPropagation();
    this.isDragOver = true;
  }

  onDragLeave(event: DragEvent): void {
    event.preventDefault();
    event.stopPropagation();
    this.isDragOver = false;
  }

  onDrop(event: DragEvent): void {
    event.preventDefault();
    event.stopPropagation();
    this.isDragOver = false;

    const files = event.dataTransfer?.files;
    if (files && files.length > 0) {
      this.processMultipleFiles(Array.from(files));
    }
  }

  checkFile(event: any): void {
    const files = event.target.files;
    if (!files || files.length === 0) return;
    this.processMultipleFiles(Array.from(files));
    
    event.target.value = '';
  }

private processMultipleFiles(files: File[]): void {

  const validFiles = files.filter(file => this.isValidExcelFile(file) && this.isValidFileSize(file));
  
  if (validFiles.length === 0) {
    return;
  }


  this.isUploading = true;
  this.uploadMultipleExcelFiles(validFiles);
}

  private isValidExcelFile(file: File): boolean {
    const fileName = file.name.toLowerCase();
    const fileExtension = fileName.split('.').pop();
    
    if (!fileExtension || !this.EXCEL_EXTENSIONS.includes(fileExtension)) {
      this._resourcesService.openSnackBar(`Archivo "${file.name}": Solo se permiten archivos Excel (.xlsx, .xls)`);
      return false;
    }
    
    return true;
  }

  private isValidFileSize(file: File): boolean {
    const maxFileSize = this.MAX_FILE_SIZE_MB * 1024 * 1024; 
    
    if (file.size > maxFileSize) {
      this._resourcesService.openSnackBar(`Archivo "${file.name}": El tamaño supera el límite de ${this.MAX_FILE_SIZE_MB} MB`);
      return false;
    }
    
    return true;
  }


private validateExcelTemplate(workbook: XLSX.WorkBook): { isValid: boolean, errors: string[] } {
  const errors: string[] = [];
  

  const existingSheets = workbook.SheetNames;
  const missingSheets = this.REQUIRED_SHEETS.filter(sheet => !existingSheets.includes(sheet));
  
  if (missingSheets.length > 0) {
    errors.push(`Tu documento no cumple con las hojas requeridas de la plantilla`);
  }


  for (const sheetName of this.REQUIRED_SHEETS) {
    if (existingSheets.includes(sheetName)) {
      const sheetErrors = this.validateSheetHeaders(workbook.Sheets[sheetName], sheetName);
      errors.push(...sheetErrors);
      

      if (sheetName === 'MANTENIMIENTO CORRECTIVO') {
        const dataErrors = this.validateSheetHasData(workbook.Sheets[sheetName], sheetName);
        errors.push(...dataErrors);
      }
    }
  }

  return {
    isValid: errors.length === 0,
    errors: errors
  };
}


private validateSheetHasData(worksheet: XLSX.WorkSheet, sheetName: string): string[] {
  const errors: string[] = [];
  
  try {

    const range = XLSX.utils.decode_range(worksheet['!ref'] || 'A1');
    
    
    const dataStartRow = this.HEADER_ROW + 2; 
    

    let hasData = false;
    

    if (range.e.r >= dataStartRow) {

      const mainColumns = ['B', 'D', 'F', 'H']; 
      
      for (let row = dataStartRow; row <= range.e.r; row++) {
        for (const col of mainColumns) {
          const cellAddress = `${col}${row}`;
          const cell = worksheet[cellAddress];
          

          if (cell && cell.v !== null && cell.v !== undefined && String(cell.v).trim() !== '') {
            hasData = true;
            break;
          }
        }
        if (hasData) break;
      }
    }
    
    if (!hasData) {
      errors.push(`La hoja "${sheetName}" no contiene datos. Se requiere al menos un registro con información.`);
    }
    
  } catch (error) {
    errors.push(`Error al validar datos en la hoja "${sheetName}": No se pudo verificar el contenido.`);
  }
  
  return errors;
}


  private validateSheetHeaders(worksheet: XLSX.WorkSheet, sheetName: string): string[] {
    const errors: string[] = [];
    

    if (sheetName === 'LISTAS') {
      return this.validateListasSheet(worksheet);
    }
    
    const expectedHeaders = this.TEMPLATE_STRUCTURE[sheetName];
    
    if (!expectedHeaders) {
      errors.push(`No se encontró la estructura esperada para la hoja: ${sheetName}`);
      return errors;
    }


    const range = XLSX.utils.decode_range(worksheet['!ref'] || 'A1');
    
    for (const [column, expectedHeader] of Object.entries(expectedHeaders)) {
      const cellAddress = `${column}${this.HEADER_ROW}`;
      const cell = worksheet[cellAddress];
      const actualHeader = cell ? String(cell.v).trim() : '';
      
      if (actualHeader !== expectedHeader) {
        errors.push(`Hoja "${sheetName}", Columna ${column}: Se esperaba "${expectedHeader}" pero se encontró "${actualHeader}"`);
      }
    }

    return errors;
  }


  private validateListasSheet(worksheet: XLSX.WorkSheet): string[] {
    const errors: string[] = [];


    for (const [column, expectedHeader] of Object.entries(this.LISTAS_STRUCTURE.row3)) {
      const cellAddress = `${column}3`;
      const cell = worksheet[cellAddress];
      const actualHeader = cell ? String(cell.v).trim() : '';
      
      if (actualHeader !== expectedHeader) {
        errors.push(`Hoja "LISTAS", Fila 3, Columna ${column}: Se esperaba "${expectedHeader}" pero se encontró "${actualHeader}"`);
      }
    }


    for (const [column, expectedHeader] of Object.entries(this.LISTAS_STRUCTURE.row4)) {
      const cellAddress = `${column}4`;
      const cell = worksheet[cellAddress];
      const actualHeader = cell ? String(cell.v).trim() : '';
      
      if (actualHeader !== expectedHeader) {
        errors.push(`Hoja "LISTAS", Fila 4, Columna ${column}: Se esperaba "${expectedHeader}" pero se encontró "${actualHeader}"`);
      }
    }

    return errors;
  }


  private async validateFileStructure(file: File): Promise<{ isValid: boolean, errors: string[] }> {
    return new Promise((resolve, reject) => {
      const reader = new FileReader();
      reader.onload = (e: any) => {
        try {
          const data = new Uint8Array(e.target.result);
          const workbook = XLSX.read(data, { type: 'array' });
          
          const validation = this.validateExcelTemplate(workbook);
          resolve(validation);
        } catch (error) {
          reject(error);
        }
      };
      reader.onerror = reject;
      reader.readAsArrayBuffer(file);
    });
  }  

async uploadMultipleExcelFiles(files: File[]): Promise<void> {
  try {
    const idUser = localStorage.getItem('idusuario');
    const uploadPromises: Promise<any>[] = [];

    for (const file of files) {

      const validationResult = await this.validateFileStructure(file);
      
      if (!validationResult.isValid) {

        this._resourcesService.openSnackBar(`Archivo "${file.name}" - Plantilla inválida: ${validationResult.errors.join('. ')}`);
        continue; 
      }


      const formData = new FormData();
      formData.append('file', file);
      formData.append('id_user', idUser!);
      formData.append('linea', "1");
      formData.append('tipo_archivo', 'excel');

      uploadPromises.push(
        lastValueFrom(this._gdelService.uploadTempFile(formData)).then(async (response) => {
          if (response.error) {
            throw new Error(`${file.name}: ${response.msg}`);
          } else {

            const tempFileInfo: TempFileInfo = {
              id: response.response.idArchivo,
              name: file.name,
              size: this.formatBytes(file.size)
            };

            this.uploadedFiles.push(tempFileInfo);


            await this.readExcelFile(file, response.response.idArchivo);
            
            const newDocument: Document = {
              nombre_del_archivo: file.name,
              tamano_del_archivo: this.formatBytes(file.size),
              archivo_id: response.response.idArchivo,
              tipo_archivo: 'excel',
              fecha_carga: new Date().toLocaleString()
            };

            this.excelFiles.push(newDocument);
            return { success: true, fileName: file.name };
          }
        })
      );
    }


    if (uploadPromises.length === 0) {
      this._resourcesService.openSnackBar('El archivo debe de contener por lo menos un registro y cumplir con la estructura requerida ');
      this.isUploading = false;
      return;
    }

 
    const results = await Promise.allSettled(uploadPromises);
    

    const successful = results.filter(result => result.status === 'fulfilled').length;
    const failed = results.filter(result => result.status === 'rejected');

    if (successful > 0) {
      this.updateTableData();
      this._resourcesService.openSnackBar(
        `${successful} archivo${successful > 1 ? 's' : ''} cargado${successful > 1 ? 's' : ''} exitosamente`
      );
    }

    if (failed.length > 0) {
      const errorMessages = failed.map(result => 
        result.status === 'rejected' ? result.reason.message : 'Error desconocido'
      );
      this._resourcesService.openSnackBar(`Errores: ${errorMessages.join('; ')}`);
    }

    this.isUploading = false;
  } catch (error: any) {
    this.handleUploadError(error);
    this.isUploading = false;
  }
}

  private async readExcelFile(file: File, fileId: any): Promise<void> {
    return new Promise((resolve, reject) => {
      const reader = new FileReader();
      reader.onload = (e: any) => {
        try {
          const data = new Uint8Array(e.target.result);
          const workbook = XLSX.read(data, { type: 'array' });
          

          const sheetNames = workbook.SheetNames;
          const sheetsData: any = {};
          
          sheetNames.forEach(sheetName => {
            const worksheet = workbook.Sheets[sheetName];
            sheetsData[sheetName] = XLSX.utils.sheet_to_json(worksheet, { header: 1 });
          });

          
          this.excelDataMap.set(fileId, {
            fileName: file.name,
            sheets: sheetsData,
            sheetNames: sheetNames,
            fileId: fileId
          });

          resolve();
        } catch (error) {
          reject(error);
        }
      };
      reader.onerror = reject;
      reader.readAsArrayBuffer(file);
    });
  }


  async deleteTempFile(fileId: string, fileName: string): Promise<void> {
    try {
      let idUser = localStorage.getItem('idusuario');
      let formData = new FormData();

      formData.append('id_user', idUser!);
      formData.append('id_file', fileId);
      formData.append('linea', '1');

      await lastValueFrom(this._gdelService.deleteTempFile(formData));


      this.uploadedFiles = this.uploadedFiles.filter(file => file.id !== fileId);
      this.excelFiles = this.excelFiles.filter(file => file.archivo_id !== fileId);
      this.excelDataMap.delete(fileId);
      
      this.updateTableData();
      this._resourcesService.openSnackBar(`Archivo "${fileName}" eliminado exitosamente`);
    } catch (error: any) {
      this.handleUploadError(error);
    }
  }


  async deleteAllTempFiles(): Promise<void> {
    if (this.uploadedFiles.length === 0) return;

    try {
      this.isLoading = true;
      const deletePromises = this.uploadedFiles.map(file => 
        this.deleteTempFileById(file.id)
      );

      await Promise.all(deletePromises);
      

      this.uploadedFiles = [];
      this.excelFiles = [];
      this.excelDataMap.clear();
      
      this.updateTableData();
      this._resourcesService.openSnackBar('Todos los archivos eliminados exitosamente');
    } catch (error: any) {
      this.handleUploadError(error);
    } finally {
      this.isLoading = false;
    }
  }

  private async deleteTempFileById(fileId: string): Promise<void> {
    let idUser = localStorage.getItem('idusuario');
    let formData = new FormData();

    formData.append('id_user', idUser!);
    formData.append('id_file', fileId);
    formData.append('linea', '1');

    return lastValueFrom(this._gdelService.deleteTempFile(formData));
  }

  formatBytes(bytes: number, decimals = 2): string {
    if (bytes === 0) return '0 Bytes';

    const k = 1024;
    const dm = decimals < 0 ? 0 : decimals;
    const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];

    const i = Math.floor(Math.log(bytes) / Math.log(k));

    return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
  }

  private updateTableData(): void {
    this.dataSource.data = [...this.excelFiles];
  }

  private loadExcelFiles(): void {

    this.excelFiles = [];
    this.uploadedFiles = [];
    this.excelDataMap.clear();
    this.updateTableData();
  }

  private handleUploadError(error: any): void {
    if (error.error == undefined) {
      this._resourcesService.openSnackBar('Ocurrió un error inesperado.');
    } else if (error.error.msg == undefined) {
      this._resourcesService.openSnackBar('Ocurrió un error inesperado.');
    } else {
      this._resourcesService.openSnackBar(error.error.msg);
    }
  }


  previewExcelFile(fileId: string): void {
    const excelData = this.excelDataMap.get(fileId);
    
    if (!excelData) {
      this._resourcesService.openSnackBar('No hay datos del archivo Excel para mostrar');
      return;
    }

    const dialogRef = this._matDialog.open(PreviewDocumentsComponent, {
      width: '1800px',
      height: '900px',
      maxWidth: '1800px',
      data: excelData,
      disableClose: true
    });

    dialogRef.afterClosed().subscribe(result => {
      if (result) {
        console.log('Preview cerrado:', result);
      }
    });
  }


  removeExcelFile(fileId: string, fileName: string): void {
    this.deleteTempFile(fileId, fileName);
  }


  get hasExcelFiles(): boolean {
    return this.excelFiles.length > 0;
  }

  get fileCount(): number {
    return this.excelFiles.length;
  }

  get totalSize(): string {
    const totalBytes = this.excelFiles.reduce((sum, file) => {

      const sizeStr = file.tamano_del_archivo;
      const match = sizeStr.match(/^([\d.]+)\s*(\w+)$/);
      if (match) {
        const value = parseFloat(match[1]);
        const unit = match[2].toLowerCase();
        const multipliers: { [key: string]: number } = {
          'bytes': 1,
          'kb': 1024,
          'mb': 1024 * 1024,
          'gb': 1024 * 1024 * 1024
        };
        return sum + (value * (multipliers[unit] || 1));
      }
      return sum;
    }, 0);
    
    return this.formatBytes(totalBytes);
  }


  async saveFinalFiles(): Promise<void> {
    if (this.uploadedFiles.length === 0) {
      this._resourcesService.openSnackBar('No hay archivos cargados para guardar');
      return;
    }

    try {
      this.isLoading = true;
      let idUser = localStorage.getItem('idusuario');
      const savePromises: Promise<any>[] = [];

      for (const file of this.uploadedFiles) {
        let formData = new FormData();

        formData.append('id_user', idUser!);
        formData.append('id_file', file.id);
        formData.append('linea', '1');
        

        formData.append('module', 'ADSI');
        formData.append('clasification', 'LA');
        formData.append('has_order', 'N');

        savePromises.push(
          lastValueFrom(this._gdelService.saveFinalFile(formData)).then(response => {
            if (response.error) {
              throw new Error(`${file.name}: ${response.msg || 'Error al guardar'}`);
            }
            return { success: true, fileName: file.name };
          })
        );
      }

      const results = await Promise.allSettled(savePromises);
      

      const successful = results.filter(result => result.status === 'fulfilled').length;
      const failed = results.filter(result => result.status === 'rejected');

      if (successful > 0) {
        this._resourcesService.openSnackBar(
          `${successful} archivo${successful > 1 ? 's' : ''} guardado${successful > 1 ? 's' : ''} correctamente`
        );
      }

      if (failed.length > 0) {
        const errorMessages = failed.map(result => 
          result.status === 'rejected' ? result.reason.message : 'Error desconocido'
        );
        this._resourcesService.openSnackBar(`Errores al guardar: ${errorMessages.join('; ')}`);
      }

      if (successful === this.uploadedFiles.length) {
        // Todos los archivos se guardaron correctamente
        this.dialogRef.close(true);
      }

    } catch (error: any) {
      console.error('Error al guardar archivos:', error);
      this._resourcesService.openSnackBar('Error general al guardar los archivos');
    } finally {
      this.isLoading = false;
    }
  }
}