# encoding=utf-8
"""
Module for persisting acoustic measurement values in the MonPaGe database.

This module provides the ValuePersistence class that serves as an interface
between acoustic analysis results and the database storage system. It handles
the conversion of acoustic values to the appropriate database format and manages
the persistence operations through the IndicatorReporting system.
"""

__author__ = "Aaron Randreth"
__copyright__ = "Copyright 2015+, Consortium MonPaGe"
__license__ = "Creative Commons 4.0 By-Nc-Sa"
__maintainer__ = "Roland Trouville"
__email__ = "contact.monpage@gmail.com"
__status__ = "Production"

import math
from typing import Optional

from cotation.acoustic.struct.acoustic_value import AcousticValue
from reporting.indicator_reporting import IndicatorReporting


class ValuePersistence:
	"""
	Class responsible for persisting acoustic values to the database.

	This class provides methods to save, retrieve, and manage acoustic values
	for a specific patient, judge, and session. It acts as a bridge between
	the acoustic analysis components and the database layer.

	Attributes:
		_indicator_ids (Optional[dict[str, int]]): Cached mapping of indicator codes to their database IDs.
		patient_code (str): The unique identifier for the patient.
		judge_code (str): The unique identifier for the judge/clinician.
		session_date (str): The date of the evaluation session.
	"""

	_indicator_ids: Optional[dict[str, int]] = None
	patient_code: str
	judge_code: str
	session_date: str

	def __init__(self, patient_code: str, judge_code: str, session_date: str):
		"""
		Initialize a ValuePersistence instance for a specific patient, judge, and session.

		This constructor sets up the persistence context and configures the
		IndicatorReporting system with the current session information.

		Args:
			patient_code (str): The unique identifier for the patient.
			judge_code (str): The unique identifier for the judge/clinician.
			session_date (str): The date of the evaluation session.
		"""
		self.patient_code: str = patient_code
		self.judge_code: str = judge_code
		self.session_date: str = session_date

		IndicatorReporting.set_current_values(patient_code, judge_code, session_date)

	def get_indicator_ids(self) -> dict[str, int]:
		"""
		Get a mapping of indicator codes to their database IDs.

		This method lazily loads the indicator IDs from the database on first access
		and caches the result for subsequent calls. It queries the indicator table
		to create a dictionary mapping indicator codes to their corresponding database IDs.

		Returns:
			dict[str, int]: A dictionary mapping indicator codes to their database IDs.
		"""
		if self._indicator_ids is not None:
			return self._indicator_ids

		sql = "select code, id from indicator"
		res = IndicatorReporting.participant_result_db.execute(sql).fetchall()

		self._indicator_ids = {indic["code"]: indic["id"] for indic in res}

		return self._indicator_ids

	def saveable(self, value_code: str) -> bool:
		"""
		Check if a value with the given code can be saved to the database.

		This method verifies whether the provided value code corresponds to an
		existing indicator in the database.

		Args:
			value_code (str): The code of the acoustic value to check.

		Returns:
			bool: True if the value can be saved, False otherwise.
		"""
		return value_code in self.get_indicator_ids()

	def save_acoustic_value(self, value: AcousticValue):
		"""
		Save an acoustic value to the database.

		This method persists the given acoustic value to the database by
		delegating to the IndicatorReporting system. It uses the indicator ID
		corresponding to the value's code.

		Args:
			value (AcousticValue): The acoustic value object to save.

		Raises:
			KeyError: If the value's code is not found in the indicator_ids dictionary.
		"""
		IndicatorReporting.save_acoustic_value(
			self.get_indicator_ids()[value.code], value.value, value.error
		)

	@staticmethod
	def get_words_not_recognized() -> Optional[int]:
		"""
		Retrieve the count of words not recognized from the database.

		This method queries the database for the number of words that were not
		recognized during the acoustic analysis, and rounds down to the nearest
		integer.

		Returns:
			Optional[int]: The floor-rounded count of words not recognized,
			               or None if the value is not available.
		"""
		value = IndicatorReporting.get_words_not_recognized()

		return math.floor(value) if value else None

	@staticmethod
	def get_segmental_errors() -> Optional[int]:
		"""
		Retrieve the count of segmental errors from the database.

		This method queries the database for the number of segmental errors
		detected during the acoustic analysis.

		Returns:
			Optional[int]: The count of segmental errors,
			               or None if the value is not available.
		"""
		return IndicatorReporting.get_segmental_errors()

	@staticmethod
	def reset_acoustic_values():
		"""
		Reset all acoustic values for the current patient, judge, and session.

		This method clears all previously saved acoustic values from the database
		for the current context (patient, judge, and session date).
		"""
		IndicatorReporting.reset_acoustic_values()

	@staticmethod
	def commit():
		"""
		Commit all pending changes to the database.

		This method finalizes all database operations by committing the current
		transaction. This ensures that all saved acoustic values are permanently
		stored in the database.
		"""
		IndicatorReporting.participant_result_db.commit()
