/**
 * Comprehensive Chord Sheet Utility
 * Uses ChordSheetJS for parsing/rendering and Tonal for music theory
 */
import ChordSheetJS from "chordsheetjs";
import { Note, Scale, Chord, Progression } from "tonal";

// ============================================
// COMPLETE CHORD TYPE DEFINITIONS
// ============================================

// All root notes (chromatic scale)
export const ROOT_NOTES = [
  "C",
  "C#",
  "Db",
  "D",
  "D#",
  "Eb",
  "E",
  "F",
  "F#",
  "Gb",
  "G",
  "G#",
  "Ab",
  "A",
  "A#",
  "Bb",
  "B",
];

// All chord qualities/types
export const CHORD_QUALITIES = [
  { symbol: "", name: "Major" },
  { symbol: "m", name: "Minor" },
  { symbol: "dim", name: "Diminished" },
  { symbol: "aug", name: "Augmented" },
  { symbol: "7", name: "Dominant 7th" },
  { symbol: "maj7", name: "Major 7th" },
  { symbol: "m7", name: "Minor 7th" },
  { symbol: "dim7", name: "Diminished 7th" },
  { symbol: "m7b5", name: "Half-Diminished" },
  { symbol: "aug7", name: "Augmented 7th" },
  { symbol: "6", name: "Major 6th" },
  { symbol: "m6", name: "Minor 6th" },
  { symbol: "9", name: "Dominant 9th" },
  { symbol: "maj9", name: "Major 9th" },
  { symbol: "m9", name: "Minor 9th" },
  { symbol: "11", name: "Dominant 11th" },
  { symbol: "13", name: "Dominant 13th" },
  { symbol: "sus2", name: "Suspended 2nd" },
  { symbol: "sus4", name: "Suspended 4th" },
  { symbol: "7sus4", name: "7th Suspended 4th" },
  { symbol: "add9", name: "Add 9" },
  { symbol: "add11", name: "Add 11" },
  { symbol: "5", name: "Power Chord" },
  { symbol: "2", name: "Add 2" },
  { symbol: "4", name: "Add 4" },
];

// Generate ALL possible chords (root + quality combinations)
export const ALL_CHORDS = [];
ROOT_NOTES.forEach((root) => {
  CHORD_QUALITIES.forEach((quality) => {
    ALL_CHORDS.push({
      chord: root + quality.symbol,
      root: root,
      quality: quality.symbol,
      name: `${root} ${quality.name}`,
    });
  });
});

// ============================================
// KEY SIGNATURES WITH DIATONIC PROGRESSIONS
// ============================================

// Major key diatonic chords (I, ii, iii, IV, V, vi, vii°)
export const MAJOR_KEY_PROGRESSIONS = {
  C: ["C", "Dm", "Em", "F", "G", "Am", "Bdim"],
  "C#": ["C#", "D#m", "E#m", "F#", "G#", "A#m", "B#dim"],
  Db: ["Db", "Ebm", "Fm", "Gb", "Ab", "Bbm", "Cdim"],
  D: ["D", "Em", "F#m", "G", "A", "Bm", "C#dim"],
  "D#": ["D#", "E#m", "F##m", "G#", "A#", "B#m", "C##dim"],
  Eb: ["Eb", "Fm", "Gm", "Ab", "Bb", "Cm", "Ddim"],
  E: ["E", "F#m", "G#m", "A", "B", "C#m", "D#dim"],
  F: ["F", "Gm", "Am", "Bb", "C", "Dm", "Edim"],
  "F#": ["F#", "G#m", "A#m", "B", "C#", "D#m", "E#dim"],
  Gb: ["Gb", "Abm", "Bbm", "Cb", "Db", "Ebm", "Fdim"],
  G: ["G", "Am", "Bm", "C", "D", "Em", "F#dim"],
  "G#": ["G#", "A#m", "B#m", "C#", "D#", "E#m", "F##dim"],
  Ab: ["Ab", "Bbm", "Cm", "Db", "Eb", "Fm", "Gdim"],
  A: ["A", "Bm", "C#m", "D", "E", "F#m", "G#dim"],
  "A#": ["A#", "B#m", "C##m", "D#", "E#", "F##m", "G##dim"],
  Bb: ["Bb", "Cm", "Dm", "Eb", "F", "Gm", "Adim"],
  B: ["B", "C#m", "D#m", "E", "F#", "G#m", "A#dim"],
};

