# -*- coding: utf-8 -*-

from odoo import models, fields, api, _
from odoo.exceptions import ValidationError


class SkeenVenta(models.Model):
    _name = 'skeen.venta'
    _description = 'Venta SKEEN'
    _order = 'create_date desc'
    _inherit = ['mail.thread']

    # Referencia
    name = fields.Char(string='Referencia', readonly=True, copy=False)

    # Paciente
    partner_id = fields.Many2one('res.partner', string='Paciente', required=True,
                                  domain=[('is_patient', '=', True)])
    partner_phone = fields.Char(related='partner_id.phone', string='Teléfono', readonly=True)

    # Origen
    cita_id = fields.Many2one('skeen.cita', string='Cita Origen')

    # Líneas de venta
    line_ids = fields.One2many('skeen.venta.line', 'venta_id', string='Líneas')

    # Totales
    subtotal = fields.Float(string='Subtotal', compute='_compute_totals', store=True)
    discount = fields.Float(string='Descuento', default=0.0)
    tax = fields.Float(string='Impuesto', default=0.0)
    total = fields.Float(string='Total', compute='_compute_totals', store=True)
    amount_paid = fields.Float(string='Pagado', default=0.0)
    amount_due = fields.Float(string='Adeudo', compute='_compute_totals', store=True)

    # Estado
    state = fields.Selection([
        ('draft', 'Borrador'),
        ('confirmed', 'Confirmada'),
        ('paid', 'Pagada'),
        ('partial', 'Parcial'),
        ('cancelled', 'Cancelada'),
    ], string='Estado', default='draft', tracking=True)

    # Monedero
    points_redeemed = fields.Integer(string='Puntos Redimidos', default=0)
    points_discount = fields.Float(string='Descuento por Puntos', default=0.0)

    # Devolución
    refunded = fields.Boolean(string='Devuelta', default=False)
    refund_amount = fields.Float(string='Monto Devuelto', default=0.0)
    refund_reason = fields.Text(string='Motivo Devolución')
    refunded_at = fields.Datetime(string='Fecha Devolución')

    # Fechas
    date = fields.Date(string='Fecha', required=True, default=fields.Date.today)
    create_date = fields.Datetime(string='Fecha Creación', readonly=True, default=fields.Datetime.now)

    # Notas
    notes = fields.Text(string='Notas')

    @api.depends('line_ids.subtotal', 'discount', 'tax', 'amount_paid', 'points_discount')
    def _compute_totals(self):
        for venta in self:
            subtotal = sum(line.subtotal for line in venta.line_ids)
            venta.subtotal = subtotal
            venta.total = max(0.0, subtotal - venta.discount - venta.points_discount + venta.tax)
            venta.amount_due = max(0.0, venta.total - venta.amount_paid)

    @api.model_create_multi
    def create(self, vals_list):
        for vals in vals_list:
            if not vals.get('name'):
                vals['name'] = self.env['ir.sequence'].next_by_code('skeen.venta') or 'VENTA-001'
        return super(SkeenVenta, self).create(vals_list)

    def action_confirm(self):
        self.write({'state': 'confirmed'})

    def action_mark_paid(self):
        for venta in self:
            venta.write({
                'state': 'paid',
                'amount_paid': venta.total,
            })
            if venta.cita_id:
                venta.cita_id.write({
                    'payment_state': 'paid',
                    'amount_paid': venta.total,
                })
            # Acumular puntos (proporcional a líneas que generan monedero)
            monedero = self.env['skeen.monedero'].search([('partner_id', '=', venta.partner_id.id)], limit=1)
            if not monedero:
                monedero = self.env['skeen.monedero'].create({'partner_id': venta.partner_id.id})
            subtotal_total = sum(l.subtotal for l in venta.line_ids)
            elegible = sum(
                l.subtotal for l in venta.line_ids
                if (l.service_id and l.service_id.genera_monedero) or (l.item_id and l.item_id.genera_monedero)
            )
            points = int(venta.total * (elegible / subtotal_total) / 10) if subtotal_total else 0
            if points > 0:
                monedero.add_points(points, description=f'Venta {venta.name}', reference=venta.name)

    def action_cancel(self):
        self.write({'state': 'cancelled'})

    def action_refund(self, amount=None, reason=''):
        """Devolución total o parcial de una venta. Reversible: no borra nada.

        - Reduce amount_paid en `amount` (o en todo lo pagado si amount es None).
        - Revierte puntos del monedero en proporción ($10 = 1 pt), con piso en 0.
        - Estado resultante: cancelled si no queda pagado; partial si queda parcial; paid si seguía cubierto.
        """
        self.ensure_one()
        if self.state not in ('confirmed', 'paid', 'partial'):
            raise ValidationError(_('Solo se pueden devolver ventas confirmadas o pagadas'))
        if self.amount_paid <= 0:
            raise ValidationError(_('La venta no tiene monto pagado para devolver'))

        refund_amount = float(amount or 0)
        if refund_amount <= 0:
            refund_amount = self.amount_paid  # devolución total de lo pagado
        refund_amount = min(refund_amount, self.amount_paid)
        if refund_amount <= 0:
            raise ValidationError(_('Monto de devolución inválido'))

        new_amount_paid = round(self.amount_paid - refund_amount, 2)

        # Revertir puntos acumulados en proporción a lo devuelto ($10 MXN = 1 punto)
        points_to_reverse = int(refund_amount / 10)
        if points_to_reverse > 0:
            monedero = self.env['skeen.monedero'].search([('partner_id', '=', self.partner_id.id)], limit=1)
            if monedero and monedero.points > 0:
                monedero.adjust_points(
                    -points_to_reverse,
                    description=f'Devolución venta {self.name}',
                    reference=self.name,
                )

        # Estado resultante
        if new_amount_paid <= 0:
            new_state = 'cancelled'
        elif new_amount_paid < self.total:
            new_state = 'partial'
        else:
            new_state = 'paid'

        self.write({
            'amount_paid': new_amount_paid,
            'state': new_state,
            'refunded': True,
            'refund_amount': self.refund_amount + refund_amount,
            'refund_reason': reason or self.refund_reason or '',
            'refunded_at': fields.Datetime.now(),
        })

        # Reflejar en la cita origen si existe
        if self.cita_id:
            if new_amount_paid <= 0:
                cita_state = 'not_paid'
            elif new_amount_paid < self.total:
                cita_state = 'partial'
            else:
                cita_state = 'paid'
            self.cita_id.write({'amount_paid': new_amount_paid, 'payment_state': cita_state})

        return {
            'refund_amount': refund_amount,
            'points_reversed': points_to_reverse,
            'new_state': new_state,
            'new_amount_paid': new_amount_paid,
        }

    def action_apply_wallet(self, points):
        """Redime puntos del monedero como descuento en la venta"""
        self.ensure_one()
        monedero = self.env['skeen.monedero'].search([('partner_id', '=', self.partner_id.id)], limit=1)
        if not monedero or monedero.points < points:
            raise ValidationError(_('Puntos insuficientes'))

        # Máximo 50% del total
        max_discount = self.total * 0.5
        point_value = min(points, int(max_discount))

        monedero.redeem_points(point_value, description=f'Redención venta {self.name}', reference=self.name)
        self.write({
            'points_redeemed': point_value,
            'points_discount': point_value * 1.0,
        })
        return True


