import datetime
import json
import logging
import os.path
import re

from django.conf import settings
from django.contrib.postgres.fields import JSONField
from django.db import models, connections

from products.models import Product

log = logging.getLogger(__name__)


class Season(models.Model):
    id = models.CharField(max_length=30, primary_key=True)
    name = models.CharField(max_length=100)
    ordering = models.PositiveIntegerField(unique=True, default=1000)

    def serialize(self):
        return {
            'id': self.id,
            'name': self.name,
            'ordering': self.ordering,
        }


class Region(models.Model):
    id = models.CharField(max_length=30, primary_key=True)
    name = models.CharField(max_length=100)
    ordering = models.PositiveIntegerField(unique=True, default=1000)
    visible = models.BooleanField(default=True)

    def serialize(self):
        return {
            'id': self.id,
            'name': self.name,
            'ordering': self.ordering,
            'visible': self.visible
        }


def pretty_datetime(date):
    # Oct 16 2018 10:58 am
    return "{:%b %-d %Y, %-I:%M %p}".format(date)


def unix_datetime(date):
    return date.timestamp()


class Catalog(models.Model):
    PDF_DIR = 'catalogs'
    PDF_URL = 'export/catalogs'
    owner = models.ForeignKey(settings.AUTH_USER_MODEL,
                              on_delete=models.CASCADE)
    season = models.ForeignKey(Season, on_delete=models.PROTECT)
    region = models.ForeignKey(Region, on_delete=models.PROTECT)
    created = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now=True, db_index=True)
    name = models.CharField(max_length=300)
    email = models.CharField(max_length=300, null=True, blank=True)
    public = models.BooleanField(default=False, db_index=True)
    pages = models.PositiveIntegerField(default=0)
    sections = models.PositiveIntegerField(default=0)
    materials = models.PositiveIntegerField(default=0)
    data = JSONField(null=True)
    master = models.BooleanField(default=False)
    show_prices = models.BooleanField(default=False)
    build_progress = models.PositiveIntegerField(default=0)

    # JSONField docs:
    # https://docs.djangoproject.com/en/2.2/ref/contrib/postgres/fields/#jsonfield


    def pdf_name(self):
        # NOTE: must match what the builder writes!
        pdf = "{}-{}".format(self.name, self.id)
        pdf = re.sub(r'\s+', r'_', pdf)
        pdf = re.sub(r'[^\w\-]', r'', pdf)
        pdf += '.pdf'
        return pdf


    def pdf_url(self):
        return "http://{}/{}/{}/{}".format(settings.PUBLIC_WEB_HOST,
                                           self.PDF_URL,
                                           self.season.id,
                                           self.pdf_name())


    def pdf_file(self):
        return os.path.join(settings.ASSET_DIR,
                            self.PDF_DIR,
                            self.season.id,
                            self.pdf_name())


    def pdf_exists(self):
        return os.path.isfile(self.pdf_file())


    def summary(self):
        """Useful in the catalog list."""
        return {
            'id': self.id,
            'seasonCode': self.season.id,
            'regionCode': self.region.id,
            'name': self.name,
            'ownerName': self.owner.get_full_name(),
            'public': self.public,
            'updated': unix_datetime(self.updated),
            'updatedPretty': pretty_datetime(self.updated),
            'created': unix_datetime(self.created),
            'createdPretty': pretty_datetime(self.created),
            'pages': self.pages,
            'sections': self.sections,
            'materials': self.materials,
            'master': self.master,
            'show_prices': self.show_prices,
            'build_progress': self.build_progress,
            'pdf': self.pdf_url(),
        }


    def update_metadata(self):
        """Update meta properties from what's in 'data'"""
        if not self.data or len(self.data) < 1:
            log.warning('no data in update_metadata()')
            return

        data = self.data

        self.name = data.get('name', '(No name)')
        self.email = data.get('email')

        self.season = Season.objects.get(id=data.get('season'))
        self.region = Region.objects.get(id=data.get('region'))

        self.public = data.get('public', False)
        self.master = data.get('master', False)
        self.show_prices = data.get('show_prices', False)

        # NOTE 'is_utility' in the json data is not exposed to the
        # django model or placed in a separate postgres column.

        # reset until pdf is made
        self.build_progress = 0

        # calculate some properties
        sections = 0
        pages = 0
        materials = 0
        for section in data.get('sections', []):
            sections += 1
            pages += 1 # for breaker page
            for page in section.get('pages', []):
                pages += 1
                for block in page:
                    materials += len(block.get('ids', []))

        self.sections = sections
        self.pages = pages
        self.materials = materials


    def products(self):
        """Return an unordered list of all products in the catalog."""
        ids = self.product_ids()
        log.info("ids: %s", ids)
        # TODO filter on catalog region and season when we
        # have that data
        return Product.objects.filter(sap__in=ids).distinct('sap')


    def product_ids(self):
        """Return an unordered list of all product ids in the catalog."""
        materials = set()

        if self.data and len(self.data) > 0:
            for section in self.data.get('sections', []):
                for page in section.get('pages', []):
                    for block in page:
                        materials = materials.union(set(block.get('ids', [])))

        return materials


    def queue_render(self):
        """Create a pdf render job for us in the queue."""
        job_state = 'new'
        job_class = 'Appy::Queue::ProCatalogRenderJob'
        job_args = { 'id': self.id }

        job_sql = 'insert into job_queue (state,job_class,job_args) values(%s,%s,%s)'
        job_params = [job_state, job_class, json.dumps(job_args)]

        with connections['products'].cursor() as cursor:
            cursor.execute(job_sql, job_params)
