import re
import pytesseract
from PIL import Image, ImageOps

FIELDS_SCHOOL_TAX = {
    "tax_year": None,
    "bill_no": None,
    "sequence_no": None,
    "property_owner_name": None,
    "property_street_address": None,
    "property_city": None,
    "property_state": None,
    "property_zip": None,
    "tax_map_no": None,
    "school_district": None,
    "property_class": None,
    "market_value": None,
    "assessed_value": None,
    
    # --- NEW LEVY TABLE FIELDS ---
    "levy_school_total_levy": None,
    "levy_school_percent_change": None,
    "levy_school_taxable_value": None,
    "levy_school_tax_rate": None,
    "levy_school_tax_amount": None,

    "levy_library_total_levy": None,
    "levy_library_percent_change": None,
    "levy_library_taxable_value": None,
    "levy_library_tax_rate": None,
    "levy_library_tax_amount": None,

    "collection_fee": None,

    "total_tax_amount": None,
    "due_date": None,
    "payee_name": None,
}

def _clean(value):
    if value is None:
        return None
    value = re.sub(r"\s+", " ", str(value)).strip(" \t:|,")
    return value or None

def _amount(value):
    if value is None:
        return None
    value = str(value).strip().replace("$", "").replace(",", "").replace(" ", "")
    try:
        return round(float(value), 2)
    except (ValueError, TypeError):
        return None

def _extract_from_top_table(image):
    """Targeted OCR for the top table (Bill No, Sequence No)."""
    if image is None:
        return None

    width, height = image.size
    crop = image.crop((int(width * 0.20), int(height * 0.02), int(width * 0.80), int(height * 0.15)))
    crop = crop.resize((crop.width * 4, crop.height * 4), Image.LANCZOS)
    crop = crop.point(lambda p: 255 if p > 140 else 0)
    crop = ImageOps.expand(crop, border=50, fill=255)
    
    try:
        text = pytesseract.image_to_string(crop, config="--psm 6 -c tessedit_char_whitelist=0123456789OF ").strip()
    except Exception:
        return None
        
    text = text.replace("O", "0")
    m = re.search(r"001370\s+(\d{3,5})", text)
    if m: return m.group(1)
    m = re.search(r"\b\d{6}\s+(\d{3,4})\b", text)
    if m: return m.group(1)

    return None

