# -*- coding: utf-8 -*-
import logging
import os

from io import open
from sqlalchemy import create_engine, orm


log = logging.getLogger(__name__)


class DatabaseAdapter(object):
    _connections_ = dict()

    def __init__(self):
        """
        This Class provides a bucket for database connection handling. It uses SQLalchemy for database talk.
        It enables distinct session sharing on the rfc1738 URL string. This reduces the amount of used
        sessions drastically.
        """
        pass

    def add_connection(self, connection_string):
        """
        Adds a new connection to this object. Also creates the necessary engine and session object.

        Args:
            connection_string (str): The rfc1738 URL string which defines the database
                connection
        """
        if connection_string not in self._connections_:
            engine = create_engine(connection_string, pool_recycle=30)
            session = orm.scoped_session(orm.sessionmaker(bind=engine))
            self._connections_[connection_string] = {
                'engine': engine,
                'session': session
            }
        else:
            log.info('Connection already exists: {0}'.format(connection_string))

    def get_connections(self):
        """
        Returns a dictionary with the available connections.

        Returns:
            dict: The available connections.
        """
        return self._connections_

    def get_session(self, key, request=None):
        """
        The point where you will get what you need: The session to talk to the database!

        Args:
            key (str): The key to identify the desired connection in the pool of available
                connections.
            request (pyramid.request.Request or None): The request of the underlying pyramid
                application. This can be useful to handle errorcases and treat sessions in
                the right way.

        Returns:
            sqlalchemy.orm.Session: The requested clean session instance ready for use

        Raises:
            KeyError
        """
        if key in self._connections_:
            session = self._connections_.get(key).get('session')
            return session()
        else:
            self.add_connection(key)
            log.info('Connection does not exist, implicitly creating it: {0}'.format(key))
            return self.get_session(key, request=request)


class FileAdapter(object):

    def __init__(self, cwd=None):
        """
        This Class provides a list of methods for file handling.

        It uses the current working directory as central point for file handling.

        Args:
            cwd: The path of the current working directory.

        """
        self._cwd_ = os.path.abspath(cwd or '.')

    @property
    def cwd(self):
        """
        The property of the current working directory.
        """
        return self._cwd_

    def cd(self, path):
        """
        This method changes the path of the current working directory.

        Args:
            path(str): The path of the new working directory.
        """
        self._cwd_ = os.path.abspath(os.path.join(self._cwd_, path))

    def ls(self):
        """
        This method lists all content of the given path.

        Returns:
            String: JSON formatted string.
        """
        result = list()
        for entry in os.listdir(self._cwd_):
            path = os.path.join(self._cwd_, entry)
            result.append((entry, {
                'is_file': os.path.isfile(path),
                'is_dir': os.path.isdir(path),
                'modified': os.path.getmtime(path)
            }))
        return result

    def read(self, filename, mode='rb'):
        """
        This method uses a given path to read a file and returns its content.

        Args:
            filename (str): The path to the file.
            mode: The mode in which to open the file. Default is "rb" (read binary).

        Returns:
            String: The content of the file

        Raises:
            IOError
        """
        filepath = os.path.join(self._cwd_, filename)
        if os.path.isfile(filepath):
            with open(filepath, mode=mode) as f:
                content = f.read()
            return content
        else:
            raise IOError('Not a file: {0}'.format(filepath))
