from __future__ import annotations

import re
from typing import Any

MAX_INPUT_CHARS = 1_000_000
MAX_MATCHES = 2_000
MAX_SUB_OUTPUT = 500_000


def regex_findall(
    text: str,
    pattern: str,
    *,
    max_matches: int = MAX_MATCHES,
    flags: int = 0,
) -> dict[str, Any]:
    if len(text) > MAX_INPUT_CHARS:
        return {"ok": False, "error": f"text exceeds {MAX_INPUT_CHARS} chars"}
    try:
        rx = re.compile(pattern, flags)
    except re.error as e:
        return {"ok": False, "error": f"invalid regex: {e}"}
    matches: list[str] = []
    truncated = False
    for i, m in enumerate(rx.finditer(text)):
        if i >= max_matches:
            truncated = True
            break
        matches.append(m.group(0))
    return {"ok": True, "matches": matches, "count": len(matches), "truncated": truncated}


def regex_sub(
    text: str,
    pattern: str,
    repl: str,
    *,
    count: int = 0,
    flags: int = 0,
) -> dict[str, Any]:
    if len(text) > MAX_INPUT_CHARS:
        return {"ok": False, "error": f"text exceeds {MAX_INPUT_CHARS} chars"}
    try:
        rx = re.compile(pattern, flags)
    except re.error as e:
        return {"ok": False, "error": f"invalid regex: {e}"}
    out, n = rx.subn(repl, text, count=count)
    if len(out) > MAX_SUB_OUTPUT:
        return {
            "ok": False,
            "error": f"substitution output exceeds {MAX_SUB_OUTPUT} chars",
            "replacements": n,
        }
    return {"ok": True, "text": out, "replacements": n}


def normalize_whitespace(text: str) -> dict[str, Any]:
    if len(text) > MAX_INPUT_CHARS:
        return {"ok": False, "error": f"text exceeds {MAX_INPUT_CHARS} chars"}
    out = " ".join(text.split())
    return {"ok": True, "text": out}


def split_lines(text: str, *, keepends: bool = False) -> dict[str, Any]:
    if len(text) > MAX_INPUT_CHARS:
        return {"ok": False, "error": f"text exceeds {MAX_INPUT_CHARS} chars"}
    lines = text.splitlines(keepends)
    return {"ok": True, "lines": lines, "count": len(lines)}


def run_operation(operation: str, payload: dict[str, Any]) -> dict[str, Any]:
    op = operation.strip().lower()
    text = str(payload.get("text") or "")
    pattern = str(payload.get("pattern") or "")
    flags = 0
    if payload.get("ignore_case"):
        flags |= re.IGNORECASE
    if payload.get("multiline"):
        flags |= re.MULTILINE
    if op == "regex_findall":
        return regex_findall(
            text,
            pattern,
            max_matches=int(payload.get("max_matches", MAX_MATCHES)),
            flags=flags,
        )
    if op == "regex_sub":
        return regex_sub(
            text,
            pattern,
            str(payload.get("repl") or ""),
            count=int(payload.get("count", 0)),
            flags=flags,
        )
    if op == "normalize":
        return normalize_whitespace(text)
    if op == "split_lines":
        return split_lines(text, keepends=bool(payload.get("keepends", False)))
    return {"ok": False, "error": f"unknown operation: {operation}"}
