from json import dumps
from db import delete_entry_by_id, get_catalog, get_completed_entries, get_entries_for_delete, expand_entry, get_entry_for_id, dump_sql_table
from models import Entry, Sense
from io import BytesIO
from htmldocx import HtmlToDocx
from helper import *
from flask import render_template
from pathlib import Path


def dump_entry(entry: Entry):
    entry = expand_entry(entry, True)
    searchables = [entry.head, entry.head_ijk]
    searchables += entry.headwords_acc
    searchables += entry.headwords_acc_ijk
    searchables = list(set([base_form(x) for x in searchables]))

    return {
        "id" : entry.id,
        "headword": entry.head,
        "headword_ijk" : entry.head_ijk,
        "alternatives" : entry.headwords_acc,
        "alternatives_ijk" : entry.headwords_acc_ijk,
        "homograph" : entry.homograph,
        "pos" : entry.pos,
        "Fpos" : catalog[entry.pos] if entry.pos in catalog else entry.pos,
        "type" : entry.type,
        "frequency_per_million" : entry.freq,
        "frequency_rank" : entry.rang,
        "zero_sense" : dump_sense(entry.zero),
        "senses" : [dump_sense(x) for x in entry.senses if x.label != ''],
        "expressions" : [x.__dict__ for x in entry.expressions if x.role.startswith("израз ")],
        "proverbs" : [x.__dict__ for x in entry.expressions if x.role == "пословица"],
        "reflexive" : [x.__dict__ for x in entry.expressions if x.role == "повратни гл."],
        "notes" : [x.__dict__ for x in entry.notes if x.type == "јавна"],
        "searchables" : searchables
    }

def dump_sense(sense: Sense):
    return {
        "sense_id" : sense.id,
        "label" : sense.label,
        "domains" : sense.domains,
        "Fdomains" : [catalog[x] if x in catalog else x for x in sense.domains],
        "qualifiers" : sense.qualifiers,
        "Fqualifiers" : [catalog[x] if x in catalog else x for x in sense.qualifiers],
        "collocations" : sense.collocations,
        "indicator" : sense.indicator,
        "grammartext" : sense.grammartext,
        "grammartext_ijk" : sense.grammartext_ijk,
        #"grammar" : [x.__dict__ for x in sense.grammars],
        "definitions": [x.__dict__ for x in sense.definitions if x.type != "rsj" and x.type !="модел" and x.text],
        "collocations" : [x.__dict__ for x in sense.examples if x.type == "синтагматски"],
        "examples" : [x.__dict__ for x in sense.examples if x.type not in ["rsj", "модел", "синтагматски"] and x.text],
        "connected" : [x.__dict__ for x in sense.connected if x.role not in ["супр.", "синоним"] and "израз" not in x.role and "гл." not in x.role and "одредница" not in x.role],
        "sinonimi" : [x.__dict__ for x in sense.connected if x.role=="синоним"],
        "antonimi" : [x.__dict__ for x in sense.connected if x.role=="супр."],
    }


catalog = {x: y for (x, y) in get_catalog()}
entries_all = pool.map(dump_entry, get_completed_entries())
entries = [x for x in entries_all if x["type"]!="subentry"]
entries_heads = [x["headword"]+ " " + str(x["homograph"]) if x["homograph"] else x["headword"] for x in entries]
limit = 10


with open(str(Path(__file__).parent.resolve()) + '/static/empty_db.sql', 'r') as file:
    db_schema = file.read()


def add_entry_to_list(id, entries=entries, entries_heads = entries_heads):
    x = dump_entry(get_entry_for_id(id))
    entries.append(x)
    head = x["headword"]+ " " + str(x["homograph"]) if x["homograph"] else x["headword"]
    entries_heads.append(head)


def getPos(text : str):
    result = [x["Fpos"] for x in entries if idem(text, x["headword"]) or idemin(text, x["alternatives"])]
    if not result:
        result = [x["Fpos"] for x in entries if idemin(text, x["searchables"])]
    return dumps(result, ensure_ascii=False, indent=4)

def get_sound(text : str):
    return None

def getSimilar(text : str):
    return None

def get_synonyms(text : str):
    results = [x["senses"]+[x["zero_sense"]] for x in entries if idem(text, x["headword"]) or idemin(text, x["alternatives"])]
    if not results:
        results = [x["senses"]+[x["zero_sense"]] for x in entries if idemin(text, x["searchables"])]
    result = []
    for x in results[0]:
        if x["sinonimi"]:
            result += x["sinonimi"]["head"]
    return dumps(result, ensure_ascii=False, indent=4)

