from ..helpers.classes import FakeCursor, SQLALL
from ..helpers.methods import uuid2int
from ..objects import Query, Field
from .base import NoSQLAdapter, SQLAdapter
from . import adapters


@adapters.register_for('couchdb')
class CouchDB(NoSQLAdapter):
    dbengine = 'couchdb'
    drivers = ('couchdb',)

    uploads_in_blob = True

    def _initialize_(self, do_connect):
        super(CouchDB, self)._initialize_(do_connect)
        self.ruri = 'http://' + self.uri[10:]
        self.db_codec = 'UTF-8'

    def connector(self):
        conn = self.driver.Server(self.ruri, **self.driver_args)
        conn.cursor = lambda: FakeCursor()
        conn.close = lambda: None
        conn.commit = lambda: None
        return conn

    def create_table(self, table, migrate=True, fake_migrate=False,
                     polymodel=None):
        if migrate:
            try:
                self.connection.create(table._tablename)
            except:
                pass
        super(CouchDB, self).create_table(
            table, migrate, fake_migrate, polymodel)

    def _expand(self, expression, field_type=None, query_env={}):
        if isinstance(expression, Field):
            if expression.type == 'id':
                return "%s._id" % expression.tablename
        return SQLAdapter._expand(self, expression, field_type,
            query_env=query_env)

    def insert(self, table, fields):
        rid = uuid2int(self.db.uuid())
        ctable = self.connection[table._tablename]
        values = dict((k.name, self.represent(v, k.type)) for k, v in fields)
        values['_id'] = str(rid)
        ctable.save(values)
        return rid

    @staticmethod
    def _make_id_field(field_name):
        return field_name == 'id' and '_id' or field_name

    def _select(self, query, fields, left=False, join=False, distinct=False,
                orderby=False, groupby=False, having=False, limitby=False,
                orderby_on_limitby=True, for_update=False, outer_scoped=[],
                required=None, cache=None, cacheable=None, processor=None):
        if not isinstance(query, Query):
            raise SyntaxError("Not Supported")

        new_fields = []
        for item in fields:
            if isinstance(item, SQLALL):
                new_fields += item._table
            else:
                new_fields.append(item)

        fields = new_fields
        tablename = self.get_table(query)._tablename
        fieldnames = [f.name for f in (fields or self.db[tablename])]
        colnames = [
            '%s.%s' % (tablename, fieldname) for fieldname in fieldnames]
        fields = ','.join(
            ['%s.%s' % (tablename, self._make_id_field(f)) for f in fieldnames]
        )
        fn = '(function(%(t)s){if(%(query)s)emit(%(order)s,[%(fields)s]);})' \
            % dict(
                t=tablename, query=self.expand(query),
                order='%s._id' % tablename, fields=fields)
        return fn, colnames

    def select(self, query, fields, attributes):
        fn, colnames = self._select(query, fields, attributes)
        tablename = colnames[0].split('.')[0]
        ctable = self.connection[tablename]
        rows = [cols['value'] for cols in ctable.query(fn)]
        processor = attributes.get('processor', self.parse)
        return processor(rows, fields, colnames, False)

    def update(self, table, query, fields):
        from ..drivers import couchdb
        if not isinstance(query, Query):
            raise SyntaxError("Not Supported")
        if query.first.type == 'id' and query.op == self.dialect.eq:
            rid = query.second
            tablename = query.first.tablename
            ctable = self.connection[tablename]
            try:
                doc = ctable[str(rid)]
                for key, value in fields:
                    doc[key.name] = self.represent(
                        value, self.db[tablename][key.name].type)
                ctable.save(doc)
                return 1
            except couchdb.http.ResourceNotFound:
                return 0
        tablename = self.get_table(query)._tablename
        rows = self.select(query, [self.db[tablename]._id], {})
        ctable = self.connection[tablename]
        table = self.db[tablename]
        for row in rows:
            doc = ctable[str(row.id)]
            for key, value in fields:
                doc[key.name] = self.represent(value, table[key.name].type)
            ctable.save(doc)
        return len(rows)

    def count(self, query, distinct=None):
        if distinct:
            raise RuntimeError("COUNT DISTINCT not supported")
        if not isinstance(query, Query):
            raise SyntaxError("Not Supported")
        tablename = self.get_table(query)._tablename
        rows = self.select(query, [self.db[tablename]._id], {})
        return len(rows)

    def delete(self, table, query):
        from ..drivers import couchdb
        if not isinstance(query, Query):
            raise SyntaxError("Not Supported")
        if query.first.type == 'id' and query.op == self.eq:
            rid = query.second
            tablename = query.first.tablename
            assert(tablename == query.first.tablename)
            ctable = self.connection[tablename]
            try:
                del ctable[str(rid)]
                return 1
            except couchdb.http.ResourceNotFound:
                return 0
        tablename = self.get_table(query)._tablename
        rows = self.select(query, [self.db[tablename]._id], {})
        ctable = self.connection[tablename]
        for row in rows:
            del ctable[str(row.id)]
        return len(rows)
