import { Component, OnInit, ViewChild, ElementRef, inject } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { IonicModule, ToastController, IonTextarea, PopoverController, NavController } from '@ionic/angular';
import { createWorker } from 'tesseract.js';
import { CantiService } from '../../services/canti.service';
import { MyCantiService } from '../../services/my-canti.service';
import { ThemeService } from '../../services/theme.service';

@Component({
  selector: 'app-propose-canto',
  templateUrl: './propose-canto.page.html',
  styleUrls: ['./propose-canto.page.scss'],
  standalone: true,
  imports: [CommonModule, FormsModule, IonicModule]
})
export class ProposeCantoPage implements OnInit {
  @ViewChild('contentTextarea', { static: false }) contentTextarea!: IonTextarea;
  @ViewChild('cameraInput', { static: false }) cameraInput!: ElementRef;

  public cantiService = inject(CantiService);
  private myCantiService = inject(MyCantiService);
  private navCtrl = inject(NavController);
  public themeService = inject(ThemeService);

  title: string = '';
  author: string = '';
  youtubeLink: string = '';
  selectedLiturgico: number[] = [];
  selectedTematico: number[] = [];
  
  private _content: string = '';
  get content(): string { return this._content; }
  set content(val: string) {
    if (this._content !== val) {
      this.saveToUndoStack(this._content);
      this._content = val;
    }
  }

  undoStack: string[] = [];
  isProcessingOCR: boolean = false;
  ocrProgress: number = 0;
  get isHighContrast(): boolean { return this.themeService.highContrast(); }

  groupedChords = [
    {
      root: 'DO',
      chords: ['DO', 'DO-', 'DO#', 'DO#-', 'DO7', 'DO-7', 'DOmaj7', 'DO4', 'DOdim', 'DOm7']
    },
    {
      root: 'RE',
      chords: ['RE', 'RE-', 'RE#', 'RE#-', 'RE7', 'RE-7', 'REmaj7', 'RE4', 'REdim', 'REm7']
    },
    {
      root: 'MI',
      chords: ['MI', 'MI-', 'MI7', 'MI-7', 'MImaj7', 'MI4', 'MIdim']
    },
    {
      root: 'FA',
      chords: ['FA', 'FA-', 'FA#', 'FA#-', 'FA7', 'FAmaj7', 'FA4', 'FAdim', 'FAm7']
    },
    {
      root: 'SOL',
      chords: ['SOL', 'SOL-', 'SOL#', 'SOL#-', 'SOL7', 'SOLmaj7', 'SOL4', 'SOLdim', 'SOLm7']
    },
    {
      root: 'LA',
      chords: ['LA', 'LA-', 'LA#', 'LA#-', 'LA7', 'LA-7', 'LAmaj7', 'LA4', 'LAdim', 'LAm7']
    },
    {
      root: 'SI',
      chords: ['SI', 'SI-', 'SI7', 'SI-7', 'SImaj7', 'SI4', 'SIdim']
    }
  ];

  selectedRootChord: string | null = null;

  selectRoot(root: string) {
    if (this.selectedRootChord === root) {
      this.selectedRootChord = null;
    } else {
      this.selectedRootChord = root;
    }
  }

  getVariations(): string[] {
    if (!this.selectedRootChord) return [];
    const group = this.groupedChords.find(g => g.root === this.selectedRootChord);
    return group ? group.chords : [];
  }

  commonTags = [
    { label: 'Ritornello', start: '{start_chorus}', end: '{end_chorus}' },
    { label: 'Strofa', start: '{start_verse}', end: '{end_verse}' },
    { label: 'Strofa Num.', start: '{start_verse_num}', end: '{end_verse_num}' },
    { label: 'Inciso', start: '{start_bridge}', end: '{end_bridge}' },
    { label: 'Intro Accordi', start: '{start_chord}', end: '{end_chord}' },
    { label: 'Commento', start: '{c: ', end: '}' },
    { label: 'ChordPro Strofa', start: '{sov}', end: '{eov}' },
    { label: 'ChordPro Rit.', start: '{soc}', end: '{eoc}' }
  ];

  constructor(private toastController: ToastController, private popoverController: PopoverController) { }

  ngOnInit() {
  }

  async insertText(tag: string) {
    const input = await this.contentTextarea.getInputElement();
    const start = input.selectionStart || 0;
    const end = input.selectionEnd || 0;
    
    this.content = this.content.substring(0, start) + tag + this.content.substring(end);
    
    setTimeout(() => {
      input.focus();
      input.setSelectionRange(start + tag.length, start + tag.length);
    }, 10);
  }

  insertChord(chord: string) {
    this.insertText(`[${chord}]`);
  }

