class Command(BaseCommand):

    # string with numbers, max length is 14 chars
    UPC_REGEXP = re.compile('^\d{1,14}$')

    option_list = (
        Option('--limit', dest='limit', type=int),)

    def run(self, limit=None):
        app.logger.info('Run update submissions product.')

        self.product_finder = SemanticProducts(**app.config['SEMANTICS3'])

        submissions = self._get_submissions()
        app.logger.debug('Found {} submission without product.'.format(
            submissions.count()))

        not_linked_subs = []

        if limit:
            app.logger.debug('Applied limit: {}.'.format(limit))
            submissions = submissions.limit(limit)

        for submission in submissions:
            app.logger.debug('Processing submission {}.'.format(submission.id))

            gtin = None
            if self.UPC_REGEXP.match(submission.scanned_upc):
                gtin = gtin_validator.validate_GTIN(submission.scanned_upc)

            if gtin is None:
                app.logger.debug(
                    'Submission upc {} is not valid GTIN'.format(
                        submission.scanned_upc))
                not_linked_subs.append(submission)
                continue

            product_info = self._get_product_info(gtin)
            if not product_info:
                not_linked_subs.append(submission)
                continue

            retsku_product_id = self._get_retsku_product_id(
                gtin, product_info['brand'], product_info['model'])

            if not retsku_product_id:
                not_linked_subs.append(submission)
                continue

            submission.retsku_product_id = retsku_product_id
            self._add_log(submission.id, 'update submission retsku product id',
                          None, retsku_product_id)

            app.logger.debug(('Updating retsku_product_id: {}'
                              '').format(submission.retsku_product_id))
            db.session.commit()

        if not_linked_subs:
            html = render_template('email/submissions_without_products.html',
                                   submissions=not_linked_subs)
            email_service.send_mail(app.config['DEFAULT_MAIL_TO'],
                                    'Submissions without retsku_product_id',
                                    html)

    def _get_submissions(self):
        return Submission.query.filter(
            Submission.ret_sku_id == None,
            Submission.retsku_product_id == None,
            Submission.scanned_upc != None,
            Submission.scanned_upc != '').order_by(
            Submission.updated_at.desc())

    def _get_retsku_product_id(self, gtin, brand, model):
        result = retsku_api.get_retsku_product_id(
            brand_name=brand, model_name=model, gtin=gtin)

        if result:
            return result['retsku_product_id']
        return None

    def _add_log(self, item_id, action, old_value, new_value):
        action = LogAction(item_id=item_id, action=action,
                           old_value=old_value, new_value=new_value)
        db.session.add(action)
        db.session.commit()

    def _get_product_info(self, upc):
        product_info = self._get_product_match_info(upc)
        return product_info or self._get_semantics_info(upc)

    def _get_product_match_info(self, upc):
        info = retsku_api.get_products_info(upc)
        if info:
            return {'brand': info['brand'],
                    'model': info['model']}
        return None

    def _get_semantics_info(self, upc):
        self.product_finder.clear_query()
        self.product_finder.products_field('upc', upc)

        try:
            json_data = self.product_finder.get()
            if json_data.get('results', None):
                item = json_data['results'][0]
                return {
                    'brand': item.get('brand', item.get('manufacturer')),
                    'model': item.get('model', item.get('mpn'))}
            return None
        except (ValueError, Semantics3Error):
            return None