// Minor key diatonic chords (i, ii°, III, iv, v, VI, VII)
export const MINOR_KEY_PROGRESSIONS = {
  Am: ["Am", "Bdim", "C", "Dm", "Em", "F", "G"],
  "A#m": ["A#m", "B#dim", "C#", "D#m", "E#m", "F#", "G#"],
  Bbm: ["Bbm", "Cdim", "Db", "Ebm", "Fm", "Gb", "Ab"],
  Bm: ["Bm", "C#dim", "D", "Em", "F#m", "G", "A"],
  Cm: ["Cm", "Ddim", "Eb", "Fm", "Gm", "Ab", "Bb"],
  "C#m": ["C#m", "D#dim", "E", "F#m", "G#m", "A", "B"],
  Dm: ["Dm", "Edim", "F", "Gm", "Am", "Bb", "C"],
  "D#m": ["D#m", "E#dim", "F#", "G#m", "A#m", "B", "C#"],
  Ebm: ["Ebm", "Fdim", "Gb", "Abm", "Bbm", "Cb", "Db"],
  Em: ["Em", "F#dim", "G", "Am", "Bm", "C", "D"],
  Fm: ["Fm", "Gdim", "Ab", "Bbm", "Cm", "Db", "Eb"],
  "F#m": ["F#m", "G#dim", "A", "Bm", "C#m", "D", "E"],
  Gm: ["Gm", "Adim", "Bb", "Cm", "Dm", "Eb", "F"],
  "G#m": ["G#m", "A#dim", "B", "C#m", "D#m", "E", "F#"],
};

// All keys for dropdown
export const ALL_KEYS = [
  // Major keys
  { value: "C", label: "C Major", type: "major" },
  { value: "C#", label: "C# Major", type: "major" },
  { value: "Db", label: "Db Major", type: "major" },
  { value: "D", label: "D Major", type: "major" },
  { value: "Eb", label: "Eb Major", type: "major" },
  { value: "E", label: "E Major", type: "major" },
  { value: "F", label: "F Major", type: "major" },
  { value: "F#", label: "F# Major", type: "major" },
  { value: "Gb", label: "Gb Major", type: "major" },
  { value: "G", label: "G Major", type: "major" },
  { value: "Ab", label: "Ab Major", type: "major" },
  { value: "A", label: "A Major", type: "major" },
  { value: "Bb", label: "Bb Major", type: "major" },
  { value: "B", label: "B Major", type: "major" },
  // Minor keys
  { value: "Am", label: "A Minor", type: "minor" },
  { value: "A#m", label: "A# Minor", type: "minor" },
  { value: "Bbm", label: "Bb Minor", type: "minor" },
  { value: "Bm", label: "B Minor", type: "minor" },
  { value: "Cm", label: "C Minor", type: "minor" },
  { value: "C#m", label: "C# Minor", type: "minor" },
  { value: "Dm", label: "D Minor", type: "minor" },
  { value: "D#m", label: "D# Minor", type: "minor" },
  { value: "Ebm", label: "Eb Minor", type: "minor" },
  { value: "Em", label: "E Minor", type: "minor" },
  { value: "Fm", label: "F Minor", type: "minor" },
  { value: "F#m", label: "F# Minor", type: "minor" },
  { value: "Gm", label: "G Minor", type: "minor" },
  { value: "G#m", label: "G# Minor", type: "minor" },
];

// ============================================
// CHORD SHEET PARSING & RENDERING
// ============================================

/**
 * Parse ChordPro format to structured song object
 * ChordPro format: [C]Amazing [G]grace how [Am]sweet
 */
export function parseChordPro(content) {
  const parser = new ChordSheetJS.ChordProParser();
  try {
    return parser.parse(content);
  } catch (e) {
    return null;
  }
}

/**
 * Parse Ultimate Guitar / plain text format
 * Format with chords on separate lines above lyrics
 */
export function parsePlainText(content) {
  const parser = new ChordSheetJS.ChordsOverWordsParser();
  try {
    return parser.parse(content);
  } catch (e) {
    return null;
  }
}

/**
 * Render song to ChordPro format
 */
export function renderToChordPro(song) {
  const formatter = new ChordSheetJS.ChordProFormatter();
  return formatter.format(song);
}