  undo() {
    if (this.undoStack.length > 0) {
      const previous = this.undoStack.pop();
      if (previous !== undefined) {
        this._content = previous;
      }
    }
  }

  private saveToUndoStack(val: string) {
    if (this.undoStack.length >= 30) {
      this.undoStack.shift();
    }
    this.undoStack.push(val);
  }

  takePhoto() {
    this.cameraInput.nativeElement.click();
  }

  async onFileSelected(event: any, isCamera: boolean) {
    const file = event.target.files[0];
    if (!file) {
      console.log('[OCR-Capture] Nessun file selezionato.');
      return;
    }

    console.log(`[OCR-Capture] File selezionato: nome="${file.name}", tipo="${file.type}", dimensione=${(file.size / (1024 * 1024)).toFixed(2)} MB`);

    this.isProcessingOCR = true;
    this.ocrProgress = 0;

    try {
      console.log('[OCR-Capture] Immagine/Fotocamera rilevata. Avvio ridimensionamento...');
      const compressedBlob = await this.resizeImage(file);
      const compressedFile = new File([compressedBlob], file.name, { type: 'image/jpeg' });
      console.log(`[OCR-Capture] Dimensioni dopo compressione: ${(compressedFile.size / 1024).toFixed(1)} KB`);
      
      console.log('[OCR-Capture] Avvio motore Tesseract OCR local...');
      const extractedText = await this.processImageOCR(compressedFile);

      if (extractedText) {
        console.log('[OCR-Capture] Testo estratto con successo! Inserimento nell\'editor...');
        this.content += (this.content ? '\n\n' : '') + extractedText;
        const toast = await this.toastController.create({
          message: 'Scansione completata!',
          duration: 2000,
          color: 'success'
        });
        toast.present();
      } else {
        console.warn('[OCR-Capture] Nessun testo estratto dal file.');
      }
    } catch (error) {
      console.error('[OCR-Capture] Errore durante l\'elaborazione del file:', error);
      const errorToast = await this.toastController.create({
        message: 'Errore durante la scansione dell\'immagine.',
        duration: 3000,
        color: 'danger'
      });
      errorToast.present();
    } finally {
      this.isProcessingOCR = false;
      this.ocrProgress = 0;
      event.target.value = '';
    }
  }

  async resizeImage(file: File): Promise<Blob> {
    console.log('[OCR-Capture] Caricamento immagine in memoria...');
    return new Promise((resolve, reject) => {
      const img = new Image();
      img.onload = () => {
        console.log(`[OCR-Capture] Immagine caricata in memoria. Dimensioni originali: ${img.width}x${img.height}`);
        const canvas = document.createElement('canvas');
        const maxDim = 1200;
        let width = img.width;
        let height = img.height;

        if (width > maxDim || height > maxDim) {
          if (width > height) {
            height = Math.round((height * maxDim) / width);
            width = maxDim;
          } else {
            width = Math.round((width * maxDim) / height);
            height = maxDim;
          }
        }

        console.log(`[OCR-Capture] Ridimensionamento a: ${width}x${height}`);
        canvas.width = width;
        canvas.height = height;
        const ctx = canvas.getContext('2d');
        if (ctx) {
          ctx.drawImage(img, 0, 0, width, height);
          canvas.toBlob(blob => {
            if (blob) {
              console.log('[OCR-Capture] Compressione in JPEG completata con successo.');
              resolve(blob);
            } else {
              reject(new Error('Canvas toBlob failed'));
            }
          }, 'image/jpeg', 0.85);
        } else {
          reject(new Error('Canvas getContext 2d failed'));
        }
      };
      img.onerror = (err) => {
        console.error('[OCR-Capture] Impossibile caricare l\'immagine in memoria:', err);
        reject(err);
      };
      img.src = URL.createObjectURL(file);
    });
  }

  async processImageOCR(file: File): Promise<string> {
    console.log('[OCR-Capture] Inizializzazione Worker Tesseract.js...');
    const worker = await createWorker('ita', 1, {
      logger: m => {
        if (m.status === 'recognizing text') {
          this.ocrProgress = m.progress;
          console.log(`[OCR-Capture] Progresso OCR: ${(m.progress * 100).toFixed(0)}%`);
        }
      }
    });
    console.log('[OCR-Capture] Avvio riconoscimento caratteri (OCR) con blocks abilitato...');
    const { data } = await worker.recognize(file, {}, { blocks: true });
    console.log('[OCR-Capture] Riconoscimento caratteri terminato. Spegnimento worker...');
    await worker.terminate();
    console.log('[OCR-Capture] Spegnimento worker completato. Estrazione parole...');

    // Flatten blocks hierarchy to get a flat words list
    const words: any[] = [];
    if (data && (data as any).blocks) {
      const blocks = (data as any).blocks;
      blocks.forEach((block: any) => {
        if (block.paragraphs) {
          block.paragraphs.forEach((paragraph: any) => {
            if (paragraph.lines) {
              paragraph.lines.forEach((line: any) => {
                if (line.words) {
                  line.words.forEach((word: any) => {
                    words.push(word);
                  });
                }
              });
            }
          });
        }
      });
    }

    console.log(`[OCR-Capture] Parole estratte dal blocco gerarchico: ${words.length}`);
    return this.parseSongSpatially(words);
  }

