from .operation import *

TAG_TYPE_NODE = 'NODE'
TAG_TYPE_LINK = 'LINK'

def get_tag_schema(name: str) -> dict[str, dict[str, Any]]:
    return { 't_type' : {'type': 'str' , 'optional': False , 'readonly': False},
             'id'     : {'type': 'str' , 'optional': False , 'readonly': False},
             'tag'    : {'type': 'str' , 'optional': True  , 'readonly': False},}


def get_tag(name: str, t_type: str, id: str) -> dict[str, Any]:
    t = None
    if t_type == TAG_TYPE_NODE:
        t = try_read(name, f"select * from tags_node where id = '{id}'")
    elif t_type == TAG_TYPE_LINK:
        t = try_read(name, f"select * from tags_link where id = '{id}'")
    else:
        raise Exception('Only support NODE and Link')
    if t == None:
        return { 't_type': t_type, 'id': id, 'tag': None }
    d = {}
    d['t_type'] = t_type
    d['id'] = str(t['id'])
    d['tag'] = str(t['tag']) if t['tag'] != None else None
    return d


class Tag(object):
    def __init__(self, input: dict[str, Any]) -> None:
        self.type = 'tag'
        self.t_type = str(input['t_type'])
        self.id = str(input['id'])
        self.tag = str(input['tag']) if 'tag' in input and input['tag'] != None else None

        self.f_type = f"'{self.type}'"
        self.f_t_type = f"'{self.t_type}'"
        self.f_id = f"'{self.id}'"
        self.f_tag = f"'{self.tag}'" if self.tag != None else 'null'

    def as_dict(self) -> dict[str, Any]:
        return { 'type': self.type, 't_type': self.t_type, 'id': self.id, 'tag': self.tag }


def set_tag_cache(name: str, cs: ChangeSet) -> SqlChangeSet:
    old = Tag(get_tag(name, cs.operations[0]['t_type'], cs.operations[0]['id']))
    raw_new = get_tag(name, cs.operations[0]['t_type'], cs.operations[0]['id'])

    new_dict = cs.operations[0]
    schema = get_tag_schema(name)
    for key, value in schema.items():
        if key in new_dict and not value['readonly']:
            raw_new[key] = new_dict[key]
    new = Tag(raw_new)

    table = ''
    if cs.operations[0]['t_type'] == TAG_TYPE_NODE:
        table = 'tags_node'
    elif cs.operations[0]['t_type'] == TAG_TYPE_LINK:
        table = 'tags_link'
    else:
        raise Exception('Only support NODE and Link')

    redo_sql = f"delete from {table} where id = {new.f_id};"
    if new.tag != None:
        redo_sql += f"\ninsert into {table} (id, tag) values ({new.f_id}, {new.f_tag});"

    undo_sql = f"delete from {table} where id = {old.f_id};"
    if old.tag != None:
        undo_sql += f"\ninsert into {table} (id, tag) values ({old.f_id}, {old.f_tag});"

    redo_cs = g_update_prefix | new.as_dict()
    undo_cs = g_update_prefix | old.as_dict()

    return SqlChangeSet(redo_sql, undo_sql, redo_cs, undo_cs)


def set_tag(name: str, cs: ChangeSet) -> ChangeSet:
    return execute_command(name, set_tag_cache(name, cs))


class InpTag:
    def __init__(self, line: str) -> None:
        tokens = line.split()

        num = len(tokens)
        has_desc = tokens[-1].startswith(';')
        num_without_desc = (num - 1) if has_desc else num

        self.t_type = str(tokens[0])
        self.id = str(tokens[1])
        self.tag = str(tokens[2])


def inp_in_tag(section: list[str]) -> ChangeSet:
    cs = ChangeSet()
    for s in section:
        # skip comment
        if s.startswith(';'):
            continue
        obj = InpTag(s)
        cs.append(g_update_prefix | {'type': 'tag', 't_type': obj.t_type, 'id': obj.id, 'tag': obj.tag})
    return cs


def inp_out_tag(name: str) -> list[str]:
    lines = []
    objs = read_all(name, 'select * from tags_node')
    for obj in objs:
        t_type = TAG_TYPE_NODE
        id = obj['id']
        tag = obj['tag']
        lines.append(f'{t_type} {id} {tag}')
    objs = read_all(name, 'select * from tags_link')
    for obj in objs:
        t_type = TAG_TYPE_LINK
        id = obj['id']
        tag = obj['tag']
        lines.append(f'{t_type} {id} {tag}')
    return lines
