import json
import os
import random
import struct
import time
import pvporcupine
import simpleaudio as sa
import vosk
import yaml
from fuzzywuzzy import fuzz
from pvrecorder import PvRecorder
import config
import tts
from const import SYSTEM_PROMPT

from llm_models.mini_4o_model import ModelClient

from services.browser_service import open_browser, open_youtube, open_gpt, open_network, toggle_keyboard_layout, \
    toggle_fullscreen, move_browser_to_other_monitor, toggle_play
from services.lamp_service import turn_on, turn_off
from services.news_service import get_rates
from services.pc_service import pc_off, enter_sleep_mode, cancel_sleep_mode, switch_to_speakers, switch_to_headphones, \
    set_volume
from services.weather_service import WeatherFetcher

CDIR = os.getcwd()
VA_CMD_LIST = yaml.safe_load(
    open('commands.yaml', 'rt', encoding='utf8'),
)

# PORCUPINE
porcupine = pvporcupine.create(
    access_key=config.PICOVOICE_TOKEN,
    keyword_paths=['Sonya_en_windows_v3_0_0.ppn'],
    sensitivities=[1]
)

# VOSK
model = vosk.Model("model_small")
samplerate = 16000
device = config.MICROPHONE_INDEX
kaldi_rec = vosk.KaldiRecognizer(model, samplerate)

client = ModelClient()
weatherFetcher = WeatherFetcher()


def print_yellow(text):
    """Выводит текст в желтом цвете."""
    print(f"\033[93m{text}\033[0m")


def print_red(text):
    """Выводит текст в красном цвете."""
    print(f"\033[91m{text}\033[0m")


def play(phrase, wait_done=True):
    global recorder
    filename = f"{CDIR}\\sound\\"

    if phrase == "greet":
        filename += f"start.wav"
    if phrase == "cancel":
        filename += f"cancel.wav"

    if wait_done:
        recorder.stop()

    wave_obj = sa.WaveObject.from_wave_file(filename)
    play_obj = wave_obj.play()

    if wait_done:
        play_obj.wait_done()
        recorder.start()


def recognize_cmd(cmd: str):
    rc = {'cmd': '', 'percent': 0}
    for c, v in VA_CMD_LIST.items():

        for x in v:
            vrt = fuzz.ratio(cmd, x)
            if vrt > rc['percent']:
                rc['cmd'] = c
                rc['percent'] = vrt

    return rc


def execute_cmd(cmd: str, voice: str):
    if cmd == 'open_browser':
        tts.va_speak(open_browser())
    elif cmd == 'open_youtube':
        tts.va_speak(open_youtube())
    elif cmd == 'lamp_on':
        tts.va_speak(turn_on())
    elif cmd == 'lamp_off':
        tts.va_speak(turn_off())
    # elif cmd == 'pc_off':
    #     tts.va_speak(pc_off(0))
    elif cmd == 'enter_sleep_mode':
        tts.va_speak(enter_sleep_mode())
    elif cmd == 'switch_to_speakers':
        tts.va_speak(switch_to_speakers())
    elif cmd == 'switch_to_headphones':
        tts.va_speak(switch_to_headphones())
    elif cmd == 'normalize_volume':
        tts.va_speak(set_volume(40))
    elif cmd == 'max_volume':
        tts.va_speak(set_volume(100))
    elif cmd == 'cancel_sleep_mode':
        tts.va_speak(cancel_sleep_mode())
    elif cmd == 'open_gpt':
        tts.va_speak(open_gpt())
    elif cmd == 'open_network':
        tts.va_speak(open_network())
    elif cmd == 'get_rates':
        tts.va_speak(get_rates(client, voice))
    elif cmd == 'get_weather':
        tts.va_speak(weatherFetcher.get_weather(client))
    elif cmd == 'toggle_keyboard_layout':
        tts.va_speak(toggle_keyboard_layout())
    elif cmd == 'toggle_fullscreen':
        tts.va_speak(toggle_fullscreen())
    elif cmd == 'toggle_play':
        tts.va_speak(toggle_play())
    elif cmd == 'move_browser_to_other_monitor':
        tts.va_speak(move_browser_to_other_monitor())
    elif cmd == 'off':
        responses = ["Завершаю работу, до встречи!", "Выключаюсь, до свидания!", "Пока!"]
        tts.va_speak(random.choice(responses))
        porcupine.delete()
        exit(0)


def va_respond(voice: str):
    if voice:
        print(f"Распознано: {voice}")

        cmd = recognize_cmd(voice)
        print(cmd)

        if len(cmd['cmd'].strip()) <= 0:
            return False
        elif cmd['percent'] < 70 or cmd['cmd'] not in VA_CMD_LIST.keys():
            response_generator = client.get_response(
                message=voice,
                sys_prompt=SYSTEM_PROMPT
            )

            full_response = []
            last = ''

            for response in response_generator:
                part = response.replace(last, '')
                print(part, end='', flush=True)
                full_response.append(part)
                last = response

            print()

            complete_response = ''.join(full_response)
            tts.va_speak(complete_response)
            client.history.append((voice, complete_response))
            return True
        else:
            execute_cmd(cmd['cmd'], voice)
            return True


def get_current_microphone():
    """ Получить текущее устройство микрофона. """
    return recorder.selected_device


recorder = PvRecorder(device_index=config.MICROPHONE_INDEX, frame_length=porcupine.frame_length)
recorder.start()
print_yellow('Using device: %s' % get_current_microphone())

print(f"Sonya (v1.0) к твоим услугам ...")

ltc = time.time() - 1000
listening = False

# Сохраняем текущее устройство
current_device = get_current_microphone()

while True:
    try:
        # Проверяем, изменилось ли устройство
        if get_current_microphone() != current_device:
            current_device = get_current_microphone()
            print_yellow(f"Using device: {current_device}")

        pcm = recorder.read()
        # Для улучшения звука с микрофона:
        # Применение усиления к каждому PCM образцу
        # pcm = [int(sample * 2) for sample in pcm]
        # Устранение фонового шума
        # pcm = nr.reduce_noise(y=pcm, sr=samplerate)
        keyword_index = porcupine.process(pcm)

        if keyword_index >= 0:
            recorder.stop()
            play("greet", True)
            print("Yes, sir.")
            recorder.start()
            ltc = time.time()
            listening = True

        while time.time() - ltc <= 10:
            pcm = recorder.read()
            sp = struct.pack("h" * len(pcm), *pcm)

            if kaldi_rec.AcceptWaveform(sp):
                if va_respond(json.loads(kaldi_rec.Result())["text"]):
                    ltc = time.time()
                break

        if listening and time.time() - ltc > 10:
            play("cancel", True)
            client.history.clear()
            listening = False

    except KeyboardInterrupt:
        print_red("Программа завершена пользователем.")
        break
    except OSError as err:
        print_red(f"Ошибка при чтении с устройства: {err}")
    except Exception as err:
        print_red(f"Unexpected {err=}, {type(err)=}")
        raise

Как вместо микрофона использовать звуковые данные полученные по tsp socket

    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_socket.bind((HOST, PORT))
    server_socket.listen(1)