"""Solution for silent_practice.py."""
import re

MAX_WORDS = 40
PLACEHOLDER_RE = re.compile(r"\[[^\]]{1,40}\]|\{[^}]{1,40}\}|<[A-Za-z ]{1,30}>")

REFUSAL = "I don't know based on the provided documents."
NOT_FOUND = "I couldn't find anything about that in the documents."
UNAVAILABLE = "Search is unavailable right now, so I can't answer from the documents. Please try again in a minute."


def validate_reply(reply, order_id=None):
    problems = []
    n = len(reply.split())
    if n > MAX_WORDS:
        problems.append(f"reply has {n} words; the limit is {MAX_WORDS}")
    if order_id and order_id not in reply:
        problems.append(f"reply doesn't mention the order number {order_id}")
    m = PLACEHOLDER_RE.search(reply)
    if m:
        problems.append(f"reply contains a placeholder: {m.group(0)}")
    return problems


def reask_note(problems):
    return "That reply is not valid: " + "; ".join(problems) + ". Reply again with only the corrected JSON."


def ask_until_valid(ask, validate, reasks=2, feedback=True):
    note = None
    for attempt in range(reasks + 1):
        text = ask(note)
        problems = validate(text)
        if not problems:
            return {"text": text, "valid": True, "attempts": attempt + 1}
        note = reask_note(problems) if feedback else None
    return {"text": text, "valid": False, "attempts": reasks + 1}


def is_refusal(answer):
    a = answer.strip().lower().replace("’", "'")
    return any(m.lower() in a for m in (REFUSAL, NOT_FOUND, UNAVAILABLE)) or a.startswith("i don't know")


def refusal_rate(answers):
    return sum(is_refusal(a) for a in answers) / len(answers) if answers else 0.0


def refusal_alarm(answers, normal_rate, margin=0.2):
    return refusal_rate(answers) > normal_rate + margin


def answer_defended(question, search, answer):
    passages = None
    for _ in range(2):
        try:
            passages = search(question)
            break
        except TimeoutError:
            continue
    else:
        return UNAVAILABLE
    if not passages:
        return NOT_FOUND
    return answer(question, passages)