/**
 * Render song to HTML with chords ABOVE lyrics
 */
export function renderToHtml(song) {
  const formatter = new ChordSheetJS.HtmlTableFormatter();
  return formatter.format(song);
}

/**
 * Render song to plain text with chords above lyrics
 */
export function renderToText(song) {
  const formatter = new ChordSheetJS.TextFormatter();
  return formatter.format(song);
}

// ============================================
// TRANSPOSITION USING CHORDSHEETJS
// ============================================

/**
 * Transpose a song by semitones
 */
export function transposeSong(song, semitones) {
  if (!song || semitones === 0) return song;
  return song.transpose(semitones);
}

/**
 * Transpose from one key to another
 */
export function transposeToKey(song, fromKey, toKey) {
  const semitones = getSemitonesBetweenKeys(fromKey, toKey);
  return transposeSong(song, semitones);
}

/**
 * Calculate semitones between two keys
 */
export function getSemitonesBetweenKeys(fromKey, toKey) {
  const chromatic = [
    "C",
    "C#",
    "D",
    "D#",
    "E",
    "F",
    "F#",
    "G",
    "G#",
    "A",
    "A#",
    "B",
  ];
  const flatToSharp = {
    Db: "C#",
    Eb: "D#",
    Fb: "E",
    Gb: "F#",
    Ab: "G#",
    Bb: "A#",
    Cb: "B",
  };

  // Extract root note (remove minor suffix if present)
  const fromRoot = fromKey.replace(/m$/, "");
  const toRoot = toKey.replace(/m$/, "");

  // Normalize to sharp notation
  const fromNorm = flatToSharp[fromRoot] || fromRoot;
  const toNorm = flatToSharp[toRoot] || toRoot;

  const fromIdx = chromatic.indexOf(fromNorm);
  const toIdx = chromatic.indexOf(toNorm);

  if (fromIdx === -1 || toIdx === -1) return 0;

  return (toIdx - fromIdx + 12) % 12;
}

// ============================================
// CHORD PROGRESSION GENERATION
// ============================================

/**
 * Get diatonic chords for a key
 */
export function getDiatonicChords(key) {
  const isMinor = key.endsWith("m");

  if (isMinor) {
    return MINOR_KEY_PROGRESSIONS[key] || MINOR_KEY_PROGRESSIONS["Am"];
  }
  return MAJOR_KEY_PROGRESSIONS[key] || MAJOR_KEY_PROGRESSIONS["C"];
}

/**
 * Get common chord progressions for a key
 */
export function getCommonProgressions(key) {
  const chords = getDiatonicChords(key);
  const isMinor = key.endsWith("m");

  // Roman numeral positions
  // Major: I=0, ii=1, iii=2, IV=3, V=4, vi=5, vii°=6
  // Minor: i=0, ii°=1, III=2, iv=3, v=4, VI=5, VII=6

  return {
    "I-IV-V-I": [chords[0], chords[3], chords[4], chords[0]],
    "I-V-vi-IV": [chords[0], chords[4], chords[5], chords[3]],
    "I-vi-IV-V": [chords[0], chords[5], chords[3], chords[4]],
    "ii-V-I": [chords[1], chords[4], chords[0]],
    "I-IV-vi-V": [chords[0], chords[3], chords[5], chords[4]],
    "vi-IV-I-V": [chords[5], chords[3], chords[0], chords[4]],
    "I-ii-IV-V": [chords[0], chords[1], chords[3], chords[4]],
    "I-iii-IV-V": [chords[0], chords[2], chords[3], chords[4]],
  };
}

// ============================================
// LYRICS + CHORD POSITIONING
// ============================================

/**
 * Parse lyrics with embedded [Chord] markers OR chords-above-lyrics format
 * for rendering chords ABOVE lyrics
 *
 * Input format: [C]Amazing [G]grace how [Am]sweet
 * OR:
 * Am    F    G
 * Lord prepare me
 *
 * Output: Array of Array of { chord: string|null, text: string }
 */
