import logging
from datetime import date

from django.contrib.postgres.indexes import GinIndex
from django.contrib.postgres.search import SearchVectorField
from django.core import serializers
from django.db import connection, models
from django.db.models.constraints import UniqueConstraint
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.utils import timezone
from django.utils.translation import gettext as _
from mpublico.choices import *

from . import helpers
from .stop_words import STOP_WORDS
logger = logging.getLogger(__name__)

class Empresa(models.Model):
    created_at = models.DateTimeField(
        _('Fecha'), default=timezone.now, blank=True)
    updated_at = models.DateTimeField(auto_now=True, null=True)
    CodigoEmpresa = models.CharField(_('Código empresa'), unique=True)
    NombreEmpresa = models.CharField(_('Razón social'), max_length=1000)

    class Meta:
        verbose_name = 'Empresa'
        verbose_name_plural = 'Empresa'

    class Meta:
        indexes = [
            models.Index(fields=['NombreEmpresa', 'CodigoEmpresa']),
            models.Index(fields=['NombreEmpresa']),
        ]

    def __str__(self):
        return self.NombreEmpresa

class Licitacion(models.Model):
    created_at = models.DateTimeField(
        _('Fecha'), default=timezone.now, blank=True)
    updated_at = models.DateTimeField(auto_now=True, null=True)
    CodigoExterno = models.CharField(_('Código externo'), unique=True)
    CodigoEstado = models.IntegerField(
        _('Código estado'), choices=ESTADO_LICITACION_CHOICES, blank=True, null=True)
    FechaCierre = models.DateTimeField(
        _('Fecha Cierre'), blank=True, null=True)
    Nombre = models.CharField(_('Nombre Producto'), blank=True, null=True)
    date = models.DateField(_("Fecha Publicación"), blank=True, null=True)
    empresa = models.ForeignKey(
        Empresa, on_delete=models.CASCADE, blank=True, null=True)

    class Meta:
        verbose_name = 'Licitacion Base'
        verbose_name_plural = 'Licitacion Base'

    def __str__(self):
        return self.CodigoExterno

class LicitacionDetalle(models.Model):
    licitacion = models.ForeignKey(
        Licitacion, on_delete=models.CASCADE, related_name='licitacion_detalle')
    created_at = models.DateTimeField(
        _('Fecha'), default=timezone.now, blank=True)
    updated_at = models.DateTimeField(auto_now=True, null=True)
    active = models.BooleanField(default=True)
    CodigoExterno = models.CharField()
    CodigoEstado = models.IntegerField(
        _('Código estado'), choices=ESTADO_LICITACION_CHOICES)
    Descripcion = models.CharField(
        _('Descripción de la Licitación u objetode la contratación'), null=True, blank=True)
    Estado = models.CharField(
        _('Estado en el que se encuentra la Licitación'), null=True, blank=True)
    Informada = models.BooleanField(
        _('Indica si la Licitación es Informada'), null=True, blank=True)
    DiasCierreLicitacion = models.IntegerField(
        _('cantidad de días para el cierre de la Licitación'), null=True, blank=True)
    CodigoTipo = models.IntegerField(
        _('Tipo Licitación'), choices=CODIGO_TIPO_LICITACION_CHOICES, null=True, blank=True)
    Tipo = models.CharField(
        _('Tipo de Licitación de Mercado Publico'), choices=TIPO_LICITACION_CHOICES, null=True, blank=True)
    TipoConvocatoria = models.IntegerField(
        _('Tipo Convocatoria'), choices=TIPO_CONVOCATORIA_CHOICES, null=True, blank=True)
    Moneda = models.CharField(
        _('Código de la moneda en la Licitación'), choices=MONEDA_CHOICES, null=True, blank=True)
    MontoEstimado = models.FloatField(
        _('Monto estimado que maneja el Organismo o Institución para licitar'), null=True, blank=True)

    def __str__(self):
        return str(self.CodigoExterno)

    class Meta:
        verbose_name = 'Licitacion Detalle'
        verbose_name_plural = 'Licitacion Detalle'

        indexes = [
            models.Index(
                fields=['MontoEstimado', 'licitacion', 'CodigoExterno', 'active']),
            models.Index(fields=['MontoEstimado', 'licitacion']),
            models.Index(fields=['MontoEstimado']),
            models.Index(fields=['Adjudicacion_UrlActa']),
            models.Index(fields=['active', 'CodigoExterno']),
            models.Index(fields=['active']),
        ]


class Proveedor(models.Model):
    created_at = models.DateTimeField(
        _('Fecha'), default=timezone.now, blank=True)
    updated_at = models.DateTimeField(auto_now=True, null=True)
    RutProveedor = models.CharField()
    NombreProveedor = models.CharField()

    def __str__(self):
        return self.NombreProveedor

    class Meta:
        constraints = [
            models.UniqueConstraint(
                fields=['RutProveedor', 'NombreProveedor'], name='unique_proovedor')
        ]
        verbose_name = 'Proveedor'
        verbose_name_plural = 'Proveedor'

        indexes = [
            models.Index(fields=['RutProveedor', 'NombreProveedor']),
            models.Index(fields=['NombreProveedor']),
        ]