class SkeenVentaLine(models.Model):
    _name = 'skeen.venta.line'
    _description = 'Línea de Venta SKEEN'

    venta_id = fields.Many2one('skeen.venta', string='Venta', required=True, ondelete='cascade')
    service_id = fields.Many2one('skeen.servicio', string='Servicio')
    item_id = fields.Many2one('skeen.inventario.item', string='Artículo inventario')
    description = fields.Char(string='Descripción')
    quantity = fields.Float(string='Cantidad', default=1.0, required=True)
    price_unit = fields.Float(string='Precio Unitario', required=True)
    subtotal = fields.Float(string='Subtotal', compute='_compute_subtotal', store=True)
    # Artículo recetado por médico (base para comisiones)
    is_prescribed = fields.Boolean(string='Recetado', default=False,
                                   help='Marcar si este artículo fue recetado por un médico (entra al cálculo de comisiones).')
    prescribed_by_id = fields.Many2one('hr.employee', string='Recetado por')

    _sql_constraints = [
        ('service_o_item', 'CHECK(service_id IS NOT NULL OR item_id IS NOT NULL)',
         'La línea debe tener un servicio o un artículo de inventario.')
    ]

    @api.constrains('service_id', 'item_id')
    def _check_service_o_item(self):
        for line in self:
            if bool(line.service_id) == bool(line.item_id):
                raise ValidationError(_('La línea debe tener un servicio O un artículo, no ambos.'))

    @api.depends('quantity', 'price_unit')
    def _compute_subtotal(self):
        for line in self:
            line.subtotal = line.quantity * line.price_unit

    @api.onchange('service_id')
    def _onchange_service(self):
        if self.service_id:
            self.price_unit = self.service_id.price
            self.description = self.service_id.name

    @api.onchange('item_id')
    def _onchange_item(self):
        if self.item_id:
            self.price_unit = self.item_id.price
            self.description = self.item_id.name


