import html
import json
import logging
import os
from typing import Any, List

logging.basicConfig(level="INFO", format="%(asctime)s - %(name)s - %(levelname)s - %(message)s")

from dotenv import load_dotenv
from flask import Flask, render_template

from .constants import POS_MAPPING, SPACY_EXPLAIN, nlp
from .forms import QueryForm
from .mw import MerriamWebster

logger = logging.getLogger(__name__)

load_dotenv()

app = Flask(__name__)
app.secret_key = os.environ["SECRET_KEY"]
mw_client = MerriamWebster(os.environ["MW_LEARNERS_API_KEY"])


def get_definitions(text: str, base_form: str, pos: str) -> List[Any]:
    # Search by base form (e.g. burning -> burn)
    res = mw_client.search(base_form)
    if not res:
        return []

    # Exact matches
    res = [e for e in res if text == e["base_form"].lower()] or res

    # "Stems" of a word contain all its possible forms (e.g. be: is, was, been, etc.)
    res = [e for e in res if text in e["stems"]
           ] or [e for e in res if base_form in e["stems"]] or res

    # Filter out senses which are not of the same part of speech identified earlier
    res = [e for e in res if e["pos"] in POS_MAPPING.get(pos, [])] or res

    return res[:3]  # Up to 3 definitions


@app.route("/", methods=["GET", "POST"])
def home():
    form = QueryForm()
    if form.validate_on_submit():
        sentence = form.sentence.data.strip()
        logger.info("Query: %s", sentence)
        doc = nlp(sentence)
        spans = []
        for token in doc:
            escaped_text = html.escape(token.text)

            if token.pos_ in ("PUNCT", "SYM", "SPACE", "X") or not token.text.isalpha():
                # No definition
                spans.append((escaped_text, token))
                continue

            if token.pos_ == "PROPN":
                spans.append(
                    (
                        f'<a class="ss" data-pos-explain="{html.escape(SPACY_EXPLAIN[token.pos_])}">'
                        + escaped_text + '</a>', token
                    )
                )
                continue

            entries = get_definitions(token.text.lower(), token.lemma_, token.pos_)
            spans.append(
                (
                    f'<a class="ss" data-pos-explain="{html.escape(SPACY_EXPLAIN[token.pos_])}" '
                    f'data-defs="{html.escape(json.dumps(entries))}">' + escaped_text + "</a>",
                    token
                )
            )

        s = sentence
        sentence_html = ""
        for span, token in spans:
            while not s.startswith(token.text):
                sentence_html += html.escape(s[0])
                s = s[1:]
            sentence_html += span
            s = s[len(token.text):]
        sentence_html += html.escape(s)

        mw_client.save_cache()
        return render_template("home.html", form=form, sentence=sentence_html)

    return render_template("home.html", form=form)

import html
import json
import logging
import os
from typing import Any, List

logging.basicConfig(level="INFO", format="%(asctime)s - %(name)s - %(levelname)s - %(message)s")

from dotenv import load_dotenv
from flask import Flask, render_template

from .constants import POS_MAPPING, SPACY_EXPLAIN, nlp
from .forms import QueryForm
from .mw import MerriamWebster

logger = logging.getLogger(__name__)

load_dotenv()

app = Flask(__name__)
app.secret_key = os.environ["SECRET_KEY"]
mw_client = MerriamWebster(os.environ["MW_LEARNERS_API_KEY"])


def get_definitions(text: str, base_form: str, pos: str) -> List[Any]:
    # Search by base form (e.g. burning -> burn)
    res = mw_client.search(base_form)
    if not res:
        return []

    # Exact matches
    res = [e for e in res if text == e["base_form"].lower()] or res

    # "Stems" of a word contain all its possible forms (e.g. be: is, was, been, etc.)
    res = [e for e in res if text in e["stems"]
           ] or [e for e in res if base_form in e["stems"]] or res

    # Filter out senses which are not of the same part of speech identified earlier
    res = [e for e in res if e["pos"] in POS_MAPPING.get(pos, [])] or res

    return res[:3]  # Up to 3 definitions


@app.route("/", methods=["GET", "POST"])
def home():
    form = QueryForm()
    if form.validate_on_submit():
        sentence = form.sentence.data.strip()
        logger.info("Query: %s", sentence)
        doc = nlp(sentence)
        spans = []
        for token in doc:
            escaped_text = html.escape(token.text)

            if token.pos_ in ("PUNCT", "SYM", "SPACE", "X") or not token.text.isalpha():
                # No definition
                spans.append((escaped_text, token))
                continue

            if token.pos_ == "PROPN":
                spans.append(
                    (
                        f'<a class="ss" data-pos-explain="{html.escape(SPACY_EXPLAIN[token.pos_])}">'
                        + escaped_text + '</a>', token
                    )
                )
                continue

            entries = get_definitions(token.text.lower(), token.lemma_, token.pos_)
            spans.append(
                (
                    f'<a class="ss" data-pos-explain="{html.escape(SPACY_EXPLAIN[token.pos_])}" '
                    f'data-defs="{html.escape(json.dumps(entries))}">' + escaped_text + "</a>",
                    token
                )
            )

        s = sentence
        sentence_html = ""
        for span, token in spans:
            while not s.startswith(token.text):
                sentence_html += html.escape(s[0])
                s = s[1:]
            sentence_html += span
            s = s[len(token.text):]
        sentence_html += html.escape(s)

        mw_client.save_cache()
        return render_template("home.html", form=form, sentence=sentence_html)

    return render_template("home.html", form=form)

# Search by base form (e.g. burning -> burn) # Exact matches # "Stems" of a word contain all its possible forms (e.g. be: is, was, been, etc.) # Filter out senses which are not of the same part of speech identified earlier # Up to 3 definitions # No definition