# -*- coding: utf-8 -*-
from odoo import models, fields, api, _
from odoo.exceptions import ValidationError
import logging

_logger = logging.getLogger(__name__)


class LimsResult(models.Model):
    _name = 'lims.result'
    _description = 'Resultado de Prueba de Laboratorio'
    _rec_name = 'display_name'
    _order = 'test_id, sequence'

    display_name = fields.Char(
        string='Nombre',
        compute='_compute_display_name',
        store=True
    )
    
    test_id = fields.Many2one(
        'lims.test',
        string='Prueba',
        required=True,
        ondelete='cascade'
    )
    
    # Cambio de parameter_name a parameter_id
    parameter_id = fields.Many2one(
        'lims.analysis.parameter',
        string='Parámetro',
        required=True,
        ondelete='restrict'
    )
    
    # Mantener parameter_name como campo related para compatibilidad
    parameter_name = fields.Char(
        string='Nombre del Parámetro',
        related='parameter_id.name',
        store=True,
        readonly=True
    )
    
    parameter_code = fields.Char(
        string='Código',
        related='parameter_id.code',
        store=True,
        readonly=True
    )
    
    sequence = fields.Integer(
        string='Secuencia',
        default=10
    )
    
    # Campos relacionados del parámetro
    parameter_value_type = fields.Selection(
        related='parameter_id.value_type',
        string='Tipo de Valor',
        store=True,
        readonly=True
    )
    
    parameter_unit = fields.Char(
        related='parameter_id.unit',
        string='Unidad',
        readonly=True
    )
    
    # Valores del resultado
    value_numeric = fields.Float(
        string='Valor Numérico'
    )
    
    value_text = fields.Char(
        string='Valor de Texto'
    )
    
    value_selection = fields.Char(
        string='Valor de Selección'
    )
    
    value_boolean = fields.Boolean(
        string='Valor Sí/No'
    )
    
    # Campo unificado para mostrar el valor
    value_display = fields.Char(
        string='Valor',
        compute='_compute_value_display',
        store=True
    )
    
    # Campos computados para validación de rangos
    applicable_range_id = fields.Many2one(
        'lims.parameter.range',
        compute='_compute_applicable_range',
        string='Rango Aplicable',
        store=False
    )
    
    is_out_of_range = fields.Boolean(
        string='Fuera de Rango',
        compute='_compute_is_out_of_range',
        store=True
    )
    
    is_critical = fields.Boolean(
        string='Valor Crítico',
        compute='_compute_is_out_of_range',
        store=True
    )
    
    notes = fields.Text(
        string='Notas del Técnico'
    )
    
    # Información del paciente (para cálculo de rangos)
    patient_id = fields.Many2one(
        related='test_id.patient_id',
        string='Paciente',
        store=True
    )
    
    test_date = fields.Datetime(
        related='test_id.create_date',
        string='Fecha de la Prueba',
        store=True
    )
    
    result_status = fields.Selection([
        ('normal', 'Normal'),
        ('abnormal', 'Anormal'),
        ('critical', 'Crítico')
    ], string='Estado', compute='_compute_result_status', store=True)
    
    @api.depends('test_id', 'parameter_name')
    def _compute_display_name(self):
        """Calcula el nombre a mostrar."""
        for record in self:
            if record.test_id and record.parameter_name:
                record.display_name = f"{record.test_id.name} - {record.parameter_name}"
            else:
                record.display_name = record.parameter_name or _('Nuevo')
    
    @api.depends('value_numeric', 'value_text', 'value_selection', 'value_boolean', 'parameter_value_type')
    def _compute_value_display(self):
        """Calcula el valor a mostrar según el tipo de dato."""
        for record in self:
            if record.parameter_value_type == 'numeric':
                if record.value_numeric is not False:
                    record.value_display = f"{record.value_numeric} {record.parameter_unit or ''}"
                else:
                    record.value_display = ''
            elif record.parameter_value_type == 'text':
                record.value_display = record.value_text or ''
            elif record.parameter_value_type == 'selection':
                record.value_display = record.value_selection or ''
            elif record.parameter_value_type == 'boolean':
                record.value_display = 'Sí' if record.value_boolean else 'No'
            else:
                record.value_display = ''
    
    @api.depends('parameter_id', 'patient_id', 'test_date')
    def _compute_applicable_range(self):
        """Determina el rango de referencia aplicable según el paciente."""
        for record in self:
            if not record.parameter_id or not record.patient_id:
                record.applicable_range_id = False
                continue
            
            # Calcular edad del paciente en la fecha del test
            if record.test_date:
                age = record.patient_id.get_age_at_date(record.test_date.date())
            else:
                age = record.patient_id.age
            
            # Buscar rango más específico
            domain = [
                ('parameter_id', '=', record.parameter_id.id),
                ('age_min', '<=', age),
                ('age_max', '>=', age),
                '|',
                ('gender', '=', record.patient_id.gender),
                ('gender', '=', 'both')
            ]
            
            # Considerar embarazo si aplica
            if record.patient_id.gender == 'female' and record.patient_id.is_pregnant:
                domain.append(('pregnant', '=', True))
            
            # Ordenar para obtener el más específico primero
            ranges = self.env['lims.parameter.range'].search(
                domain, 
                order='gender desc, pregnant desc',
                limit=1
            )
            
            record.applicable_range_id = ranges[0] if ranges else False
    
    @api.depends('value_numeric', 'applicable_range_id', 'parameter_value_type')
    def _compute_is_out_of_range(self):
        """Determina si el valor está fuera del rango normal y si es crítico."""
        for record in self:
            record.is_out_of_range = False
            record.is_critical = False
            
            # Solo aplica para valores numéricos
            if record.parameter_value_type != 'numeric' or record.value_numeric is False:
                continue
            
            if not record.applicable_range_id:
                continue
            
            range_obj = record.applicable_range_id
            status = range_obj.get_value_status(record.value_numeric)
            
            record.is_out_of_range = (status != 'normal')
            record.is_critical = (status == 'critical')
    
    @api.depends('parameter_id', 'value_numeric', 'is_out_of_range', 'is_critical', 'parameter_value_type')
    def _compute_result_status(self):
        """Calcula el estado visual del resultado."""
        for record in self:
            if record.parameter_value_type != 'numeric':
                record.result_status = 'normal'
            elif record.is_critical:
                record.result_status = 'critical'
            elif record.is_out_of_range:
                record.result_status = 'abnormal'
            else:
                record.result_status = 'normal'
    
    @api.constrains('value_numeric', 'value_text', 'value_selection', 'value_boolean', 'parameter_value_type')
    def _check_value_type(self):
        """Asegura que el valor ingresado corresponda al tipo de parámetro."""
        for record in self:
            if not record.parameter_id:
                continue
                
            value_type = record.parameter_value_type
            has_value = False
            
            if value_type == 'numeric':
                has_value = record.value_numeric is not False
                if record.value_text or record.value_selection:
                    raise ValidationError(
                        _('Para parámetros numéricos solo se debe ingresar el valor numérico.')
                    )
            elif value_type == 'text':
                has_value = bool(record.value_text)
                if record.value_numeric is not False or record.value_selection or record.value_boolean:
                    raise ValidationError(
                        _('Para parámetros de texto solo se debe ingresar el valor de texto.')
                    )
            elif value_type == 'selection':
                has_value = bool(record.value_selection)
                if record.value_numeric is not False or record.value_text or record.value_boolean:
                    raise ValidationError(
                        _('Para parámetros de selección solo se debe elegir una opción.')
                    )
            elif value_type == 'boolean':
                has_value = True  # Boolean siempre tiene valor (True o False)
                if record.value_numeric is not False or record.value_text or record.value_selection:
                    raise ValidationError(
                        _('Para parámetros Sí/No solo se debe marcar el checkbox.')
                    )
            
            if not has_value and record.parameter_id:
                raise ValidationError(
                    _('Debe ingresar un valor para el resultado del parámetro %s.') % record.parameter_name
                )
    
    @api.onchange('parameter_id')
    def _onchange_parameter_id(self):
        """Limpia los valores cuando se cambia el parámetro."""
        if self.parameter_id:
            # Limpiar todos los valores
            self.value_numeric = False
            self.value_text = False
            self.value_selection = False
            self.value_boolean = False
            
            # Si es selección, obtener las opciones
            if self.parameter_value_type == 'selection' and self.parameter_id.selection_values:
                # Esto se usará en las vistas para mostrar las opciones dinámicamente
                pass