def get_antonyms(text : str):
    results = [x["senses"]+[x["zero_sense"]] for x in entries if idem(text, x["headword"]) or idemin(text, x["alternatives"])]
    if not results:
        results = [x["senses"]+[x["zero_sense"]] for x in entries if idemin(text, x["searchables"])]
    result = []
    for x in results[0]:
        if x["antonimi"]:
            result += x["antonimi"]["head"]
    return dumps(result, ensure_ascii=False, indent=4)

def get_ijekavica(text : str):
    result = [x["alternatives_ijk"]+[x["headword_ijk"]] for x in entries if idem(text, x["headword"]) or idemin(text, x["alternatives"])]
    if not result:
        result = [x["alternatives_ijk"]+[x["headword_ijk"]] for x in entries if idemin(text, x["searchables"])]
    return dumps(result, ensure_ascii=False, indent=4)

def getWordsStartingWith(text : str):
    result = [x for x in entries if startsw(text,x["headword"]) or startswin(text, x["alternatives"])]
    if not result:
        result = [x for x in entries if startswin(text, x["searchables"])]
    return dumps(result[:limit], ensure_ascii=False, indent=4)

def getWordsContaining(text : str):
    result = [x for x in entries if isin(text, x["headword"]) or isinin(text, x["alternatives"])]
    if not result:
        result = [x for x in entries if isinin(text, x["searchables"])]
    return dumps(result[:limit], ensure_ascii=False, indent=4)

def getWordsEndingWith(text: str):
    result = [x for x in entries if endsw(text, x["headword"]) or endswin(text, x["alternatives"])]
    if not result:
        result = [x for x in entries if endswin(text, x["searchables"])]
    return dumps(result[:limit], ensure_ascii=False, indent=4)

def getDomains(text : str):
    result = [x for x in entries if idem(text, x["headword"]) or idemin(text, x["alternatives"])]
    if not result:
        result = [x for x in entries if idemin(text, x["searchables"])]
    domains = []
    for x in result:
        for y in x["senses"]:
            domains.extend(y["Fdomains"]) 
    domains = list(set(domains))
    return dumps(domains, ensure_ascii=False, indent=4)

def getWordsForDomains(text : str):
    domains = text.split(",")
    result = [x for x in entries if overlap(sum([y["Fdomains"] for y in x["senses"]], []), domains)]
    if not result:
        result = [x for x in entries if overlap([base_form(z) for z in sum([y["Fdomains"] for y in x["senses"]], [])], domains)]
    return dumps(result[:limit], ensure_ascii=False, indent=4)

def getOneLetterOff(text : str):
    result = [x for x in entries if islike(text, x["headword"]) or islikein(text, x["alternatives"])]
    if not result:
        result = [x for x in entries if islikein(text, x["searchables"])]
    return dumps(result[:limit], ensure_ascii=False, indent=4)

def getWordFull(text : str):
    result = [x for x in entries if idem(text, x["headword"]) or idemin(text, x["alternatives"])]
    if not result:
        result = [x for x in entries if idemin(text, x["searchables"])]
    return dumps(result, ensure_ascii=False, indent=4)

def getLatin(text : str):
    return translit(getWordFull(text))

def single_entry(id):
    result = [x for x in entries if str(x["id"]) == str(id)]
    return dumps(result, ensure_ascii=False, indent=4)

def single_entry_html(id):
    result = [x for x in entries if str(x["id"]) == str(id)]
    return render_template("rssj_entries.html", entries=result, dump_entry_html=dump_entry_html, dump_sense_html=dump_sense_html, sub=False)

def getAllWordsList(text : str):
    result = [x["headword"] for x in entries]
    return dumps(result, ensure_ascii=False, indent=4)

def dump_entry_html_api(text : str):
    result = [x for x in entries if isin(text, x["headword"]) or isinin(text, x["alternatives"])]
    if not result:
        result = [x for x in entries if isinin(text, x["searchables"])]
    return render_template("rssj_entries.html", entries=result, dump_entry_html=dump_entry_html, dump_sense_html=dump_sense_html, sub=False)


def dump_json(mime="application/json"):
    if not entries:
        return "\"Тренутно нема завршених чланака.\"", mime
    return dumps(entries, ensure_ascii=False, indent=4), mime

def dump_html(mime="text/html"):
    if not entries:
        return "Тренутно нема завршених чланака.", mime
    return render_template("rssj_entries.html", entries=entries, dump_entry_html=dump_entry_html2, dump_sense_html=dump_sense_html), mime

def dump_word(mime="application/vnd.openxmlformats-officedocument.wordprocessingml.document"):
    if not entries:
        return "Тренутно нема завршених чланака.", mime
    html = dump_html()[0]
    new_parser = HtmlToDocx()
    output = BytesIO()
    x = new_parser.parse_html_string(html)
    x.save(output)
    return output.getbuffer().tobytes(), mime

