import os
import logging
import openai
import sys
import lxml.etree as ET
import librosa
import noisereduce as nr
import numpy as np
import torch
import asyncio
import soundfile as sf
from torchaudio.transforms import MelSpectrogram
from transformers import Wav2Vec2Tokenizer, Wav2Vec2ForCTC, HubertForCTC
from music21 import stream, note, converter, instrument
from scipy import signal
from pydub import AudioSegment
from pydub.playback import play

# Настройка логирования
logging.basicConfig(level=logging.DEBUG)  # Переключаем на DEBUG для детального логирования

# Загрузка ключа API из переменных среды
openai.api_key = os.environ.get('OPENAI_API_KEY')

def initialize_openai():
    """
    Проверка подключения к OpenAI с использование ключа API.
    """
    if openai.api_key:
        try:
            response = openai.Completion.create(model="gpt-4", prompt="Hello, world!", max_tokens=5)
            logging.info("Подключение к OpenAI API успешно")
        except openai.error.AuthenticationError:
            logging.error("Ошибка аутентификации: Неверный ключ OpenAI API")
        except Exception as e:
            logging.error(f"Ошибка при подключении к OpenAI API: {e}")
    else:
        logging.warning("Ключ OpenAI API не найден. API будет проигнорирован")
        # НЕ поднимаем ValueError здесь

initialize_openai()

async def preprocess_audio(file_path):
    """
    Загрузка и предварительная обработка аудио (нормализация, шумоподавление).
    """
    try:
        logging.info(f"Loading and preprocessing audio from {file_path}")
        y, sr = librosa.load(file_path, sr=None)
        y = librosa.util.normalize(y)
        y = nr.reduce_noise(y=y, sr=sr)
        y = np.asfortranarray(y)
        logging.info("Audio preprocessing completed")
        return y, sr
    except Exception as e:
        logging.error(f"Error preprocessing audio: {e}")
        return None, None

async def extract_features(y, sr):
    """
    Извлечение мел-спектрограммы из аудио.
    """
    try:
        logging.info("Extracting features")
        mel_spec = MelSpectrogram()(torch.tensor(y).unsqueeze(0))
        logging.info("Feature extraction completed")
        return mel_spec
    except Exception as e:
        logging.error(f"Error extracting features: {e}")
        return None

def save_musicxml(score, output_path):
    """
    Сохранение партитуры в формате MusicXML.
    """
    try:
        logging.info(f"Saving MusicXML file to {output_path}")
        tree = ET.ElementTree(score)
        tree.write(output_path, encoding='UTF-8', xml_declaration=True, pretty_print=True)
        logging.info(f"MusicXML file saved successfully: {output_path}")
    except Exception as e:
        logging.error(f"Error saving MusicXML file: {e}")

def analyze_audio(y, sr):
    """
    Анализ аудио файла для определения темпа и тактов.
    """
    try:
        logging.info("Analyzing audio")
        tempo, beat_frames = librosa.beat.beat_track(y=y, sr=sr)
        logging.info(f"Audio analysis completed: tempo={tempo}, beat_frames={beat_frames}")
    except Exception as e:
        logging.error(f"Error analyzing audio: {e}")

def generate_text_descriptions(y, sr):
    """
    Генерация текстовых описаний аудио.
    """
    try:
        logging.info("Generating text descriptions")
        description = f"Audio has a sample rate of {sr} and length of {len(y)} samples."
        logging.info(f"Text descriptions generated: {description}")
    except Exception as e:
        logging.error(f"Error generating text descriptions: {e}")

def visualize_score(score, output_image_path):
    """
    Визуализация партитуры и сохранение изображения.
    """
    try:
        logging.info("Visualizing score")
        tree = ET.ElementTree(score)
        tree.write(output_image_path, encoding='UTF-8', xml_declaration=True, pretty_print=True)
        logging.info(f"Score visualization completed: {output_image_path}")
    except Exception as e:
        logging.error(f"Error visualizing score: {e}")

def transformer_features_wav2vec2(y, sr):
    """
    Извлечение признаков и транскрипция аудио с использованием Wav2Vec2.
    """
    try:
        tokenizer = Wav2Vec2Tokenizer.from_pretrained("facebook/wav2vec2-base-960h")
        model = Wav2Vec2ForCTC.from_pretrained("facebook/wav2vec2-base-960h")
        input_values = tokenizer(y.tolist(), return_tensors="pt", padding=True).input_values
        logits = model(input_values).logits
        predicted_ids = torch.argmax(logits, dim=-1)
        transcription = tokenizer.batch_decode(predicted_ids)[0]
        logging.info(f"Transcription: {transcription}")
        return transcription
    except Exception as e:
        logging.error(f"Error extracting transformer features with Wav2Vec2: {e}")
        return None

async def process_pipeline(file_path, output_path, output_image_path):
    """
    Основной конвейер обработки аудио: включает предварительную обработку, извлечение признаков,
    транскрипцию, создание MusicXML, анализ и визуализацию.
    """
    try:
        logging.info("Starting audio processing pipeline")

        # Предварительная обработка аудио
        y, sr = await preprocess_audio(file_path)
        if not y or not sr:
            logging.error("Failed to preprocess audio")
            return

        # Извлечение признаков
        features = await extract_features(y, sr)
        if not features:
            logging.error("Failed to extract features")
            return

        # Транскрипция используя Wav2Vec2
        transcription = transformer_features_wav2vec2(y, sr)
        if not transcription:
            logging.error("Failed to transcribe audio")
            return

        # Создание простого XML-документа
        logging.info("Creating simple MusicXML document")
        score = ET.Element("score-partwise", version="3.1")
        part = ET.SubElement(score, "part", id="P1")
        measure = ET.SubElement(part, "measure", number="1")
        note_elem = ET.SubElement(measure, "note")
        pitch_elem = ET.SubElement(note_elem, "pitch")
        ET.SubElement(pitch_elem, "step").text = "C"
        ET.SubElement(pitch_elem, "octave").text = "4"
        ET.SubElement(note_elem, "duration").text = "1"
        ET.SubElement(note_elem, "type").text = "quarter"

        # Сохранение MusicXML файла
        save_musicxml(score, output_path)

        # Анализ аудио
        analyze_audio(y, sr)

        # Генерация текстовых описаний
        generate_text_descriptions(y, sr)

        # Визуализация партитуры
        visualize_score(score, output_image_path)

    except Exception as e:
        logging.error(f"Error in audio processing pipeline: {e}")

if __name__ == "__main__":
    file_path = r"C:\Users\ARTYOM\Documents\Python\Bring_it_up.mp3"
    output_path = r"C:\Users\ARTYOM\Documents\Python\Bring_it_up.musicxml"
    output_image_path = r"C:\Users\ARTYOM\Documents\Python\Bring_it_up.png"

    try:
        loop = asyncio.get_event_loop()
        loop.run_until_complete(process_pipeline(file_path, output_path, output_image_path))
    except Exception as e:
        logging.error(f"Failed to run audio processing pipeline: {e}")