"""Generate regex patterns from marked PDF text selections."""

import re

ANCHOR_MAX_LEN = 48
SNIPPET_RADIUS = 120

INDIAN_STATES = (
    "ANDHRA PRADESH|ARUNACHAL PRADESH|ASSAM|BIHAR|CHHATTISGARH|GOA|GUJARAT|HARYANA|"
    "HIMACHAL PRADESH|JHARKHAND|KARNATAKA|KERALA|MADHYA PRADESH|MAHARASHTRA|MANIPUR|"
    "MEGHALAYA|MIZORAM|NAGALAND|ODISHA|PUNJAB|RAJASTHAN|SIKKIM|TAMIL NADU|TELANGANA|"
    "TRIPURA|UTTAR PRADESH|UTTARAKHAND|WEST BENGAL|DELHI|PONDICHERRY|CHANDIGARH"
)

COMMON_BOUNDARIES = (
    r"\s+S/O",
    r"\s+SCO",
    r"\s+R/O",
    r"\s+C/O",
    r"\s+D/O",
    r"\s+W/O",
    r"\s+CONTACT\s+NUMBER",
    r"\s+Mobile\s+No",
    r"\s+Policy\s+No",
    r"\s+ADDRESS",
    r"\s+Address",
    r"\s+Period\s+of",
    r"\s+VEHICLE\s+NO",
    r"\s+Engine\s+No",
    r"\s+Chassis\s+No",
)


def compute_selection_context(raw_text: str, selection_start: int, selection_end: int) -> dict[str, str | int]:
    """Derive anchors and snippet from character offsets in normalized raw text."""
    text = raw_text or ""
    start = max(0, min(selection_start, len(text)))
    end = max(start, min(selection_end, len(text)))
    value = text[start:end].strip()
    if not value:
        raise ValueError("Selected text is empty")

    before = text[max(0, start - ANCHOR_MAX_LEN) : start]
    after = text[end : min(len(text), end + ANCHOR_MAX_LEN)]
    snippet_start = max(0, start - SNIPPET_RADIUS)
    snippet_end = min(len(text), end + SNIPPET_RADIUS)

    return {
        "value": value,
        "anchor_before": _normalize_anchor(before),
        "anchor_after": _normalize_anchor(after, tail=False),
        "snippet": text[snippet_start:snippet_end].strip(),
        "selection_start": start,
        "selection_end": end,
    }


def suggest_regex(field_name: str, value: str, anchor_before: str | None, anchor_after: str | None) -> str:
    """Build a label-anchored regex from a training example."""
    capture = _value_to_capture(field_name, value)
    before = _regex_prefix(anchor_before or "")
    after = _regex_suffix(anchor_after or "", field_name)
    return f"{before}{capture}{after}"


def extract_with_regex(regex: str, text: str) -> tuple[str | None, bool]:
    """Return first capture group value and whether pattern matched."""
    try:
        match = re.search(regex, text, re.IGNORECASE | re.DOTALL)
    except re.error:
        return None, False
    if not match:
        return None, False
    if match.lastindex and match.lastindex >= 1:
        return (match.group(1) or "").strip(), True
    return (match.group(0) or "").strip(), True


def extract_mobile_from_match(match: re.Match) -> str | None:
    """Build a 10-digit mobile from single or split capture groups."""
    if match.lastindex and match.lastindex >= 2:
        area = (match.group(1) or "").strip()
        local = (match.group(2) or "").strip()
        combined = re.sub(r"\D", "", f"{area}{local}")
        if len(combined) >= 10:
            return combined[-10:]
    value = (match.group(1) or "").strip()
    digits = re.sub(r"\D", "", value)
    return digits[-10:] if len(digits) >= 10 else (digits if len(digits) == 10 else None)


def extract_field_with_patterns(
    field_name: str,
    patterns: list[str],
    text: str,
) -> tuple[str | None, bool]:
    for regex in patterns:
        try:
            match = re.search(regex, text, re.IGNORECASE | re.DOTALL)
        except re.error:
            continue
        if not match:
            continue
        if field_name == "mobile_number":
            value = extract_mobile_from_match(match)
        elif match.lastindex and match.lastindex >= 1:
            value = (match.group(1) or "").strip()
        else:
            value = (match.group(0) or "").strip()
        if value:
            return value, True
    return None, False