def dump_sql(mime="application/sql"):
    if not entries:
        return "Тренутно нема завршених чланака.", mime
    entry_ids = []
    sense_ids = []
    relation_ids = []
    for x in entries:
        entry_ids.append(x["id"])
        sense_ids.append(x["zero_sense"]["sense_id"])
        if x["expressions"]:
            relation_ids.extend([ex["rid"] for ex in x["expressions"]])
        if x["proverbs"]:
            relation_ids.extend([ex["rid"] for ex in x["proverbs"]])
        if x["reflexive"]:
            relation_ids.extend([ex["rid"] for ex in x["reflexive"]])
        if x["senses"]:
            for s in x["senses"]:
                sense_ids.append(s["sense_id"])
                if s["connected"]:
                    relation_ids.extend([ex["rid"] for ex in s["connected"]])

    sql_table_dump_filter = {
    "entry_id" : (["rssj.entry", "rssj.sense", "rssj.estatus", "rssj.pronunciation", "rssj.note"], entry_ids, {"rssj.note": "AND type != 'приватна'"}),
    "sense_id" :(["rssj.definition", "rssj.example", "rssj.corpus_frequency"], sense_ids, {"rssj.definition": "AND type != 'rsj' AND type != 'модел'", "rssj.example": "AND type != 'rsj' AND type != 'модел'"}),
    "relation_id" : (["rssj.relation"], relation_ids, None),
    "member_sense_id" : (["rssj.member"], sense_ids, None),
    "'1'" : (["rssj.catalog"], ["1"], None),
    }

    result = db_schema
    for cid in sql_table_dump_filter:
        id_column = cid
        tables = sql_table_dump_filter[cid][0]
        ids = sql_table_dump_filter[cid][1]
        type_filter = sql_table_dump_filter[cid][2]
        if ids:
            for table in tables:
                result += dump_sql_table(table, id_column, ids, type_filter)
        else:
            print(id_column)
    return result, mime


def dump_sense_html(s: Sense, zero=False, pos=None):
    return render_template("rssj_sense.html", s=s, zero=zero, pos=pos, specpunkt=cfg["specpunkt"])


def dump_entry_html(id: int, sub: bool = False):
    es = [dump_entry(get_entry_for_id(id))]
    return render_template("rssj_entries.html", specpunkt=cfg["specpunkt"], entries=es, dump_entry_html=dump_entry_html, dump_sense_html=dump_sense_html, sub=sub)

def dump_entry_html2(id: int, sub: bool = False):
    es = [x for x in entries_all if x["id"]==id]
    return render_template("rssj_entries.html", specpunkt=cfg["specpunkt"], entries=es, dump_entry_html=dump_entry_html2, dump_sense_html=dump_sense_html, sub=sub)


services = {
    "getPos" : (getPos, True, 'application/json'),
    
    # "getPronounciation" : (get_sound, True),
    # "getSimilar" : (getSimilar, False),

    "getSynonyms" : (get_synonyms, False, 'application/json'), 
    "getAntonyms" : (get_antonyms, False, 'application/json'), 
    "getIjekavsku" : (get_ijekavica, True, 'application/json'), 

    "getWordsStartingWith" : (getWordsStartingWith, False, 'application/json'), 
    "getWordsContaining" : (getWordsContaining, False, 'application/json'),
    "getWordsEndingWith" : (getWordsEndingWith, False, 'application/json'),
    "getDomains" : (getDomains, False, 'application/json'),
    "getWordsForDomains" : (getWordsForDomains, False, 'application/json'),
    "getOneLetterOff" : (getOneLetterOff, False, 'application/json'),
    "getWordFull": (getWordFull, False, 'application/json'),
    "getLatin" : (getLatin, False, 'application/json'),
    "getDescription" : (getWordFull, False, 'application/json'),
    "getAllWordsList" : (getAllWordsList, False, 'application/json'),
    "entry" : (single_entry, False, 'application/json'),
    "entryHtml" : (dump_entry_html_api, True, 'text/html')
}

restricted_services = {
    "json": dump_json,
    "docx" : dump_word,
    "sql" : dump_sql,
    "html" : dump_html,
}

def delete_entry(id):
    global entries
    global entries_heads
    if id == '-999':
        ids = get_entries_for_delete()
    else:
        ids = [id]
    for id in ids:
        delete_entry_by_id(id)
        entries = [x for x in entries if x["id"]!=id]
        entries_heads = [x["headword"]+ " " + str(x["homograph"]) if x["homograph"] else x["headword"] for x in entries]
