from uuid import uuid4 as uuid
from collections import defaultdict

from buddly.db import query_db


class BaseModel(object):
    pass


class Buddy(BaseModel):

    def __init__(self, name, email, id_=None, hash_=None):

        self.id_ = id_  # if NULL, let db assign id
        self.hash_ = hash_ or uuid().hex  # if NULL, create new hash

        self.name = name
        self.email = email

        # { Event : [ Buddy, ] }
        self.buddies = None

    @classmethod
    def from_db(cls, email=None, hash_=None, id_=None):
        assert email is not None ^ hash_ is not None ^ id_ is not None
        # the above xor (with three operands) will result to True when all three operands are
        # True; so we specifically check against that as well
        assert email is None or hash_ is None or id_ is None

        sql = 'SELECT * FROM buddy ' \
              'WHERE ? = email OR ? = hash_ OR ? = id_'
        row = query_db(sql, [email, hash_, id_], one=True)

        if row is None:
            return None

        return cls(*row)

    def get_events(self, from_db=False):
        sql = 'SELECT * FROM event' \
              ' JOIN event_to_buddies as map' \
              ' WHERE map.buddy_id = ?' \
              '   AND map.event_id = event.id_'

        if self.buddies is None or from_db:
            self.buddies = defaultdict(list)
            rows = query_db(sql, [self.id_])
            for row in rows:
                unused = self.buddies[Event(*row)]

        return self.buddies.keys()

    def get_buddies(self, from_db=False):
        sql = 'SELECT * from buddy' \
              ' JOIN pair' \
              ' WHERE pair.santa_id = ?' \
              '   AND pair.event_id = ?' \
              '   AND pair.buddy_id = buddy.id_'

        if self.buddies is None or from_db:
            events = self.get_events(from_db)
            for ev in events:
                rows = query_db(sql, [self.id_, ev.id_])
                for row in rows:
                    b = Buddy(*row)
                    self.buddies[ev].append(b)

        return self.buddies.values()

    def __repr__(self):
        return '<Buddy %r>' % self.name


class Event(BaseModel):
    def __init__(self, name, desc=None, image=None, start_date=None, id_=None):
        self.id_ = id_ or None  # if NULL, let db assign id
        self.name = name
        self.desc = desc or ''
        self.image = image
        self.start_date = start_date

        # [ Buddy, ]
        self.owners = []

    def __repr__(self):
        return '<Event %r>' % self.name

@@ -59,16 +59,51 @@
 aultdict
+%0Afrom sqlite3 import IntegrityError
 %0A%0Afrom b
@@ -126,16 +126,24 @@
 query_db
+, get_db
 %0A%0A%0Aclass
@@ -489,19 +489,97 @@
 ddies = 
-Non
+defaultdict(list)%0A%0A    def __repr__(self):%0A        return '%3CBuddy %25r%3E' %25 self.nam
 e%0A%0A    @
@@ -2223,63 +2223,1335 @@
 def 
-__repr__(self):%0A        return '%3CBuddy %25r%3E' %25 self.name
+commit(self):%0A        ev_sql = 'INSERT INTO event_to_buddy (event_id, buddy_id) VALUES (?, ?)'%0A        bud_sql = 'INSERT INTO pair (santa_id, buddy_id, event_id) VALUES (?, ?, ?)'%0A%0A        if self.id_ is None:%0A            try:%0A                with get_db():%0A                    # new buddy, try to insert into db%0A                    sql = 'INSERT INTO buddy (hash_, name, email) VALUES (?, ?, ?)'%0A                    get_db().execute(sql, (self.hash_, self.name, self.email))%0A%0A                    sql = 'SELECT id_ FROM buddy' %5C%0A                          ' WHERE hash_ = ?'%0A                    r = query_db(sql, %5Bself.hash_%5D, one=True)%0A                    self.id_ = r%5B'id_'%5D%0A%0A                    for (ev, bud_list) in self.buddies.items():%0A                        if ev.id_ is None:%0A                            raise NotImplementedError('event does not exist?')%0A                        get_db().execute(ev_sql, %5Bev.id_, self.id_%5D)%0A                        for bud in bud_list:%0A                            if bud.id_ is None:%0A                                raise NotImplementedError('buddy does not exist?')%0A                            get_db().execute(bud_sql, %5Bself.id_, bud.id_, ev.id_%5D)%0A%0A            except IntegrityError:%0A                return None%0A%0A        else:%0A            raise NotImplementedError('cannot do update')
 %0A%0A%0Ac

add commit (insert) to Buddy