def generic_field_patterns(field_name: str) -> list[str]:
    """Label-based regexes that work across layouts for the same insurer field."""
    common: dict[str, list[str]] = {
        "customer_name": [
            r"Name\s+of\s+the\s+Insured\s+"
            r"((?:MR|MS|MRS|MISS|SHRI|SMT)\.?\s+[A-Z]+(?:\s+[A-Z]+)?)"
            r"(?=\s+(?:OLD|S/O|SCO|R/O|ADDRESS|Address|CHAUTALA|\d{1,4}/))",
            r"Insured\s+((?:MR|MS|MRS|MISS|SHRI|SMT)\.?\s+[A-Z]+(?:\s+[A-Z]+)?)\s+(?:S/O|SCO|R/O|C/O)",
        ],
        "mobile_number": [
            r"Mobile\s+No\.-\s*(\d{10})",
            r"CONTACT\s+NUMBER:\s*(\d{10})",
            r"Telephone\s*\((\d{3,4})\)\s*(\d{6,8})",
        ],
        "vehicle_registration_number": [
            r"Registration\s+No\.\s*(?:Trailer\s+Cubic\s+Seating\s+including\s+Obsolete\s+Vehicle\s+)?"
            r"((?:HR|DL|UP|MH|RJ|GJ|PB|HP|TN|KA|WB|BR|MP|CG|UK|TS|AP)[\s-]+\d{1,2}[\s-]+[A-Z]{1,3}[\s-]+\d{1,4})",
            r"VEHICLE\s+NO\.:\s*((?:HR|DL|UP|MH|RJ|GJ|PB|HP|TN|KA|WB|BR|MP|CG|UK|TS|AP)[\s-]+\d{1,2}[\s-]+[A-Z]{1,3}[\s-]+\d{1,4})",
            r"(?:SOLO WITH|WITH)\s+((?:HR|DL|UP|MH|RJ|GJ|PB|HP|TN|KA|WB|BR|MP|CG|UK|TS|AP)[\s-]+\d{1,2}[\s-]+[A-Z]{1,3}[\s-]+\d{1,4})",
        ],
        "vehicle_make": [
            r"driver any\)\s+[A-Z0-9]{5,20}\s+[A-Z0-9]{5,20}\s+([A-Z][A-Z0-9\s./&]+?LTD\.?)\s*/",
        ],
        "vehicle_model": [
            r"\bNo\s+([A-Z][A-Z0-9\s]+?)(?:\s+(?:19|20)\d{2}|\s+\d{2}\s+\d|\s+Registration)",
        ],
        "body_type": [
            r"/\s*(SOLO WITH PILLION|SOLO WITH|SOLO|RIKSHAW|RICKSHAW)(?:\s+(?:HR|DL)|\s+No\s+)",
        ],
        "manufacturing_year": [
            r"driver any\).+?\b((?:19|20)\d{2})\b",
        ],
        "engine_number": [
            r"Engine\s+No\.?\s+([A-Z0-9]{5,20})(?:\s+Chassis|\s+Make|\s+MBL|\s+HA)",
            r"driver any\)\s+([A-Z0-9]{5,20})\s+[A-Z0-9]{5,20}\s+[A-Z]",
        ],
        "chassis_number": [
            r"Chassis\s+No\.?\s+([A-Z0-9]{5,20})(?:\s+Make|\s+Type|\s+Year|\s+HERO|\s+PIAGGIO)",
            r"driver any\)\s+[A-Z0-9]{5,20}\s+([A-Z0-9]{5,20})\s+[A-Z]",
        ],
        "policy_number": [
            r"Policy\s+No\.\s*([\dA-Z]+P[\d]+)",
            r"POLICY\s+NO\.:\s*([\dA-Z]+P[\d]+)",
        ],
        "policy_end_date": [
            r"To\s+Midnight\s+of\s+(\d{1,2}/\d{1,2}/\d{4})",
            r"Date\s+of\s+Expiry\s+of\s+the\s+Insurance\s+Midnight\s+on\s+(\d{1,2}/\d{1,2}/\d{4})",
        ],
        "policy_start_date": [
            r"From\s+00:00\s+Hrs\s+of\s+(\d{1,2}/\d{1,2}/\d{4})",
            r"Hrs\s+on\s+(\d{1,2}/\d{1,2}/\d{4})\s+Date\s+of\s+Expiry",
        ],
        "premium_amount": [
            r"Total\(Rounded\s+Off\):\s*([\d,]+(?:\.\d{2})?)",
            r"Premium:\s*([\d,]+\.\d{2})",
        ],
        "idv": [
            r"Insured's\s+Declared\s+Value\s+([\d,]+(?:\.\d{2})?)",
        ],
        "pincode": [
            r"Address\s+of\s+the\s+Insured\s+(\d{6})\s+[A-Z]",
        ],
        "state": [
            rf"Address\s+of\s+the\s+Insured\s+\d{{6}}\s+[A-Z][A-Za-z\s]+?\s+({INDIAN_STATES})\b",
        ],
        "city": [
            rf"Address\s+of\s+the\s+Insured\s+\d{{6}}\s+[A-Z][A-Za-z\s]+?\s+([A-Z][A-Za-z]+)\s+(?:{INDIAN_STATES})\b",
        ],
    }
    return common.get(field_name, [])