class LicitacionItem(models.Model):
    created_at = models.DateTimeField(
        _('Fecha'), default=timezone.now, blank=True)
    updated_at = models.DateTimeField(auto_now=True, null=True)
    licitacion = models.ForeignKey(
        Licitacion, on_delete=models.CASCADE, related_name='licitacion_items')
    licitacion_detalle = models.ForeignKey(
        LicitacionDetalle, on_delete=models.CASCADE, related_name='licitacion_detalle_items')
    producto = models.ForeignKey(
        'Producto', on_delete=models.CASCADE, blank=True, null=True)
    categoria = models.ForeignKey(
        'Categoria', on_delete=models.CASCADE, blank=True, null=True)
    proveedor = models.ForeignKey(
        Proveedor, on_delete=models.CASCADE, null=True, blank=True)
    CodigoExterno = models.CharField()
    Descripcion = models.CharField(blank=True, null=True)
    UnidadMedida = models.CharField(_('Unidad Medida'), blank=True, null=True)
    Cantidad = models.FloatField(_('Cantidad Licitada'), blank=True, null=True)
    Adjudicacion_Cantidad = models.FloatField(
        _('Cantidad Adjudicada'), blank=True, null=True)
    Adjudicacion_MontoUnitario = models.FloatField(
        _('Monto Unitario Adjudicación'), blank=True, null=True)
    active = models.BooleanField(_('Vigencia Item'), default=True)
    Correlativo = models.IntegerField(blank=True, null=True)
    analizado = models.BooleanField(default=False)
    touch = models.BooleanField(default=False)
    search = SearchVectorField(
        null=True,
        help_text="used to store all searchable info",
    )

    def __str__(self):
        return self.Descripcion

    class Meta:
        verbose_name = 'Licitacion Items Seguimiento Diario'
        verbose_name_plural = 'Licitacion Items Seguimiento Diario'
        indexes = [
            models.Index(fields=['Descripcion', 'active', 'Cantidad']),
            models.Index(fields=['Cantidad', 'licitacion']),
            models.Index(fields=['active', 'CodigoExterno']),
            models.Index(fields=['Descripcion', 'licitacion']),
            models.Index(fields=['Cantidad']),
            models.Index(fields=['Descripcion']),
            models.Index(fields=['active']),
            GinIndex(fields=["search"]),
        ]

    def update_search_vector(self):
        """
        Update SearchVector field of SearchModel using raw SQL
        search field is used to store SearchVector
        """
        db_table = self._meta.db_table

        # first get anything interesting out of the media
        # that needs to be searchable

        if self.id:

            items = []
            if self.Descripcion:
                items.append(str(self.Descripcion))

            if self.proveedor:
                if self.proveedor.NombreProveedor:
                    items.append(str(self.proveedor.NombreProveedor))

            if self.categoria:
                if self.categoria.Categoria:
                    items.append(str(self.categoria.Categoria))

            if self.licitacion:
                if self.licitacion.Nombre:
                    items.append(str(self.licitacion.Nombre))

            items = [item for item in items if item]
            text = " ".join(items)
            text = " ".join([token for token in text.lower().split(
                " ") if token not in STOP_WORDS])

            text = helpers.clean_query(text)

            sql_code = """
                UPDATE {db_table} SET search = to_tsvector(
                    '{config}', '{text}'
                ) WHERE {db_table}.id = {id}
                """.format(
                db_table=db_table, config="simple", text=text, id=self.id
            )

            try:
                with connection.cursor() as cursor:
                    cursor.execute(sql_code)
            except BaseException as e:
                logger.error(f': LicitacionItem {str(e)}')
                pass
        return True

class Categoria(models.Model):
    created_at = models.DateTimeField(
        _('Fecha'), default=timezone.now, blank=True)
    updated_at = models.DateTimeField(auto_now=True, null=True)
    Categoria = models.CharField()
    CodigoCategoria = models.CharField()

    def __str__(self):
        return str(self.Categoria)

    class Meta:
        constraints = [
            models.UniqueConstraint(
                fields=['Categoria', 'CodigoCategoria'], name='unique_categoria')
        ]
        verbose_name = 'Categoria'
        verbose_name_plural = 'Categoria'

        indexes = [
            models.Index(fields=['CodigoCategoria', 'Categoria']),
            models.Index(fields=['Categoria']),
        ]

class Producto(models.Model):
    created_at = models.DateTimeField(
        _('Fecha'), default=timezone.now, blank=True)
    updated_at = models.DateTimeField(auto_now=True, null=True)
    NombreProducto = models.CharField()
    CodigoProducto = models.CharField()
    categoria = models.ForeignKey(
        Categoria, on_delete=models.CASCADE, blank=True, null=True)

    def __str__(self):
        return self.NombreProducto

    class Meta:
        constraints = [
            models.UniqueConstraint(
                fields=['NombreProducto', 'CodigoProducto'], name='unique_producto')
        ]

        verbose_name = 'Producto'
        verbose_name_plural = 'Producto'

        indexes = [
            models.Index(fields=['NombreProducto',
                         'categoria', 'CodigoProducto']),
            models.Index(fields=['NombreProducto', 'categoria']),
            models.Index(fields=['NombreProducto']),
        ]