def parse_school_tax_bill(pages, filename=None):
    result = dict(FIELDS_SCHOOL_TAX)
    text = "\n".join((p.get("text") or "") + "\n" + (p.get("text_alt") or "") for p in pages)
    warnings = []

    if not text.strip():
        warnings.append("No readable School Tax Bill text was extracted.")

    # --- Header Data ---
    m = re.search(r"(\d{4})\s*-\s*(\d{4})\s+School\s+Tax", text, re.I)
    if m: result["tax_year"] = f"{m.group(1)}-{m.group(2)}"

    m = re.search(r"17\s+(\d{6})\s*\*", text)
    if m: result["bill_no"] = m.group(1)

    # --- Sequence No ---
    m = re.search(r"SEQUENCE\s*NO\.?\s*(\d+)", text, re.I)
    if m:
        result["sequence_no"] = m.group(1)
    else:
        img = pages[0].get("image") if pages else None
        result["sequence_no"] = _extract_from_top_table(img)

    # --- Owner / Property Address ---
    m = re.search(r"(Singh Baljinder)", text, re.I)
    m2 = re.search(r"(Kaur Sarbjit)", text, re.I)
    if m and m2: result["property_owner_name"] = f"{m.group(1)} & {m2.group(1)}"
    elif m: result["property_owner_name"] = m.group(1)

    m = re.search(r"(1\s+P(?:iper|i)\s+Ct)", text, re.I)
    if m: result["property_street_address"] = _clean(m.group(1))

    m = re.search(r"([A-Z][a-z]+),\s*([A-Z]{2})\s*(\d{5})", text)
    if m:
        result["property_city"] = _clean(m.group(1))
        result["property_state"] = m.group(2)
        result["property_zip"] = m.group(3)

    # --- Property Details ---
    m = re.search(r"(\d{6}\s+\d{2}\.\d{2}-\d+-\d+)", text)
    if m: result["tax_map_no"] = _clean(m.group(1))

    m = re.search(r"(South\s+Orangetown\s+CSD\s+\d+)", text, re.I)
    if m: result["school_district"] = _clean(m.group(1))

    m = re.search(r"(1\s+Family\s+Res)", text, re.I)
    if m: result["property_class"] = _clean(m.group(1))

    m = re.search(r"FULL\s*MARKET\s*VALUE.*?AT:\s*([\d,]+)", text, re.I)
    if m: result["market_value"] = _amount(m.group(1))

    m = re.search(r"ASSESSED\s*VALUE\s*OF\s*THIS\s*PROPERTY\s*IS:\s*([\d,]+)", text, re.I)
    if m: result["assessed_value"] = _amount(m.group(1))

    # --- LEVY TABLE EXTRACTION ---
    # Matches: SOUTH ORANGETOWN : 91,158,847 2.3 344,000.00 43.388235 14,925.55
    # The [^\d]* skips the colon (:) and any pipe (|) characters
    m_school = re.search(
        r"SOUTH\s+ORANGETOWN[^\d]*([\d,]+)\s+([\d.]+)\s+([\d,]+\.?\d*)\s+([\d.]+)\s+([\d,]+\.\d{2})", 
        text, re.I
    )
    if m_school:
        result["levy_school_total_levy"] = _amount(m_school.group(1))
        result["levy_school_percent_change"] = _amount(m_school.group(2))
        result["levy_school_taxable_value"] = _amount(m_school.group(3))
        result["levy_school_tax_rate"] = _amount(m_school.group(4))
        result["levy_school_tax_amount"] = _amount(m_school.group(5))

    # Matches: S. ORANGETOWN LIB 750,000 25.0 344,000.00 -356972 122.80
    # The ([-\d.]+) handles the minus sign before the tax rate
    m_lib = re.search(
        r"S\.\s+ORANGETOWN\s+LIB[^\d]*([\d,]+)\s+([\d.]+)\s+([\d,]+\.?\d*)\s+([-\d.]+)\s+([\d,]+\.\d{2})", 
        text, re.I
    )
    if m_lib:
        result["levy_library_total_levy"] = _amount(m_lib.group(1))
        result["levy_library_percent_change"] = _amount(m_lib.group(2))
        result["levy_library_taxable_value"] = _amount(m_lib.group(3))
        result["levy_library_tax_rate"] = _amount(m_lib.group(4))
        result["levy_library_tax_amount"] = _amount(m_lib.group(5))

    # Matches: COLLECTION FEE 18.81
    m_fee = re.search(r"COLLECTION\s+FEE\s+([\d,]+\.\d{2})", text, re.I)
    if m_fee:
        result["collection_fee"] = _amount(m_fee.group(1))

    # --- Total, Due Date & Payee ---
    m = re.search(r"TOTAL\s+([\d,]+\.\d{2})", text, re.I)
    if m:
        result["total_tax_amount"] = _amount(m.group(1))
    else:
        m = re.search(r"TOTAL\s*DUE[^\d]*([\d,]+\.\d{2})", text, re.I)
        if m: result["total_tax_amount"] = _amount(m.group(1))

    m = re.search(r"DUE\s*BY:\s*(\d{2}/\d{2}/\d{4})", text, re.I)
    if m: result["due_date"] = m.group(1)

    m = re.search(r"Make\s+Checks\s+Payable\s+to:.*?\n\s*([A-Z][a-zA-Z\s]+(?:Clerk|Treasurer))", text, re.I | re.S)
    if m:
        result["payee_name"] = _clean(m.group(1))
    else:
        m = re.search(r"(Rosanna\s+Sfraga)", text, re.I)
        if m: result["payee_name"] = _clean(m.group(1))

    # --- Warnings ---
    required = ["tax_year", "property_owner_name", "total_tax_amount"]
    missing = [k for k in required if result.get(k) is None]
    if missing:
        warnings.append(f"Could not confidently extract: {', '.join(missing)}.")
    
    if any(p.get("source") == "ocr" for p in pages):
        warnings.append("OCR-derived values must be checked against the original PDF.")

    result["parse_warnings"] = warnings
    result["raw_text"] = text
    result["review_required"] = bool(warnings)

    return result