import re

_ADJECTIVE_PREDICATES = frozenset({
    "tired", "down", "up", "high", "low", "slow", "fast", "broken", "ready",
    "busy", "empty", "full", "late", "early", "wrong", "right", "stable",
    "unstable", "flaky", "hot", "cold", "open", "closed", "live", "dead",
    "stuck", "frozen", "active", "inactive", "available", "unavailable",
    "online", "offline", "good", "bad", "fine", "sick", "free", "gone",
    "done", "back", "over", "mad", "angry", "happy", "sad", "hungry",
})

_BARE_LOCATIVES = frozenset({"here", "there"})

_PREPOSITION_LOCATIVES = frozenset({"at", "in", "on"})

_NONVERB_ING = frozenset({
    "thing", "nothing", "something", "everything", "anything", "bring",
    "string", "spring", "morning", "evening", "during", "sibling",
    "ceiling", "darling",
})

_EQUATIVE_SUBJECTS = frozenset({
    "goal", "aim", "point", "idea", "plan", "problem", "issue", "question",
    "key", "trick", "focus", "priority", "challenge", "task", "job",
    "mission", "objective", "purpose", "strategy", "answer", "solution",
})

_ZERO_COPULA_RE = re.compile(
    r"\b(?P<subj>[A-Za-z][\w']*)[ \t]+(?P<cop>is|are)[ \t]+"
    r"(?P<pred>[A-Za-z][\w']*)(?=(?P<after>[ \t]+[A-Za-z][\w']*)?)"
)


def _deletable_predicate(subj: str, pred: str, has_after: bool) -> bool:
    pred_l = pred.lower()
    if pred_l == "gonna":
        return True
    if pred_l in _ADJECTIVE_PREDICATES:
        return True
    if pred_l in _BARE_LOCATIVES:
        return True
    if pred_l in _PREPOSITION_LOCATIVES and has_after:
        return True
    if (
        pred_l.endswith("ing")
        and len(pred_l) >= 5
        and pred_l not in _NONVERB_ING
        and subj.lower() not in _EQUATIVE_SUBJECTS
    ):
        return True
    return False


def zero_copula(text: str) -> str:
    """Labov, W. "Contraction, Deletion, and Inherent Variability of the English Copula," Language 45(4), 1969."""
    def repl(m: re.Match) -> str:
        subj = m.group("subj")
        pred = m.group("pred")
        if _deletable_predicate(subj, pred, m.group("after") is not None):
            return f"{subj} {pred}"
        return m.group(0)

    return _ZERO_COPULA_RE.sub(repl, text)


_HABITUAL_RE = re.compile(
    r"\b(?P<prev>[\w']+)[ \t]+"
    r"(?P<adv>usually|typically|often|regularly|normally)[ \t]+"
    r"(?P<verb>[a-z][a-z]+)\b"
)

_HABITUAL_PREV_BLOCK = frozenset({
    "is", "are", "am", "was", "were", "be", "been", "being", "will", "would",
    "can", "could", "may", "might", "must", "should", "shall", "has", "have",
    "had", "do", "does", "did", "to", "not", "don't", "doesn't", "didn't",
    "won't", "can't", "isn't", "aren't", "wasn't", "weren't", "it's", "he's",
    "she's", "that's",
})

_HABITUAL_VERB_BLOCK = frozenset({
    "the", "a", "an", "this", "that", "these", "those", "it", "its", "he",
    "she", "they", "we", "you", "i", "there", "is", "are", "was", "were",
    "am", "be", "been", "being", "not", "very", "quite", "really", "pretty",
    "more", "most", "so", "too", "all", "some", "when", "if", "just", "only",
    "never", "always", "still", "also", "because", "but", "and", "or", "in",
    "on", "at", "to", "for", "with", "from", "by", "of", "has", "had", "did",
    "will", "would", "can", "could",
})

_MONOSYLLABIC_CVC_RE = re.compile(
    r"[bcdfghjklmnpqrstvwxyz]*[aeiou][bcdfgklmnprstvz]"
)