class SkeenCorteCaja(models.Model):
    _name = 'skeen.corte.caja'
    _description = 'Corte de Caja SKEEN'
    _order = 'date desc'

    name = fields.Char(string='Referencia', readonly=True, copy=False)
    date = fields.Date(string='Fecha', required=True, default=fields.Date.today, index=True)
    user_id = fields.Many2one('res.users', string='Usuario', default=lambda self: self.env.uid)

    # Apertura
    opening_cash = fields.Float(string='Efectivo Apertura', default=0.0)

    # Ventas del día
    venta_ids = fields.One2many('skeen.venta', 'date', string='Ventas del Día',
                                 domain=[('state', 'in', ('confirmed', 'paid', 'partial'))])

    # Totales por método de pago
    total_cash = fields.Float(string='Efectivo', compute='_compute_totals', store=True)
    total_card = fields.Float(string='Tarjeta', compute='_compute_totals', store=True)
    total_transfer = fields.Float(string='Transferencia', compute='_compute_totals', store=True)
    total_other = fields.Float(string='Otros', compute='_compute_totals', store=True)
    total_sales = fields.Float(string='Total Ventas', compute='_compute_totals', store=True)

    # Cierre
    closing_cash = fields.Float(string='Efectivo Cierre', default=0.0)
    difference = fields.Float(string='Diferencia', compute='_compute_difference', store=True)

    # Estado
    state = fields.Selection([
        ('open', 'Abierto'),
        ('closed', 'Cerrado'),
    ], string='Estado', default='open')

    notes = fields.Text(string='Notas')

    @api.depends('date')
    def _compute_totals(self):
        for corte in self:
            ventas = self.env['skeen.venta'].search([
                ('date', '=', corte.date),
                ('state', 'in', ('confirmed', 'paid', 'partial')),
            ])
            # Sumar también pagos por método
            pagos = self.env['skeen.pago'].search([
                ('payment_date', '>=', corte.date.strftime('%Y-%m-%d 00:00:00')),
                ('payment_date', '<=', corte.date.strftime('%Y-%m-%d 23:59:59')),
                ('state', '=', 'completed'),
            ])
            cash = sum(p.amount for p in pagos if p.payment_method == 'cash')
            card = sum(p.amount for p in pagos if p.payment_method in ('card', 'stripe', 'mercadopago'))
            transfer = sum(p.amount for p in pagos if p.payment_method == 'transfer')
            other = sum(p.amount for p in pagos if p.payment_method not in ('cash', 'card', 'stripe', 'mercadopago', 'transfer'))

            corte.total_cash = cash
            corte.total_card = card
            corte.total_transfer = transfer
            corte.total_other = other
            corte.total_sales = cash + card + transfer + other

    @api.depends('opening_cash', 'total_cash', 'closing_cash')
    def _compute_difference(self):
        for corte in self:
            expected = corte.opening_cash + corte.total_cash
            corte.difference = corte.closing_cash - expected

    @api.model_create_multi
    def create(self, vals_list):
        for vals in vals_list:
            if not vals.get('name'):
                vals['name'] = self.env['ir.sequence'].next_by_code('skeen.corte.caja') or 'CORTE-001'
        return super(SkeenCorteCaja, self).create(vals_list)

    def action_close(self):
        self.write({'state': 'closed'})

    def action_reopen(self):
        self.write({'state': 'open'})
