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 { PlaylistService } from '../../services/playlist.service';
import { ThemeService } from '../../services/theme.service';
import { ActivatedRoute, RouterModule, Router } from '@angular/router';
import { LyricsParserService, ParsedSection } from '../../services/lyrics-parser.service';

@Component({
  selector: 'app-propose-canto',
  templateUrl: './propose-canto.page.html',
  styleUrls: ['./propose-canto.page.scss'],
  standalone: true,
  imports: [CommonModule, FormsModule, IonicModule, RouterModule]
})
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 playlistService = inject(PlaylistService);
  private navCtrl = inject(NavController);
  public themeService = inject(ThemeService);
  private route = inject(ActivatedRoute);
  private router = inject(Router);
  public lyricsParser = inject(LyricsParserService);

  showChordsPreview: boolean = true;

  get parsedSections(): ParsedSection[] {
    return this.lyricsParser.parseAccordi(this.content);
  }

  toggleChordsPreview() {
    this.showChordsPreview = !this.showChordsPreview;
  }

  title: string = '';
  author: string = '';
  youtubeLink: string = '';
  selectedLiturgico: number[] = [];
  selectedTematico: number[] = [];
  editId: string | null = null;
  
  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;
  isDraggingOver: boolean = false;
  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() {
    this.route.queryParams.subscribe(params => {
      const editId = params['editId'];
      if (editId) {
        this.editId = editId;
        // Find the song in standard canti, personal canti, or remote custom canti list
        const song = [
          ...this.cantiService.canti(),
          ...this.myCantiService.myCanti(),
          ...this.playlistService.remoteCustomSongs(),
          ...this.playlistService.remoteShareCanti()
        ].find(c => c.id === editId);

        if (song) {
          this.title = song.titolo;
          this.author = song.autore || '';
          this.youtubeLink = song.link_youtube || '';
          this.content = song.accordi || song.testo || '';
          
          // Pre-populate liturgico and tematico lists
          const litIds = this.cantiService.indiceLiturgico().map(m => m.id);
          const temIds = this.cantiService.indiceTematico().map(m => m.id);
          
          this.selectedLiturgico = song.id_momenti?.filter((id: number) => litIds.includes(id)) || [];
          this.selectedTematico = song.id_momenti?.filter((id: number) => temIds.includes(id)) || [];
        }
      }
    });
  }

  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();
  }

  onDragOver(event: DragEvent) {
    event.preventDefault();
    event.stopPropagation();
    
    // Mostra l'overlay solo se si sta trascinando un file
    if (event.dataTransfer?.types.includes('Files')) {
      this.isDraggingOver = true;
    }
  }

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

  async onDrop(event: DragEvent) {
    event.preventDefault();
    event.stopPropagation();
    this.isDraggingOver = false;

    if (event.dataTransfer && event.dataTransfer.files && event.dataTransfer.files.length > 0) {
      const file = event.dataTransfer.files[0];
      if (file.type.indexOf('image') !== -1) {
        await this.processImageFile(file);
      } else {
        const toast = await this.toastController.create({
          message: 'Per favore, trascina un file immagine valido.',
          duration: 3000,
          color: 'warning'
        });
        toast.present();
      }
    }
  }

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

  async onPaste(event: ClipboardEvent) {
    const items = event.clipboardData?.items;
    if (!items) return;

    for (let i = 0; i < items.length; i++) {
      if (items[i].type.indexOf('image') !== -1) {
        event.preventDefault(); // Prevent pasting the image representation as text
        const blob = items[i].getAsFile();
        if (blob) {
          const file = new File([blob], 'pasted-image.png', { type: blob.type });
          await this.processImageFile(file);
        }
        break;
      }
    }
  }

  async processImageFile(file: File) {
    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;
    }
  }

  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);
  }

  sanitizeOcrChord(text: string): string {
    if (!text) return text;
    if (text.includes('/')) {
      return text.split('/').map(part => this.sanitizeOcrChord(part.trim())).join('/');
    }
    let cleaned = text;
    // Replace D0/d0 with DO/do
    cleaned = cleaned.replace(/^D0/gi, 'DO');
    // Replace S0L/s0l with SOL/sol
    cleaned = cleaned.replace(/^S0L/gi, 'SOL');
    // Clean H/sharp mismatches:
    cleaned = cleaned.replace(/H#/gi, '#');
    cleaned = cleaned.replace(/#H/gi, '#');
    cleaned = cleaned.replace(/([CDEFGAB]|DO|RE|MI|FA|SOL|LA|SI)H/gi, '$1#');
    // Clean duplicate sharps (e.g. ## -> #)
    cleaned = cleaned.replace(/##+/g, '#');
    return cleaned;
  }

  convertEnglishChordToItalian(chord: string): string {
    if (!chord) return chord;
    if (chord.includes('/')) {
      return chord.split('/').map(part => this.convertEnglishChordToItalian(part.trim())).join('/');
    }
    const upper = chord.toUpperCase();
    if (upper.startsWith('DO')) {
      return chord;
    }
    if (upper.startsWith('FA')) {
      if (upper.startsWith('FAUG') || upper.startsWith('FADD') || upper.startsWith('FALT')) {
        return 'FA' + chord.slice(1);
      }
      return chord;
    }
    if (upper.startsWith('C')) {
      return 'DO' + chord.slice(1);
    }
    if (upper.startsWith('D')) {
      return 'RE' + chord.slice(1);
    }
    if (upper.startsWith('E')) {
      return 'MI' + chord.slice(1);
    }
    if (upper.startsWith('F')) {
      return 'FA' + chord.slice(1);
    }
    if (upper.startsWith('G')) {
      return 'SOL' + chord.slice(1);
    }
    if (upper.startsWith('A')) {
      return 'LA' + chord.slice(1);
    }
    if (upper.startsWith('B')) {
      return 'SI' + chord.slice(1);
    }
    return chord;
  }

  isLabelLine(text: string): boolean {
    return /^(Intro|Strofa|Rit|Special|Coro|Bridge|RIT|CHORUS|VERSE)/i.test(text.trim());
  }

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

    // Preprocess words to split run-together chords like BA
    const preprocessedWords: any[] = [];
    words.forEach(w => {
      if (!w.text) return;
      const match = w.text.match(/^([ABCDEFG])([ABCDEFG])$/i);
      if (match && !(match[1].toUpperCase() === 'F' && match[2].toUpperCase() === 'A')) {
        const charWidth = (w.bbox.x1 - w.bbox.x0) / 2;
        preprocessedWords.push({
          text: match[1],
          bbox: { ...w.bbox, x1: w.bbox.x0 + charWidth }
        });
        preprocessedWords.push({
          text: match[2],
          bbox: { ...w.bbox, x0: w.bbox.x0 + charWidth }
        });
      } else {
        preprocessedWords.push(w);
      }
    });
    words = preprocessedWords;

    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.85;
    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)?(\/(DO|RE|MI|FA|SOL|LA|SI|C|D|E|F|G|A|B)(#|B)?)?$/i;
    const isChordWord = (text: string): boolean => {
      if (!text || text.length === 0) return false;
      const lower = text.toLowerCase();
      // Skip common valid words written purely in lowercase
      if (text[0] === lower[0] && ['la', 'mi', 're', 'do', 'si', 'fa', 'e'].includes(lower)) {
         return false;
      }
      let clean = text.toUpperCase().replace(/\s+/g, '');
      clean = clean.replace(/\((.*?)\)/g, '/$1');
      clean = clean.replace(/[\.\,]$/g, '');
      clean = this.sanitizeOcrChord(clean);
      if (clean.includes('_')) {
        const parts = clean.split('_').filter(p => p.length > 0);
        if (parts.length === 0) return false;
        return parts.every(p => chordRegex.test(p));
      }
      return chordRegex.test(clean);
    };

    const classifiedLines = lines.map(line => {
      let chordCount = 0;
      let hasLongNonChord = false;
      
      line.forEach(w => {
        if (isChordWord(w.text)) {
          chordCount++;
        } else {
          const clean = w.text.replace(/[.,:;!\?]/g, '').trim();
          if (clean.length > 5) {
            hasLongNonChord = true;
          }
        }
      });
      
      const ratio = line.length > 0 ? chordCount / line.length : 0;
      let isChords = false;
      
      if (ratio >= 0.4 && line.length <= 10) {
        if (!hasLongNonChord || ratio >= 0.75) {
          isChords = true;
        }
      }

      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;
        const nextText = next ? next.words.map(w => w.text).join(' ') : '';
        if (next && !next.isChords && !this.isLabelLine(nextText)) {
          // 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 multiChordRegex = /((?:DO|RE|MI|FA|SOL|LA|SI|C|D|E|F|G|A|B)(?:#|B)?(?:MIN|MAJ|AUG|DIM|M|-)?(?:7|9|11|13)?(?:\/(?:DO|RE|MI|FA|SOL|LA|SI|C|D|E|F|G|A|B)(?:#|B)?)?)/gi;
          const expandedChords: string[] = [];
          current.words.forEach(w => {
            const parts = w.text.split(/(_)/);
            const processedParts = parts.map((part: string) => {
              if (part === '_') return ' _ ';
              if (!part.trim()) return part;
              
              let cleanText = part.toUpperCase().replace(/\s+/g, '');
              cleanText = cleanText.replace(/\((.*?)\)/g, '/$1');
              cleanText = cleanText.replace(/[\.\,]$/g, '');
              cleanText = this.sanitizeOcrChord(cleanText);
              
              if (chordRegex.test(cleanText)) {
                return `[${this.convertEnglishChordToItalian(cleanText)}]`;
              } else {
                const matches = [...cleanText.matchAll(multiChordRegex)];
                const fullMatchStr = matches.map(m => m[0]).join('');
                if (matches.length > 0 && fullMatchStr === cleanText) {
                  return matches.map((m: any) => `[${this.convertEnglishChordToItalian(m[0].toUpperCase())}]`).join('   ');
                } else {
                  return part;
                }
              }
            });
            expandedChords.push(processedParts.join(''));
          });
          const wrapped = expandedChords.join(' ');
          if (wrapped) {
            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(this.wrapChords(lineText, chordRegex));
      }

      // 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 {
    // Pre-process chordWords to merge fragmented bass notes like 'Re', '(f', 'fa#)'
    let mergedChordWords: any[] = [];
    for (let i = 0; i < chordWords.length; i++) {
      let cw = chordWords[i];
      if (cw.text.startsWith('(') && mergedChordWords.length > 0) {
        let prev = mergedChordWords[mergedChordWords.length - 1];
        prev.text += cw.text;
        prev.bbox.x1 = Math.max(prev.bbox.x1, cw.bbox.x1);
        if (!prev.text.includes(')')) {
          let j = i + 1;
          while (j < chordWords.length) {
            prev.text += chordWords[j].text;
            prev.bbox.x1 = Math.max(prev.bbox.x1, chordWords[j].bbox.x1);
            if (chordWords[j].text.includes(')')) {
              i = j;
              break;
            }
            j++;
          }
        }
      } else {
        let text = cw.text;
        let bbox = { ...cw.bbox };
        if (text.includes('(') && !text.includes(')')) {
          let j = i + 1;
          while (j < chordWords.length) {
            text += chordWords[j].text;
            bbox.x1 = Math.max(bbox.x1, chordWords[j].bbox.x1);
            if (chordWords[j].text.includes(')')) {
              i = j;
              break;
            }
            j++;
          }
        }
        mergedChordWords.push({ text, bbox });
      }
    }
    
    // Sanitize common OCR errors in chords (e.g., 'Re(f fa#)' -> 'Re(fa#)')
    mergedChordWords.forEach(cw => {
      cw.text = cw.text.replace(/f\s*fa#/gi, 'fa#');
      cw.text = cw.text.replace(/ff/gi, 'f');
      cw.text = cw.text.replace(/m\s*mi/gi, 'mi');
      cw.text = cw.text.replace(/mm/gi, 'm');
    });

    if (!textWords || textWords.length === 0) {
      return mergedChordWords.map(c => {
        const parts = c.text.split(/(_)/);
        return parts.map((part: string) => {
          if (part === '_') return ' _ ';
          if (!part.trim()) return part;
          let clean = part.toUpperCase().replace(/\s+/g, '').replace(/\((.*?)\)/g, '/$1').replace(/[\.\,\[\]]/g, '');
          clean = this.sanitizeOcrChord(clean);
          return `[${this.convertEnglishChordToItalian(clean)}]`;
        }).join('');
      }).join(' ');
    }

    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)?(\/(DO|RE|MI|FA|SOL|LA|SI|C|D|E|F|G|A|B)(#|B)?)?$/i;
    const multiChordRegex = /((?:DO|RE|MI|FA|SOL|LA|SI|C|D|E|F|G|A|B)(?:#|B)?(?:MIN|MAJ|AUG|DIM|M|-)?(?:7|9|11|13)?(?:\/(?:DO|RE|MI|FA|SOL|LA|SI|C|D|E|F|G|A|B)(?:#|B)?)?)/gi;

    const expandedChordWords: any[] = [];
    mergedChordWords.forEach(chord => {
      const parts = chord.text.split(/(_)/);
      let currentX = chord.bbox.x0;
      const totalLen = chord.text.length || 1;
      const widthPerChar = (chord.bbox.x1 - chord.bbox.x0) / totalLen;

      parts.forEach((part: string) => {
        const partLen = part.length;
        const partWidth = partLen * widthPerChar;
        const partX0 = currentX;
        const partX1 = currentX + partWidth;
        currentX = partX1;

        if (part === '_' || !part.trim()) {
          return;
        }

        let originalText = part.toUpperCase().replace(/\s+/g, '').replace(/\((.*?)\)/g, '/$1').replace(/[\.\,\[\]]/g, '');
        originalText = this.sanitizeOcrChord(originalText);
        const matches = [...originalText.matchAll(multiChordRegex)];

        if (matches.length === 0) {
          expandedChordWords.push({
            text: originalText,
            bbox: { ...chord.bbox, x0: partX0, x1: partX1 }
          });
        } else {
          const fullMatchStr = matches.map(m => m[0]).join('');
          if (fullMatchStr === originalText) {
            const matchCharWidth = (partX1 - partX0) / Math.max(1, originalText.length);
            matches.forEach(match => {
              const matchIndex = match.index!;
              const matchLength = match[0].length;
              const newX0 = partX0 + matchIndex * matchCharWidth;
              const newX1 = partX0 + (matchIndex + matchLength) * matchCharWidth;
              
              expandedChordWords.push({
                text: match[0],
                bbox: { ...chord.bbox, x0: newX0, x1: newX1 }
              });
            });
          } else {
            expandedChordWords.push({
              text: originalText,
              bbox: { ...chord.bbox, x0: partX0, x1: partX1 }
            });
          }
        }
      });
    });

    const chordAssignments = new Map<any, any[]>();

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

      textWords.forEach(textWord => {
        let dist = 0;
        if (chordX < textWord.bbox.x0) {
          dist = textWord.bbox.x0 - chordX;
        } else if (chordX > textWord.bbox.x1) {
          dist = chordX - textWord.bbox.x1;
        }

        if (dist < minDistance) {
          minDistance = dist;
          closestWord = textWord;
        }
      });

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

    let result = '';

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

      const wordText = textWord.text;
      let charWidth = (textWord.bbox.x1 - textWord.bbox.x0) / Math.max(1, wordText.length);
      if (charWidth <= 0) charWidth = 6; // safe fallback

      let lastCharIndex = 0;
      let wordResult = '';

      assignedChords.forEach(chord => {
        const chordX = (chord.bbox.x0 + chord.bbox.x1) / 2;
        let charIndex = Math.round((chordX - textWord.bbox.x0) / charWidth);

        if (charIndex < 0) charIndex = 0;
        if (charIndex > wordText.length) charIndex = wordText.length;

        let cleanChord = chord.text.replace(/[\(\)\[\]]/g, '').toUpperCase();
        cleanChord = this.sanitizeOcrChord(cleanChord);
        wordResult += wordText.substring(lastCharIndex, charIndex) + `[${this.convertEnglishChordToItalian(cleanChord)}]`;
        lastCharIndex = charIndex;
      });

      wordResult += wordText.substring(lastCharIndex);
      result += wordResult;

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

    let finalResult = result.replace(/\]\[/g, ']   [');
    finalResult = finalResult.replace(/\]_\[/g, '] _ [');
    finalResult = finalResult.replace(/\]_/g, '] _ ');
    finalResult = finalResult.replace(/_\[/g, ' _ [');
    // Normalize any duplicate spaces around underscores:
    finalResult = finalResult.replace(/\s*_\s*/g, ' _ ');
    
    console.log(`[OCR-Debug] Linea generata: ${finalResult}`);
    
    return finalResult;
  }



  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 globalRegex = new RegExp(regex.source.replace(/^\^/, '').replace(/\$$/, ''), 'gi');
    const parts = line.split(/(_)/);
    return parts.map((part: string) => {
      if (part === '_') return ' _ ';
      return part.replace(globalRegex, (match) => {
        const lower = match.toLowerCase();
        if (match === 'a' || match === 'e' || match === 'o' || match === 'i') {
          return match;
        }
        if (['la', 'mi', 're', 'do', 'si', 'fa', 'sol'].includes(lower) && match === lower) {
          return match;
        }
        const sanitized = this.sanitizeOcrChord(match.toUpperCase());
        return `[${this.convertEnglishChordToItalian(sanitized)}]`;
      });
    }).join('');
  }



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

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

    const activePlaylistId = this.playlistService.activePlaylistId();
    const isRemotePlaylist = activePlaylistId && activePlaylistId.startsWith('remote_');

    if (isRemotePlaylist) {
      // 1. Clone the song (generate a brand new my_... ID)
      const savedCanto = await this.myCantiService.saveCanto({
        titolo: this.title,
        autore: this.author,
        link_youtube: this.youtubeLink,
        testo: this.content,
        accordi: this.content,
        id_momenti: id_momenti
      });

      // 2. Clone/convert remote playlist to local personal playlist
      const remotePl = this.playlistService.remotePlaylist();
      if (remotePl) {
        const originalIds = remotePl.ids || [];
        const updatedIds = originalIds.map((id: string) => id === this.editId ? savedCanto.id : id);

        const songSettings = { ...(remotePl.songSettings || {}) };
        if (this.editId && songSettings[this.editId]) {
          songSettings[savedCanto.id] = { ...songSettings[this.editId] };
          delete songSettings[this.editId];
        }

        const localName = remotePl.name.replace('[Remote] ', '');
        
        // Force save as a new local playlist
        this.playlistService.activePlaylistId.set(null);
        await this.playlistService.savePlaylist(localName, updatedIds, songSettings);
      }

      // Navigate to the player with the new cloned song ID immediately
      this.router.navigate(['/player'], { queryParams: { id: savedCanto.id }, replaceUrl: true });
    } else {
      // Standard path
      const savedCanto = await this.myCantiService.saveCanto({
        id: this.editId || undefined,
        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
      });

      if (this.editId && !this.editId.startsWith('my_') && savedCanto && savedCanto.id) {
        await this.playlistService.replaceSongIdInPlaylists(this.editId, savedCanto.id);
      }

      if (this.editId && this.editId.startsWith('remote_share_')) {
        await this.playlistService.deleteRemoteShareCanto(this.editId);
      }

      if (this.editId && savedCanto && savedCanto.id && this.editId !== savedCanto.id) {
        this.router.navigate(['/player'], { queryParams: { id: savedCanto.id }, replaceUrl: true });
      } else {
        this.navCtrl.back();
      }
    }
  }
}