def _third_person_base(verb: str) -> str:
    if verb.endswith("ies") and len(verb) > 4:
        return verb[:-3] + "y"
    if verb.endswith(("ches", "shes", "sses", "xes", "zes", "oes")):
        return verb[:-2]
    if verb.endswith("s") and not verb.endswith("ss"):
        return verb[:-1]
    return verb


def _in_form(base: str) -> str:
    if base.endswith("ie"):
        return base[:-2] + "yin"
    if base.endswith("e") and not base.endswith("ee"):
        return base[:-1] + "in"
    if _MONOSYLLABIC_CVC_RE.fullmatch(base):
        return base + base[-1] + "in"
    return base + "in"


def habitual_be(text: str) -> str:
    """Labov 1969; Fasold, R. "Tense Marking in Black English," 1972."""
    def repl(m: re.Match) -> str:
        prev = m.group("prev")
        verb = m.group("verb")
        if (
            prev.lower() in _HABITUAL_PREV_BLOCK
            or verb in _HABITUAL_VERB_BLOCK
            or verb.endswith("ing")
        ):
            return m.group(0)
        return f"{prev} be {_in_form(_third_person_base(verb))}"

    return _HABITUAL_RE.sub(repl, text)


_COMPLETIVE_RE = re.compile(
    r"\b(?P<aux>[Hh]ave|[Hh]as|[Hh]ad|'ve|'d)[ \t]+(?:already|completely)[ \t]+"
    r"(?P<part>[a-z][\w]*)\b(?P<ger>[ \t]+[a-z]\w*ing\b)?"
)

_COMPLETIVE_PART_BLOCK = frozenset({
    "been", "being", "a", "an", "the", "not", "no", "all", "so", "too",
    "very", "quite",
})

_IRREGULAR_PARTICIPLE_PAST = {
    "written": "wrote", "gone": "went", "taken": "took", "seen": "saw",
    "eaten": "ate", "broken": "broke", "given": "gave", "driven": "drove",
    "spoken": "spoke", "gotten": "got", "begun": "began", "drunk": "drank",
    "sung": "sang", "flown": "flew", "drawn": "drew", "known": "knew",
    "grown": "grew", "thrown": "threw", "shaken": "shook", "ridden": "rode",
    "risen": "rose", "chosen": "chose", "frozen": "froze",
    "forgotten": "forgot", "hidden": "hid", "bitten": "bit", "fallen": "fell",
    "beaten": "beat", "worn": "wore", "torn": "tore", "sworn": "swore",
    "woken": "woke", "stolen": "stole", "come": "came", "become": "became",
    "run": "ran", "done": "did",
}

_AMBIGUOUS_PAST = frozenset({
    "left", "made", "sent", "built", "kept", "put", "set", "cut", "hit",
    "read", "paid", "met", "sold", "told", "found", "heard", "held", "lost",
    "meant", "shut", "spent", "won", "fed", "led", "said", "brought",
    "bought", "caught", "taught", "thought", "fought", "felt", "dealt",
    "stood", "understood", "got", "let", "quit", "split", "spread", "bet",
    "cost", "hurt", "laid", "lit", "slid", "stuck", "struck", "swung",
    "hung", "dug", "spun", "sat",
})

_BASE_PAST = {
    "run": "ran", "go": "went", "come": "came", "write": "wrote",
    "make": "made", "take": "took", "build": "built", "send": "sent",
    "set": "set", "read": "read", "do": "did", "get": "got",
    "buy": "bought", "think": "thought", "teach": "taught",
    "catch": "caught", "fight": "fought", "bring": "brought",
    "find": "found", "hold": "held", "keep": "kept", "leave": "left",
    "lose": "lost", "mean": "meant", "meet": "met", "pay": "paid",
    "sell": "sold", "spend": "spent", "tell": "told", "win": "won",
    "draw": "drew", "drive": "drove", "eat": "ate", "speak": "spoke",
    "break": "broke",
}


def _past_of_participle(part: str) -> str | None:
    if part in _IRREGULAR_PARTICIPLE_PAST:
        return _IRREGULAR_PARTICIPLE_PAST[part]
    if part.endswith("ed"):
        return part
    if part in _AMBIGUOUS_PAST:
        return part
    return None


