import socket
import vosk
import json
import yaml
from fuzzywuzzy import fuzz
import tts
from const import SYSTEM_PROMPT
from llm_models.mini_4o_model import ModelClient
from services.lamp_service import turn_on, turn_off
from services.pc_service import enter_sleep_mode
import time
import pygame

HOST = '0.0.0.0'
PORT = 6543
ANDROID_ADDR = None

client = ModelClient()

# Load command list from YAML file
VA_CMD_LIST = yaml.safe_load(open('commands.yaml', 'rt', encoding='utf8'))

WAKE_WORD = "соня"
LISTEN_DURATION = 10

pygame.mixer.init()


def play_sound(sound_file: str) -> None:
    pygame.mixer.music.load(sound_file)
    pygame.mixer.music.play()


def recognize_command(command: str) -> dict:
    best_match = {'cmd': '', 'percent': 0}
    for command_key, variations in VA_CMD_LIST.items():
        for variation in variations:
            similarity = fuzz.ratio(command, variation)
            if similarity > best_match['percent']:
                best_match['cmd'] = command_key
                best_match['percent'] = similarity
    return best_match


def execute_command(command: str) -> None:
    if command == 'enter_sleep_mode':
        tts.va_speak(enter_sleep_mode())
    elif command == 'lamp_on':
        tts.va_speak(turn_on())
    elif command == 'lamp_off':
        tts.va_speak(turn_off())


def process_recognized_text(recognized_text: str) -> None:
    command = recognize_command(recognized_text)
    print(command)

    if not command['cmd'].strip():
        return

    if command['percent'] < 70 or command['cmd'] not in VA_CMD_LIST.keys():
        response = generate_response(recognized_text)
        tts.va_speak(response)
        client.history.append((recognized_text, response))
    else:
        execute_command(command['cmd'])


def generate_response(input_text: str) -> str:
    response_generator = client.get_response(message=input_text, 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)
    return complete_response


def listen_for_commands(conn: socket.socket, recognizer: vosk.KaldiRecognizer) -> None:
    start_time = time.time()
    while time.time() - start_time < LISTEN_DURATION:
        data = conn.recv(1024)
        if not data:
            break

        if recognizer.AcceptWaveform(data):
            result = json.loads(recognizer.Result())
            recognized_text = result['text']
            if recognized_text:
                print("Recognized text:", recognized_text)
                process_recognized_text(recognized_text)


def send_to_android(param):
    # Установите соединение с Android
    try:
        android_socket = socket.socket(socket.AF_INET,
                                       socket.SOCK_STREAM)  # Здесь добавлен импорт модуля socket
        android_socket.connect((ANDROID_ADDR, 51411))
        output = android_socket.makefile('wb')  # Создаем поток записи для отправки данных
        output.write(param)  # Отправляем уведомление о начале режима
        output.close()
        android_socket.close()
    except Exception as e:
        print(e)


def main() -> None:
    global ANDROID_ADDR
    model = vosk.Model("model_small")
    recognizer = vosk.KaldiRecognizer(model, 44100)

    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_socket.bind((HOST, PORT))
    server_socket.listen(1)
    print("Server started, waiting for connection...")

    while True:
        conn, addr = server_socket.accept()
        ANDROID_ADDR = addr[0]
        print(f"Connected to {addr}")

        while True:
            data = conn.recv(1024)
            if not data:
                break

            if recognizer.AcceptWaveform(data):
                result = json.loads(recognizer.Result())
                recognized_text = result['text']

                if recognized_text:
                    print("Recognized text:", recognized_text)

                    if WAKE_WORD in recognized_text.lower():
                        print("Wake word detected, entering command listening mode.")

                        send_to_android(b'ACTIVATED')

                        play_sound('sound/start.wav')

                        command_text = recognized_text.lower().replace(WAKE_WORD, '', 1).strip()
                        if command_text:
                            process_recognized_text(command_text)
                        listen_for_commands(conn, recognizer)

                        send_to_android(b'DEACTIVATED')
                        play_sound('sound/cancel.wav')

        conn.close()
        print("Connection closed, waiting for new connection...")


if __name__ == "__main__":
    main()