from . import command, STOP_PROCESSING, USER_SET, users_map
from .woordenlijst import woordenlijst_request
import requests
from functools import lru_cache
from glom import glom, PathAccessError, T
from typing import Dict, List, Union, Any
import re
import threading
import string
import emoji
import time
import logging
import traceback
from urllib.parse import quote_plus

logger = logging.getLogger('spellcheck')

config: Dict[str, Any]

def check_voornaam(naam: str) -> bool:
    if naam in config["known_names"]:
        logger.debug(f'check_voornaam: {naam} in list of known names.')
        return True
    try:
        resp = requests.get('https://www.meertens.knaw.nl/nvb/naam/is/'+quote_plus(naam))
        if f"notfound" in resp.text:
            logger.debug(f"check_voornaam: {naam} is geen voornaam.")
            return False
        else:
            config["known_names"].append(naam)
            return True
    except requests.ConnectionError:
        traceback.print_exc()
        return True

def check_achternaam(naam: str) -> bool:
    url = "https://cbgfamilienamen.nl/nfb/lijst_namen.php"
    params = {'operator': 'eq', 'naam': naam}
    if naam in config["known_names"]:
        logger.debug(f'check_achternaam: {naam} in list of known names.')
        return True
    try:
        resp = requests.get(url, params=params)
        if resp.status_code == 200:
            return False
        elif resp.status_code == 302:
            logger.debug(f"check_achternaam: {naam} is een achternaam.")
            config["known_names"].append(naam)
            return True
    except requests.ConnectionError:
        traceback.print_exc()
        return True

def wiktionary_lookup(url: str, params: Dict[str, str]) -> bool:
    try:
        resp = requests.get(url, params=params)
        data = resp.json()
        words = set()
        for key, value in glom(data, 'query.pages').items():
            if key != "-1":
                logger.info(f"Valid word: {value['title']}")
                config['known_words'].append(value['title'])
                words.add(value['title'])
            else:
                logger.info(f"Invalid word: {value['title']}")
                missing = True
        return words
    except PathAccessError:
        return set()

def allow_words(words: Union[str, List[str]]) -> bool:
    words = {word for word in words if word not in config['known_words']}
    titles = '|'.join(words)
    if not titles:
        return True

    en_url = "https://en.wiktionary.org/w/api.php"
    nl_url = "https://nl.wiktionary.org/w/api.php"
    params = {'action': 'query', 'titles': titles, 'format': 'json'}

    en_words = wiktionary_lookup(en_url, params)
    words -= en_words

    if not words:
        return True

    params['titles'] = '|'.join(words)
    nl_words = wiktionary_lookup(nl_url, params)
    words -= nl_words

    if not words:
        return True

    for word in words:
        if not woordenlijst_request(word).get('_embedded', {}).get('exact', False):
            return False
        else:
            config['known_words'].append(word)

    return True

@command('unspellcheck (' + USER_SET + ')', auth=True, pass_groups=True)
def unregister_spellcheck(bea, bot, update, groups):
    users = groups[0]

    def unregister_single(uid):
        for cmd in bea.commands:
            if hasattr(cmd, 'spellcheck_for') and cmd.spellcheck_for == uid:
                break
        else:
            pass
        bea.commands.remove(cmd)
    users_map(bea, users, unregister_single)
    bea.reply('Prima.')

@command('spellcheck (' + USER_SET + ')', auth=True, pass_groups=True)
def register_spellcheck(bea, bot, update, groups):
    users = groups[0]

    if not hasattr(bea, 'spellcheck_debug'):
        bea.spellcheck_debug = False

    def register_spellcheck_single(uid):
        def message_processor(bea, bot, update):
            if update.effective_user.id != uid:
                logger.debug('Discarding message because of UID mismatch.')
                return
            if update.effective_message is None or not update.effective_message.text:
                logger.debug('Discarding message because it does not contain text.')
                return
            text = update.effective_message.text
            logger.debug(f'Handling message: {text}')
            to_lookup = []
            for word in text.split():
                orig_word = word
                word = word.strip("!?.:'\", ")
                word = re.sub('[' + ''.join(emoji.UNICODE_EMOJI.keys()) + ']', '', word)
                if orig_word[0] in ':=' and all(w in string.punctuation for w in orig_word[1:]):
                    continue
                if check_voornaam(word) or check_achternaam(word):
                    continue
                logger.debug(f'Processing word: {word} {orig_word!r}')
                if word.isupper():
                    word = word.lower()
                if word.istitle():
                    word = word.lower()
                if word.isdigit():
                    continue
                if not word:
                    continue
                if word.startswith('@') and all(w in string.ascii_letters + string.digits + '_' for w in word[1:]):
                    continue
                logger.info(f'looking up word: {word!r}')
                to_lookup.append(word)
            logger.debug(f'looking up: {to_lookup}')
            if any(len(word) == 1 for word in to_lookup) or not allow_words(to_lookup):
                logging.debug('Message contains misspelling.')
                try:
                    update.effective_message.delete()
                except:
                    msg = bea.reply(f'fix spelling pl0x')
                    t = threading.Timer(10, msg.delete)
                    t.start()
                return STOP_PROCESSING
        message_processor.spellcheck_for = uid
        bea.commands.insert(0, message_processor)

    users_map(bea, users, register_spellcheck_single)
    bea.reply('Komt voor de bakker.')

def init(bea, _config):
    global config
    config = _config
    if not 'known_words' in config:
        config['known_words'] = []
    if not 'known_names' in config:
        config['known_names'] = []
    return [register_spellcheck, unregister_spellcheck]