def _past_of_gerund(ger_word: str) -> str:
    raw = ger_word[:-3]
    if len(raw) >= 2 and raw[-1] == raw[-2] and raw[-1] not in "aeiou":
        dedoubled = raw[:-1]
    else:
        dedoubled = raw
    if dedoubled in _BASE_PAST:
        return _BASE_PAST[dedoubled]
    if raw + "e" in _BASE_PAST:
        return _BASE_PAST[raw + "e"]
    if raw.endswith("y") and len(raw) >= 2 and raw[-2] not in "aeiou":
        return raw[:-1] + "ied"
    if raw.endswith("e"):
        return raw + "d"
    return raw + "ed"


def completive_done(text: str) -> str:
    """Green, L. "African American English: A Linguistic Introduction," 2002; Spears, A. Language 58(4), 1982."""
    def repl(m: re.Match) -> str:
        aux = m.group("aux")
        part = m.group("part")
        ger = m.group("ger")
        if part in _COMPLETIVE_PART_BLOCK:
            return m.group(0)
        prefix = " " if aux.startswith("'") else ""
        done = "Done" if aux[0] == "H" else "done"
        if (
            part in ("finished", "completed")
            and ger
            and ger.strip() not in _NONVERB_ING
        ):
            return f"{prefix}{done} {_past_of_gerund(ger.strip())}"
        past = _past_of_participle(part)
        if past is None:
            return m.group(0)
        return f"{prefix}{done} {past}{ger or ''}"

    return _COMPLETIVE_RE.sub(repl, text)


_FINNA_RE = re.compile(
    r"\b(?P<subj>[A-Za-z][\w]*)(?P<cop>'m|'s|'re|[ \t]+am|[ \t]+is|[ \t]+are)[ \t]+"
    r"(?:about[ \t]+to|going[ \t]+to|getting[ \t]+ready[ \t]+to)[ \t]+"
    r"(?P<verb>[a-z][\w]*)\b"
)

_FINNA_VERB_BLOCK = frozenset({
    "the", "a", "an", "this", "that", "these", "those", "my", "your", "his",
    "her", "its", "our", "their", "me", "him", "them", "us", "it", "you",
    "school", "work", "bed", "town", "church", "lunch", "dinner",
    "breakfast", "college", "court", "jail", "and", "or", "not",
})


def prospective_finna(text: str) -> str:
    """Green 2002; Thomas & Grinsell, SULA 7."""
    def repl(m: re.Match) -> str:
        subj = m.group("subj")
        cop = m.group("cop").strip()
        verb = m.group("verb")
        if verb in _FINNA_VERB_BLOCK:
            return m.group(0)
        if cop in ("'m", "am"):
            return f"{subj}'m finna {verb}"
        return f"{subj} finna {verb}"

    return _FINNA_RE.sub(repl, text)


_STEADY_ADV = r"(?:constantly|continuously|persistently|nonstop)"
_STEADY_CHAIN = (
    rf"{_STEADY_ADV}(?:(?:,[ \t]*(?:and[ \t]+)?|[ \t]+and[ \t]+|[ \t]+){_STEADY_ADV})*"
)

_STEADY_POST_RE = re.compile(
    rf"\b(?P<cop>is|are)[ \t]+(?P<advs>{_STEADY_CHAIN})[ \t]+(?P<verb>[a-z]\w*ing)\b"
)

_STEADY_PRE_RE = re.compile(
    rf"\b(?P<advs>{_STEADY_CHAIN})[ \t]+(?P<cop>is|are)[ \t]+(?P<verb>[a-z]\w*ing)\b"
)


def continuative_steady(text: str) -> str:
    """Baugh, J. "Black Street Speech: Its History, Structure, and Survival," 1983."""
    def repl(m: re.Match) -> str:
        verb = m.group("verb")
        if verb in _NONVERB_ING:
            return m.group(0)
        return f"steady {verb[:-1]}"

    out = _STEADY_POST_RE.sub(repl, text)
    return _STEADY_PRE_RE.sub(repl, out)


MARKERS = {
    "zero_copula": zero_copula,
    "habitual_be": habitual_be,
    "completive_done": completive_done,
    "prospective_finna": prospective_finna,
    "continuative_steady": continuative_steady,
}