class Command(BaseCommand):

    # string with numbers, max length is 14 chars
    UPC_REGEXP = re.compile('^\d{1,14}$')

    option_list = (
        Option('--limit', dest='limit', type=int),)

    def run(self, limit=None):
        app.logger.info('Run create missing products.')

        self.product_finder = SemanticProducts(**app.config['SEMANTICS3'])

        submissions = self._get_submissions()
        app.logger.debug('Found {} submission without product.'.format(
            submissions.count()))

        products_wrong_category = []

        if limit:
            app.logger.debug('Applied limit: {}.'.format(limit))
            submissions = submissions.limit(limit)

        for submission in submissions:
            task = submission.assignment.task
            app.logger.debug('Processing submission {}.'.format(submission.id))
            app.logger.debug('Retailer {}, category: {}.'.format(
                task.retailer_id, task.retsku_category_id))

            if task.retailer_id is None:
                app.logger.debug('Task retailer id should be specified.')
                continue

            gtin = None
            if self.UPC_REGEXP.match(submission.scanned_upc):
                gtin = gtin_validator.validate_GTIN(submission.scanned_upc)

            if gtin is None:
                app.logger.debug(
                    'Submission upc {} is not valid GTIN'.format(
                        submission.scanned_upc))
                continue

            product = self._get_product(task.retailer_id, gtin,
                                        task.retsku_category_id,
                                        submission)

            if product is None:
                app.logger.debug('Couldn\'t create product.')
                continue

            if product.retsku_category_id != task.retsku_category_id:
                product.task_retsku_category_id = task.retsku_category_id
                products_wrong_category.append(product)

            if not product.retsku_product_id:
                self._update_retsku_product_id(product)

            self._add_log(submission.id, 'update submission',
                          submission.ret_sku_id, product.ret_sku_id)
            submission.product = product
            submission.retsku_product_id = product.retsku_product_id
            app.logger.debug(
                'Updating ret_sku_id: {}, retsku_product_id: {}'.format(
                    product.ret_sku_id, product.retsku_product_id))
            db.session.commit()

        if products_wrong_category:
            html = render_template('email/missing_products.html',
                                   products=products_wrong_category)
            email_service.send_mail(app.config['DEFAULT_MAIL_TO'],
                                    'Products with categories mismatch',
                                    html)

    def _get_submissions(self):
        return Submission.query.filter(
            Submission.ret_sku_id == None,
            Submission.scanned_upc != None,
            Submission.barcode != None).order_by(
            Submission.updated_at.desc())

    def _get_product(self, retailer_id, gtin, retsku_category_id,
                     submission):
        products = Product.query.filter(
            Product.retailer_id == retailer_id,
            Product.gtin == gtin).all()
        if not products:
            products = Product.query.filter(
                Product.retailer_id == retailer_id,
                Product.barcode == submission.barcode).all()

        if products:
            # if we have list use first or try to find with the same values
            for p in products:
                if p.gtin == gtin and p.barcode == submission.barcode:
                    product = p
                    break
            else:
                product = products[0]

            if product.barcode != submission.barcode:
                app.logger.debug(
                    'Updating barcode for {}. Old: {}, new: {}.'.format(
                        product.ret_sku_id, product.barcode,
                        submission.barcode))
                self._add_log(product.ret_sku_id, 'update barcode',
                              product.barcode, submission.barcode)
                product.barcode = submission.barcode
                db.session.commit()

            if product.gtin != gtin:
                app.logger.debug(
                    'Updating GTIN for {}. Old: {}, new: {}.'.format(
                        product.ret_sku_id, product.gtin, gtin))
                self._add_log(product.ret_sku_id, 'update gtin', product.gtin,
                              gtin)
                product.gtin = gtin
                db.session.commit()
        else:
            product = self._create_product(retailer_id, gtin,
                                           retsku_category_id, submission)

        return product

    def _create_product(self, retailer_id, gtin, retsku_category_id,
                        submission):

        if submission.brand is None or submission.model is None:
            semantics_info = self._get_semantics_info(submission.scanned_upc)
        else:
            semantics_info = {}

        # get or create brand
        brand_name = submission.brand or semantics_info.get('brand')
        if not brand_name:
            return None

        brand_name = brand_name.strip()
        brand = Brand.query.filter(
            func.lower(Brand.brand_name) == func.lower(brand_name)).first()
        if not brand:
            brand = Brand(brand_name=brand_name)
            db.session.add(brand)
            db.session.commit()
            self._add_log(brand.brand_id, 'create new brand', None, None)

        model = submission.model or semantics_info.get('model') or gtin
        product = Product(retailer_id=retailer_id,
                          retsku_category_id=retsku_category_id,
                          gtin=gtin,
                          barcode=submission.barcode,
                          external_product_code=gtin,
                          model_name=model,
                          brand=brand,
                          semantic_id=semantics_info.get('semantic_id'))
        db.session.add(product)
        db.session.commit()
        self._add_log(product.ret_sku_id, 'create new product', None, None)
        return product

    def _update_retsku_product_id(self, product):
        result = retsku_api.get_retsku_product_id(
            brand_name=product.brand.brand_name,
            semantic_id=product.semantic_id,
            model_name=product.model_name,
            product_code=product.external_product_code,
            gtin=product.gtin)

        if result:
            self._add_log(product.ret_sku_id, 'update retsku product id',
                          product.retsku_product_id,
                          result['retsku_product_id'])

            product.retsku_product_id = result['retsku_product_id']
            product.matcher_type = result['matcher_type']
            # yr: do we need update retsku_category_id?
            db.session.commit()

    def _add_log(self, item_id, action, old_value, new_value):
        action = LogAction(item_id=item_id, action=action,
                           old_value=old_value, new_value=new_value)
        db.session.add(action)
        db.session.commit()

    def _get_semantics_info(self, upc):
        self.product_finder.clear_query()
        self.product_finder.products_field('upc', upc)

        try:
            json_data = self.product_finder.get()
            if json_data.get('results', None):
                item = json_data['results'][0]
                return {
                    'semantic_id': item.get('sem3_id'),
                    'brand': item.get('brand', item.get('manufacturer')),
                    'model': item.get('model', item.get('mpn'))}
            return {}
        except (ValueError, Semantics3Error):
            return {}