  parseSongSpatially(words: any[]): string {
    if (!words || words.length === 0) {
      console.warn('[OCR-Capture] Nessuna parola ricevuta dall\'OCR.');
      return '';
    }

    console.log(`[OCR-Capture] Parole totali ricevute dall'OCR: ${words.length}`);
    const validWords = words.filter(w => w.text && w.text.trim().length > 0);
    console.log(`[OCR-Capture] Parole valide dopo filtraggio: ${validWords.length}`);
    if (validWords.length === 0) return '';

    // Calculate average word height to set vertical tolerance
    const heights = validWords.map(w => w.bbox.y1 - w.bbox.y0);
    const avgHeight = heights.reduce((sum, h) => sum + h, 0) / heights.length;
    const verticalTolerance = avgHeight * 0.6;
    console.log(`[OCR-Capture] Altezza media carattere: ${avgHeight.toFixed(1)}px, tolleranza verticale: ${verticalTolerance.toFixed(1)}px`);

    // 2. Group words into horizontal lines
    const lines: any[][] = [];
    validWords.forEach(word => {
      let added = false;
      for (const line of lines) {
        const avgLineY0 = line.reduce((sum, w) => sum + w.bbox.y0, 0) / line.length;
        if (Math.abs(word.bbox.y0 - avgLineY0) < verticalTolerance) {
          line.push(word);
          added = true;
          break;
        }
      }
      if (!added) {
        lines.push([word]);
      }
    });

    // Sort words horizontally in each line
    lines.forEach(line => line.sort((a, b) => a.bbox.x0 - b.bbox.x0));

    // Sort all lines vertically by average y0
    lines.sort((a, b) => {
      const avgA = a.reduce((sum, w) => sum + w.bbox.y0, 0) / a.length;
      const avgB = b.reduce((sum, w) => sum + w.bbox.y0, 0) / b.length;
      return avgA - avgB;
    });

    // 3. Classify lines as Chords vs. Text
    const chordRegex = /^(DO|RE|MI|FA|SOL|LA|SI|C|D|E|F|G|A|B)(#|B)?(M|-|MIN|MAJ|AUG|DIM)?(7|9|11|13)?$/i;
    const isChordWord = (text: string): boolean => {
      const clean = text.replace(/[\[\]\(\)\.\,\-\+]/g, '').trim().toUpperCase();
      return chordRegex.test(clean);
    };

    const classifiedLines = lines.map(line => {
      const chordCount = line.filter(w => isChordWord(w.text)).length;
      const ratio = line.length > 0 ? chordCount / line.length : 0;
      const isChords = ratio >= 0.4 && line.length <= 10;

      return {
        words: line,
        isChords: isChords,
        yCenter: line.reduce((sum, w) => sum + (w.bbox.y0 + w.bbox.y1)/2, 0) / line.length
      };
    });

    // 4. Merge chords and text lines
    const processedLines: string[] = [];
    let inChorus = false;
    let inVerse = false;
    const chorusStartRegex = /^(R:|Rit\.|Rit|Ritornello|Coro)/i;

    for (let i = 0; i < classifiedLines.length; i++) {
      const current = classifiedLines[i];

      if (current.isChords) {
        const next = (i + 1 < classifiedLines.length) ? classifiedLines[i + 1] : null;
        if (next && !next.isChords) {
          // Merge spatially!
          const merged = this.mergeChordsAndLyrics(current.words, next.words);
          const lineText = next.words.map(w => w.text).join(' ');
          const isChorus = chorusStartRegex.test(lineText);

          if (isChorus) {
            if (inVerse) { processedLines.push('{end_verse}'); inVerse = false; }
            if (!inChorus) { processedLines.push('{start_chorus}'); inChorus = true; }
          } else if (!inChorus && !inVerse && lineText.length > 5) {
            processedLines.push('{start_verse}');
            inVerse = true;
          }

          processedLines.push(merged);
          i++; // Skip next line because we consumed it!
        } else {
          // Chord line but no text below it: just wrap and print
          const wrapped = current.words.map(w => `[${w.text.replace(/[\(\)\[\]]/g, '').toUpperCase()}]`).join(' ');
          processedLines.push(wrapped);
        }
      } else {
        const lineText = current.words.map(w => w.text).join(' ');
        const isChorus = chorusStartRegex.test(lineText);

        if (isChorus) {
          if (inVerse) { processedLines.push('{end_verse}'); inVerse = false; }
          if (!inChorus) { processedLines.push('{start_chorus}'); inChorus = true; }
        } else if (!inChorus && !inVerse && lineText.length > 5) {
          processedLines.push('{start_verse}');
          inVerse = true;
        }

        processedLines.push(lineText);
      }

      // If we see a large vertical gap, close open blocks
      const currentNext = (i + 1 < classifiedLines.length) ? classifiedLines[i + 1] : null;
      if (currentNext) {
        const gap = currentNext.yCenter - current.yCenter;
        if (gap > avgHeight * 2.5) {
          if (inChorus) { processedLines.push('{end_chorus}'); inChorus = false; }
          if (inVerse) { processedLines.push('{end_verse}'); inVerse = false; }
          processedLines.push('');
        }
      }
    }

    if (inChorus) processedLines.push('{end_chorus}');
    if (inVerse) processedLines.push('{end_verse}');

    return processedLines.join('\n');
  }

  mergeChordsAndLyrics(chordWords: any[], textWords: any[]): string {
    let result = '';
    const chordAssignments = new Map<any, any[]>();

    chordWords.forEach(chord => {
      const chordX = (chord.bbox.x0 + chord.bbox.x1) / 2;
      let closestWord: any = null;
      let minDistance = Infinity;

      textWords.forEach(textWord => {
        const wordXCenter = (textWord.bbox.x0 + textWord.bbox.x1) / 2;
        const dist = Math.abs(chordX - wordXCenter);
        if (dist < minDistance) {
          minDistance = dist;
          closestWord = textWord;
        }
      });

      if (closestWord) {
        if (!chordAssignments.has(closestWord)) {
          chordAssignments.set(closestWord, []);
        }
        chordAssignments.get(closestWord)!.push(chord);
      }
    });

    textWords.forEach((textWord, index) => {
      const assignedChords = chordAssignments.get(textWord) || [];
      assignedChords.sort((a, b) => a.bbox.x0 - b.bbox.x0);

      assignedChords.forEach(chord => {
        const cleanChord = chord.text.replace(/[\(\)\[\]]/g, '').toUpperCase();
        result += `[${cleanChord}]`;
      });

      result += textWord.text;
      if (index < textWords.length - 1) {
        result += ' ';
      }
    });

    return result;
  }



  smartProcessOCR(text: string): string {
    let lines = text.split('\n');
    let processedLines: string[] = [];
    const chordRegex = /\b(DO|RE|MI|FA|SOL|LA|SI|C|D|E|F|G|A|B)(#|b)?(m|-|min|maj|aug|dim)?(7|9|11|13)?\b/gi;
    let inChorus = false;
    let inVerse = false;

    lines.forEach((line) => {
      let trimmed = line.trim();
      if (!trimmed) {
        if (inChorus) { processedLines.push('{end_chorus}'); inChorus = false; }
        if (inVerse) { processedLines.push('{end_verse}'); inVerse = false; }
        processedLines.push('');
        return;
      }

      const isChorusMarker = /^(Rit|Ritornello|Chorus|CORO)/i.test(trimmed);
      if (isChorusMarker && !inChorus) {
        if (inVerse) { processedLines.push('{end_verse}'); inVerse = false; }
        processedLines.push('{start_chorus}');
        inChorus = true;
        if (trimmed.length > 12) processedLines.push(this.wrapChords(trimmed, chordRegex));
      } else {
        if (!inVerse && !inChorus && trimmed.length > 5) {
          processedLines.push('{start_verse}');
          inVerse = true;
        }
        processedLines.push(this.wrapChords(trimmed, chordRegex));
      }
    });

    if (inChorus) processedLines.push('{end_chorus}');
    if (inVerse) processedLines.push('{end_verse}');
    return processedLines.join('\n');
  }

  private wrapChords(line: string, regex: RegExp): string {
    const chordsInLine = line.match(regex);
    if (chordsInLine && chordsInLine.length > 0) {
      return line.replace(regex, (match) => `[${match.toUpperCase()}]`);
    }
    return line;
  }



  async saveToMyCanti() {
    if (!this.title || !this.content) return;

    // Combine lit and tematico for id_momenti
    const id_momenti = [...this.selectedLiturgico, ...this.selectedTematico];

    await this.myCantiService.saveCanto({
      titolo: this.title,
      autore: this.author,
      link_youtube: this.youtubeLink,
      testo: this.content,
      accordi: this.content, // Save to both fields for compatibility
      id_momenti: id_momenti
    });

    this.navCtrl.back();
  }
}