def _normalize_anchor(text: str, *, tail: bool = True) -> str:
    cleaned = re.sub(r"\s+", " ", text)
    if tail:
        cleaned = cleaned.strip()
        if len(cleaned) <= ANCHOR_MAX_LEN:
            return cleaned
        return cleaned[-ANCHOR_MAX_LEN:]
    if len(cleaned) <= ANCHOR_MAX_LEN:
        return cleaned
    return cleaned[:ANCHOR_MAX_LEN]


def _regex_prefix(anchor_before: str) -> str:
    if not anchor_before:
        return ""
    tail = anchor_before[-32:].strip()
    tail = re.sub(r"([\\.*+?^${}()|\[\]])", r"\\\1", tail)
    tail = tail.replace(r"\ ", r"\s+")
    if not tail.endswith(r"\s+"):
        tail += r"\s+"
    return tail


def _regex_suffix(anchor_after: str, field_name: str) -> str:
    if not anchor_after:
        return _default_boundary(field_name)

    head = anchor_after[:32].strip()
    for boundary in COMMON_BOUNDARIES:
        token = boundary.replace(r"\s+", " ").replace("\\", "").strip()
        if token and token.lower() in head.lower():
            return boundary

    escaped = re.sub(r"([\\.*+?^${}()|\[\]])", r"\\\1", head[:20])
    escaped = escaped.replace(r"\ ", r"\s+")
    return escaped


def _default_boundary(field_name: str) -> str:
    if field_name == "customer_name":
        return r"(?:\s+S/O|\s+SCO|\s+R/O|\s+CONTACT|\s+Mobile|\s+Policy|\s+Address|\s+ADDRESS)"
    if field_name in ("mobile_number", "pincode"):
        return r"(?:\s|\)|,|$)"
    if field_name in ("policy_start_date", "policy_end_date"):
        return r"(?:\s+Date|\s+To|\s+From|\s+Period|\s|$)"
    if field_name == "vehicle_registration_number":
        return r"(?:\s+Engine|\s+Chassis|\s+Make|\s+PERIOD|\s|$)"
    return r"(?:\s|$)"


def _value_to_capture(field_name: str, value: str) -> str:
    value = value.strip()
    if not value:
        return r"(.+?)"

    if field_name == "mobile_number" and re.fullmatch(r"\d{10}", value):
        return r"(\d{10})"

    if field_name == "pincode" and re.fullmatch(r"\d{6}", value):
        return r"(\d{6})"

    if field_name in ("policy_start_date", "policy_end_date") and re.fullmatch(r"\d{1,2}/\d{1,2}/\d{4}", value):
        return r"(\d{1,2}/\d{1,2}/\d{4})"

    if field_name == "premium_amount" or field_name == "idv":
        if re.fullmatch(r"[\d,]+(?:\.\d{2})?", value):
            return r"([\d,]+(?:\.\d{2})?)"

    if field_name == "vehicle_registration_number":
        return (
            r"((?:HR|DL|UP|MH|RJ|GJ|PB|HP|TN|KA|WB|BR|MP|CG|UK|TS|AP)"
            r"[\s-]+\d{1,2}[\s-]+[A-Z]{1,3}[\s-]+\d{1,4})"
        )

    if field_name == "customer_name":
        if re.match(r"^(MR|MS|MRS|MISS|SHRI|SMT)\.?\s", value, re.I):
            return r"((?:MR|MS|MRS|MISS|SHRI|SMT)\.?\s+[A-Z]+(?:\s+[A-Z]+)?)"
        return r"([A-Za-z][A-Za-z\s&\.]{2,80}?)"

    if field_name == "body_type":
        return r"([A-Za-z][A-Za-z0-9\s\-/]{2,40}?)"

    if field_name == "policy_number":
        if re.search(r"P\d", value):
            return r"([\dA-Z]+P[\d]+)"
        return r"([A-Z0-9][A-Z0-9\-/]{4,35})"

    if field_name in ("engine_number", "chassis_number"):
        return r"([A-Z0-9]{5,20})"

    if len(value) <= 40 and re.fullmatch(r"[A-Za-z0-9\s./,\-]+", value):
        words = value.split()
        if len(words) == 1:
            return rf"({re.escape(value)})"
        return r"([A-Za-z0-9][A-Za-z0-9\s./,\-]{1,80}?)"

    return rf"({re.escape(value)})"
