#!/usr/bin/env python
# -*- coding: utf-8 -*-

import os
import tempfile
import subprocess
import logging
from pathlib import Path
import petl
from dotenv import load_dotenv
from jinja2 import Environment, BaseLoader
import unicodedata
import re


logger = logging.getLogger(__name__)

jinja_env = Environment(loader=BaseLoader())

DEFAULT_SQLPLUS_PATH = '/usr/bin/sqlplus64'


def env_append(var_name, elements, joiner=':'):
    cur_value = os.getenv(var_name)
    elements.insert(0, cur_value)
    new_value = ':'.join(filter(lambda v: bool(v), elements))
    os.environ[var_name] = new_value

    return os.environ[var_name]


def env_prepend(var_name, elements, joiner=':'):
    cur_value = os.getenv(var_name)
    elements.append(cur_value)
    new_value = ':'.join(filter(lambda v: bool(v), elements))
    os.environ[var_name] = new_value

    return os.environ[var_name]


COMMAND_TEMPLATE_STR = '''
set colsep {{obj.delimiter()}}
set headsep on
set pagesize 0
set heading on
set trimspool on
set termout off
set embedded off
set linesize 3000
set feedback off


spool {{output_file}}

{{query}}

spool off
exit
'''
template = jinja_env.from_string(COMMAND_TEMPLATE_STR)

DEFAULT_LD_LIBRARY_PATH = [
    '/usr/lib/oracle/10.2.0.5/client64/lib/',
    '/usr/lib/oracle/10.2.0.5/client/lib/'
]

DEFAULT_PATHS = ['/usr/lib/oracle/10.2.0.5/client64/bin']


class SQLPlusRunner:
    def __init__(self,
                 # service_name=None,
                 source=None,
                 connection=None,
                 **kwargs):

        self.source = source
        self.connection = connection

        env_append('LD_LIBRARY_PATH',
                   self.connection.params.get('LD_LIBRARY_PATH',
                                              DEFAULT_LD_LIBRARY_PATH
                                             )
        )
        env_prepend('PATH',
                    self.connection.params.get('PATHS',
                                               DEFAULT_PATHS))

    def conf(self, var_name, default=None):
        return self.connection.params.get(var_name,
                                          self.source.params.get(var_name,
                                                                 os.getenv(var_name, default))) # noqa E501
    def delimiter(self):
        return self.conf('delimiter', ',')

    def query(self, query, headers=[],
              output_file_path=None, **kwargs):
        '''Run sql query, return stream of data.'''

        # create .sql file with template

        if not output_file_path:
            print('you must pass in a filepath')

            return
        # run sqlplus with credentials, pointing at .sql file
        # self.run_sql_query(tf.name, connection_string)
        self.run_sql_query(query, output_file_path)

        # read data
        delimiter = self.source.params.get('delimiter', ',')
        with open(output_file_path, 'r') as _f:
            content = _f.read()

            if 'table or view does not exist' in content:
                print('table or view does not exist')
                exit()
        # TODO put some type of error handling here 
        # "table or view does not exist"

        table = petl.fromcsv(output_file_path, delimiter=delimiter)

        table = table.pushheader(headers)

        table = table.convertall(lambda v: v.strip())
        # for header in headers:
        #     table = table.convert(header, lambda v: v.strip())

        return table

    def create_connection_string(self):
        username = self.conf('DB_USERNAME')
        password = self.conf('DB_PASSWORD')
        port = self.conf('DB_PORT', 1521)
        server = self.conf('DB_SERVER')
        service_name = self.conf('DB_SERVICE_NAME')

        return f'{username}/{password}@//{server}:{port}/{service_name}'

    def create_sql_command_file(self, query, output_file):

        template_rendered = template.render(query=query,
                                            output_file=output_file,
                                            obj=self)
        tf = tempfile.NamedTemporaryFile(suffix='.sql')
        tf.write(template_rendered.encode())
        tf.flush()

        return tf

    # def run_sql_query(self,
    #                   sql_command_file,
    #                   connection_string):
    def run_sql_query(self, query, output_file_path):

        connection_string = self.create_connection_string()

        # tf = self.create_sql_command_file(query,
        #                                   output_file=output_file_path)

        with self.create_sql_command_file(query,
                                          output_file=output_file_path) as tf:
            sqlplus_path = self.connection.params.get('sqlplus_path',
                                                      DEFAULT_SQLPLUS_PATH)
            command = [
                sqlplus_path, connection_string, f'@{tf.name}'
            ]

            print('command', command)
            # subprocess.run(command, capture_output=True, check=False)
            subprocess.run(command, check=False)


def slugify(value, allow_unicode=False):
    """
    Convert to ASCII if 'allow_unicode' is False. Convert spaces or repeated
    dashes to single dashes. Remove characters that aren't alphanumerics,
    underscores, or hyphens. Convert to lowercase. Also strips leading and
    trailing whitespace, dashes, and underscores.
    """
    value = str(value)

    if allow_unicode:
        value = unicodedata.normalize('NFKC', value)
    else:
        value = unicodedata.normalize('NFKD', value).encode('ascii', 'ignore').decode('ascii')
    
    value = re.sub(r'[^\w\s-]', '', value.lower())

    return re.sub(r'[-\s]+', '-', value).strip('-_')


def fetch(connection=None, source=None, **kwargs):

    from oaa.settings import config

    service_name = connection.params["service_name"]
    load_dotenv(connection.params.get('env_filename', f'.{service_name}.env'))

    if not source.params['query'] or not source.params['fieldnames']:
        logger.error('query and fieldnames required to query via sqlplus')

    output_folder = Path(config.BASE_DIR) / 'query_output'

    if not os.path.exists(output_folder):
        os.mkdir(output_folder)

    filename_slugified = slugify(f'db_{service_name}_{config.app.name}')
    output_file_path = output_folder / f'{filename_slugified}.csv'

    sqlplus = SQLPlusRunner(source=source, connection=connection)

    result = sqlplus.query(query=source.params['query'],
                           headers=source.params['fieldnames'],
                           output_file_path=output_file_path,
                           # **connection.params,
                           # **source.params,
                           **kwargs)

    return result