export function parseLyricsWithChords(lyrics) {
  if (!lyrics) return [];

  const lines = lyrics.split("\n");
  const result = [];

  // Pattern to detect if a line is ONLY chords (chords-above-lyrics format)
  const chordOnlyPattern =
    /^[\sA-G#b/()m\d]*[A-G][#b]?(?:m|maj|min|dim|aug|sus|add|2|4|5|6|7|9|11|13)*[\sA-G#b/()m\d]*$/;

  for (let i = 0; i < lines.length; i++) {
    const line = lines[i];
    const nextLine = lines[i + 1];

    // Check if this line is a chord line (contains only chords and spaces)
    const isChordLine =
      line.trim().length > 0 &&
      chordOnlyPattern.test(line.trim()) &&
      nextLine !== undefined &&
      !/^[A-G][#b]?(?:m|maj|min|dim|aug|sus|add)?[0-9]*[\s]*$/.test(
        nextLine.trim(),
      );

    if (isChordLine && nextLine) {
      // This is chords-above-lyrics format
      // Parse chord positions and merge with lyrics
      const segments = [];
      const chordRegex = /([A-G][#b]?(?:m|maj|min|dim|aug|sus|add)?[0-9]*)/g;
      const chords = [];
      let match;

      while ((match = chordRegex.exec(line)) !== null) {
        chords.push({
          chord: match[1],
          position: match.index,
        });
      }

      if (chords.length > 0) {
        let lastPos = 0;

        chords.forEach((chordObj, idx) => {
          const { chord, position } = chordObj;
          const textBefore = nextLine.substring(lastPos, position);

          if (textBefore) {
            segments.push({ chord: null, text: textBefore });
          }

          // Get text after this chord until next chord or end
          const nextChordPos = chords[idx + 1]
            ? chords[idx + 1].position
            : nextLine.length;
          const textAfter = nextLine.substring(position, nextChordPos);

          segments.push({
            chord: chord,
            text: textAfter || " ",
          });

          lastPos = nextChordPos;
        });

        // Add any remaining text
        if (lastPos < nextLine.length) {
          segments.push({ chord: null, text: nextLine.substring(lastPos) });
        }

        result.push(segments);
        i++; // Skip the lyrics line since we processed it
        continue;
      }
    }

    // Check for embedded [Chord] format
    const hasEmbeddedChords =
      /\[([A-Ga-g][#b]?(?:m|M|maj|min|dim|aug|sus|add|2|4|5|6|7|9|11|13)*(?:\/[A-Ga-g][#b]?)?)\]/.test(
        line,
      );

    if (hasEmbeddedChords) {
      // Parse embedded [Chord] markers
      const segments = [];
      const regex =
        /\[([A-Ga-g][#b]?(?:m|M|maj|min|dim|aug|sus|add|2|4|5|6|7|9|11|13)*(?:\/[A-Ga-g][#b]?)?)\]/g;

      let lastIndex = 0;
      let chordMatch;

      while ((chordMatch = regex.exec(line)) !== null) {
        // Text before this chord (no chord above it)
        if (chordMatch.index > lastIndex) {
          const textBefore = line.substring(lastIndex, chordMatch.index);
          if (textBefore) {
            segments.push({ chord: null, text: textBefore });
          }
        }

        // Find text after chord until next chord or end
        const afterChordIdx = chordMatch.index + chordMatch[0].length;
        const nextMatch = regex.exec(line);
        const nextIdx = nextMatch ? nextMatch.index : line.length;
        regex.lastIndex = afterChordIdx; // Reset to continue from after chord

        const textAfter = line.substring(afterChordIdx, nextIdx);
        segments.push({
          chord: chordMatch[1],
          text: textAfter || " ",
        });

        lastIndex = nextIdx;

        // If we peeked ahead, go back
        if (nextMatch) {
          regex.lastIndex = nextMatch.index;
        }
      }

      // Remaining text
      if (lastIndex < line.length) {
        segments.push({ chord: null, text: line.substring(lastIndex) });
      }

      // If no chords found, whole line is plain text
      if (segments.length === 0) {
        segments.push({ chord: null, text: line });
      }

      result.push(segments);
    } else {
      // Plain text line (no chords)
      result.push([{ chord: null, text: line }]);
    }
  }

  return result;
}

/**
 * Transpose chord markers in lyrics text - handles BOTH formats:
 * 1. Embedded [Chord] format
 * 2. Chords-above-lyrics format (chord-only lines)
 */
export function transposeLyricsText(lyrics, fromKey, toKey) {
  if (!lyrics || fromKey === toKey) return lyrics;

  const semitones = getSemitonesBetweenKeys(fromKey, toKey);
  if (semitones === 0) return lyrics;

  // Use flat notation for flat keys
  const flatKeys = [
    "F",
    "Bb",
    "Eb",
    "Ab",
    "Db",
    "Gb",
    "Dm",
    "Gm",
    "Cm",
    "Fm",
    "Bbm",
    "Ebm",
  ];
  const useFlats = flatKeys.includes(toKey);

  // Split into lines to handle both formats
  const lines = lyrics.split("\n");
  const chordOnlyPattern =
    /^[\sA-G#b/()m\d]*[A-G][#b]?(?:m|maj|min|dim|aug|sus|add|2|4|5|6|7|9|11|13)*[\sA-G#b/()m\d]*$/;

  const transposedLines = lines.map((line) => {
    // Check if this is a chord-only line (chords-above-lyrics format)
    if (line.trim().length > 0 && chordOnlyPattern.test(line.trim())) {
      // Transpose all standalone chords in this line
      return line.replace(
        /([A-G][#b]?(?:m|maj|min|dim|aug|sus|add)?[0-9]*)/g,
        (match) => {
          return transposeChordName(match, semitones, useFlats);
        },
      );
    }

    // Otherwise, transpose embedded [Chord] patterns
    return line.replace(
      /\[([A-Ga-g][#b]?(?:m|M|maj|min|dim|aug|sus|add|2|4|5|6|7|9|11|13)*(?:\/[A-Ga-g][#b]?)?)\]/g,
      (match, chord) => {
        const transposed = transposeChordName(chord, semitones, useFlats);
        return `[${transposed}]`;
      },
    );
  });

  return transposedLines.join("\n");
}

/**
 * Transpose a single chord name by semitones
 */
export function transposeChordName(chord, semitones, useFlats = false) {
  if (!chord || semitones === 0) return chord;

  // Handle slash chords (e.g., C/G)
  if (chord.includes("/")) {
    const [main, bass] = chord.split("/");
    return (
      transposeChordName(main, semitones, useFlats) +
      "/" +
      transposeChordName(bass, semitones, useFlats)
    );
  }

  // Parse root and quality
  const match = chord.match(/^([A-Ga-g][#b]?)(.*)$/);
  if (!match) return chord;

  const [, root, quality] = match;

  const sharpScale = [
    "C",
    "C#",
    "D",
    "D#",
    "E",
    "F",
    "F#",
    "G",
    "G#",
    "A",
    "A#",
    "B",
  ];
  const flatScale = [
    "C",
    "Db",
    "D",
    "Eb",
    "E",
    "F",
    "Gb",
    "G",
    "Ab",
    "A",
    "Bb",
    "B",
  ];
  const flatToSharp = {
    Db: "C#",
    Eb: "D#",
    Fb: "E",
    Gb: "F#",
    Ab: "G#",
    Bb: "A#",
    Cb: "B",
  };

  // Normalize root to sharp
  const normRoot = root[0].toUpperCase() + (root.slice(1) || "");
  const sharpRoot = flatToSharp[normRoot] || normRoot;

  // Find index
  let idx = sharpScale.indexOf(sharpRoot);
  if (idx === -1) return chord;

  // Transpose
  const newIdx = (idx + semitones + 12) % 12;
  const scale = useFlats ? flatScale : sharpScale;
  const newRoot = scale[newIdx];

  return newRoot + quality;
}

/**
 * Detect the original key from lyrics content
 */
export function detectKeyFromLyrics(lyrics) {
  if (!lyrics) return "C";

  // Find all chords in the lyrics
  const chordMatches = lyrics.match(
    /\[([A-Ga-g][#b]?(?:m|M|maj|min|dim|aug|sus|add|2|4|5|6|7|9|11|13)*)\]/g,
  );
  if (!chordMatches || chordMatches.length === 0) return "C";

  // Extract just the chord names
  const chords = chordMatches.map((m) => m.replace(/[\[\]]/g, ""));

  // The first chord is often the key
  const firstChord = chords[0];

  // Check if it's minor
  if (firstChord.includes("m") && !firstChord.includes("maj")) {
    return firstChord.match(/^[A-Ga-g][#b]?m/)?.[0] || "Am";
  }

  // Return root as major key
  return firstChord.match(/^[A-Ga-g][#b]?/)?.[0] || "C";
}

/**
 * Insert chord markers into plain lyrics at specified positions
 */
export function insertChordsIntoLyrics(lyrics, chordPositions) {
  // chordPositions: Array of { line: number, position: number, chord: string }
  if (!lyrics || !chordPositions || chordPositions.length === 0) return lyrics;

  const lines = lyrics.split("\n");

  // Group by line
  const byLine = {};
  chordPositions.forEach((cp) => {
    if (!byLine[cp.line]) byLine[cp.line] = [];
    byLine[cp.line].push(cp);
  });

  // Process each line
  return lines
    .map((line, lineIdx) => {
      const lineChords = byLine[lineIdx];
      if (!lineChords || lineChords.length === 0) return line;

      // Sort by position descending to insert from end
      lineChords.sort((a, b) => b.position - a.position);

      let result = line;
      lineChords.forEach((cp) => {
        const pos = Math.min(cp.position, result.length);
        result = result.slice(0, pos) + `[${cp.chord}]` + result.slice(pos);
      });

      return result;
    })
    .join("\n");
}

/**
 * Convert ChordsOverWords format to ChordPro inline format
 */
export function convertChordsOverWordsToInline(content) {
  if (!content) return content;

  const lines = content.split("\n");
  const result = [];

  for (let i = 0; i < lines.length; i++) {
    const line = lines[i];
    const nextLine = lines[i + 1];

    // Check if current line is mostly chords
    if (isChordLine(line) && nextLine && !isChordLine(nextLine)) {
      // Merge chord line with lyrics line
      const merged = mergeChordLineWithLyrics(line, nextLine);
      result.push(merged);
      i++; // Skip the next line
    } else if (!isChordLine(line)) {
      result.push(line);
    }
  }

  return result.join("\n");
}

/**
 * Check if a line is primarily chord notation
 */
function isChordLine(line) {
  if (!line || line.trim().length === 0) return false;

  // Remove chord patterns and see what's left
  const withoutChords = line.replace(
    /[A-Ga-g][#b]?(?:m|M|maj|min|dim|aug|sus|add|2|4|5|6|7|9|11|13)*/g,
    "",
  );
  const originalLength = line.replace(/\s/g, "").length;
  const remainingLength = withoutChords.replace(/\s/g, "").length;

  // If most content was chords, it's a chord line
  return remainingLength < originalLength * 0.3;
}

/**
 * Merge a chord line with the lyrics line below it
 */
function mergeChordLineWithLyrics(chordLine, lyricsLine) {
  const chordPositions = [];

  // Find all chords and their positions
  const regex =
    /([A-Ga-g][#b]?(?:m|M|maj|min|dim|aug|sus|add|2|4|5|6|7|9|11|13)*(?:\/[A-Ga-g][#b]?)?)/g;
  let match;

  while ((match = regex.exec(chordLine)) !== null) {
    chordPositions.push({
      chord: match[1],
      position: match.index,
    });
  }

  // Sort by position
  chordPositions.sort((a, b) => a.position - b.position);

  // Insert chords into lyrics at corresponding positions
  let result = "";
  let lastPos = 0;

  for (const { chord, position } of chordPositions) {
    const lyricsPos = Math.min(position, lyricsLine.length);
    result += lyricsLine.substring(lastPos, lyricsPos) + `[${chord}]`;
    lastPos = lyricsPos;
  }

  result += lyricsLine.substring(lastPos);

  return result;
}

// ============================================
// EXPORT DEFAULT
// ============================================

export default {
  // Constants
  ROOT_NOTES,
  CHORD_QUALITIES,
  ALL_CHORDS,
  ALL_KEYS,
  MAJOR_KEY_PROGRESSIONS,
  MINOR_KEY_PROGRESSIONS,

  // Parsing
  parseChordPro,
  parsePlainText,
  parseLyricsWithChords,

  // Rendering
  renderToChordPro,
  renderToHtml,
  renderToText,

  // Transposition
  transposeSong,
  transposeToKey,
  transposeLyricsText,
  transposeChordName,
  getSemitonesBetweenKeys,

  // Progressions
  getDiatonicChords,
  getCommonProgressions,

  // Utilities
  detectKeyFromLyrics,
  insertChordsIntoLyrics,
  convertChordsOverWordsToInline,
};
