# Copyright (C) 2015, 2018, 2020, 2021 The Meme Factory, Inc.
# http://www.karlpinc.com/

# This file is part of PGWUI_Upload.
#
# This program is free software: you can redistribute it and/or
# modify it under the terms of the GNU Affero General Public License
# as published by the Free Software Foundation, either version 3 of
# the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public
# License along with this program.  If not, see
# <http://www.gnu.org/licenses/>.
#

# Karl O. Pinc <kop@karlpinc.com>
#
# Bugs:
#  All data is presented to the db as a string, which could result
# in problems with type coercion.

# Write python 3 compatible code.
from __future__ import print_function
from __future__ import unicode_literals
from __future__ import absolute_import
from __future__ import division

import attr
import psycopg2

from pyramid.view import view_config
import logging

from pgwui_common.view import auth_base_view
from pgwui_core import exceptions as core_ex
from pgwui_core.core import (
    UploadTableInitialPostMixin,
    UploadEngine,
    DataLineProcessor,
    UploadDoubleTableForm,
    UploadData,
    escape_eol,
    is_checked,
)
from pgwui_upload_core.views.upload import (
    UploadCoreInitialPost,
    BaseTableUploadHandler,
    set_upload_response,
)

from pgwui_upload import exceptions as upload_ex


log = logging.getLogger(__name__)


@attr.s
class UploadTableInitialPost(UploadCoreInitialPost,
                             UploadTableInitialPostMixin):
    pass


class SaveLine(DataLineProcessor):
    def __init__(self, ue, uh, insert_stmt):
        '''
        ue             UploadEngine instance
        uh             UploadHandler instance
        insert_stmt    Statement used to insert into db.
                       (psycopg2 formatted for substituion)
        '''
        super(SaveLine, self).__init__(ue, uh)
        self.insert_stmt = insert_stmt

    def eat(self, udl):
        '''
        Upload a line of data into the db.

        udl  An UploadDataLine instance
        '''
        self.cur.execute(self.insert_stmt, udl.tuples)


class TableUploadHandler(BaseTableUploadHandler):
    '''
    Attributes:
      request       A pyramid request instance
      uf            A GCUploadForm instance
      session       A pyramid session instance
      ue
      cur
    '''

    def make_form(self):
        '''
        Make the upload form needed by this handler.
        '''
        return UploadDoubleTableForm().build(self, ip=UploadTableInitialPost())

    def get_data(self):
        '''
        Return an UploadData instance, with flags set as desired.
        '''
        uf = self.uf
        self.data = UploadData(uf['localfh'],
                               uf['upload_fmt'],
                               uf['upload_null'],
                               uf['null_rep'],
                               trim=uf['trim_upload'])

    def val_input(self):
        '''
        Validate input needed beyond that required to connect to the db.

        Returns:
          A list of pgwui_core.UploadError instances
        '''
        uf = self.uf
        errors = super().val_input()

        qualified_table = uf['table']
        if qualified_table == '':
            errors.append(upload_ex.NoTableError(
                'No table or view name supplied'))

        return errors

    def quote_columns(self):
        return super().quote_columns(
            self.request.registry.settings['pgwui']['pgwui_upload'])

    def factory(self, ue):
        '''Make a db loader function from an UploadEngine.

        Input:

        Side Effects:
        Yes, lots.
        '''

        self.ue = ue
        self.cur = ue.cur
        qualified_table = self.uf['table']

        try:
            quotecols = self.quote_columns()
            column_quoter = self.get_column_quoter(quotecols)

            insert_stmt = self.build_insert_stmt(
                ue.data, qualified_table, quotecols, column_quoter)

            return SaveLine(ue, self, insert_stmt)
        except (core_ex.PGWUIError, psycopg2.Error):
            self.data.lineno = 0  # Don't report partially read number of lines
            raise


def log_success(response):
    if is_checked(response['csv_checked']):
        upload_fmt = 'CSV'
    else:
        upload_fmt = 'TAB'
    log.info('Successful upload: DB {db}: Table ({table}):'
             ' File ({filename}): Lines {lines}:'
             ' Format {format}: Upload Null {null}: Null Rep ({null_rep}):'
             ' Trim {trim}: By user {user}'
             .format(filename=response['filename'],
                     lines=response['lines'],
                     format=upload_fmt,
                     null=is_checked(response['upload_null']),
                     null_rep=escape_eol(response['null_rep']),
                     trim=is_checked(response['trim_upload']),
                     db=response['db'],
                     table=response['table'],
                     user=response['user']))


@view_config(route_name='pgwui_upload',
             renderer='pgwui_upload:templates/upload.mak')
@auth_base_view
def upload_view(request):

    tuh = TableUploadHandler(request).init()
    response = UploadEngine(tuh).run()

    set_upload_response('pgwui_upload', request, response)

    if response['db_changed']:
        log_success(response)
    return response
