#
# Copyright (c) 2025, mstoeck3
# All rights reserved.
#
# This source code is licensed under the BSD-3-Clause license found in the
# LICENSE file in the root directory of this source tree.
#

from semeion.interfaces.llm.llm_client import LLMInterface
from PySide6.QtCore import QObject, Signal, Slot, QThread


class LLMWorker(QObject): # initializes once and is queried repeatedly
    result = Signal(object)
    error = Signal(str)
    status = Signal(str)

    def __init__(self):
        super().__init__()
        self.client = None
    
    @Slot()
    def initialize(self):
        self.client = LLMInterface()
        self.status.emit("LLM: ready")

    @Slot(str)
    def run_query(self, prompt: str):
        if self.client is None:
            self.error.emit("LLM client not initialized")
            return
        try:
            self.status.emit("LLM: started")
            raw_response = self.client.simple_query(prompt)
            response = raw_response.choices[0].message.content or ""
            self.status.emit("LLM: finished")
            self.result.emit(response)
        except Exception as e:
            self.error.emit(str(e))


class LLMService(QObject):
    """persistent service"""
    query_finished = Signal(object)
    query_error = Signal(str)
    query_status = Signal(str)

    # signal to send to active worker
    _do_query = Signal(str)

    def __init__(self):
        super().__init__()
        self._thread = QThread()
        self.worker = LLMWorker()
        
        self.worker.moveToThread(self._thread)
        
        self._thread.started.connect(self.worker.initialize)
        
        self._do_query.connect(self.worker.run_query)
        
        self.worker.result.connect(self.query_finished.emit)
        self.worker.error.connect(self.query_error.emit)
        self.worker.status.connect(self.query_status.emit)
        
        self._thread.start()
        
    @Slot(str)
    def query(self, prompt: str):
        self._do_query.emit(prompt) # start query via signal
    
    def stop(self):
        self._thread.quit()
        self._thread.wait()
        self.worker.deleteLater()
        self._thread.deleteLater()