"""Shared citation formatting for lexicons, commentaries, and Bible references.

Supports SBL Handbook 2nd ed. (style="sbl") and Chicago Manual of Style 18th ed.
(style="cmos") formatting conventions.
"""


# ---------------------------------------------------------------------------
# Bible Book Abbreviations (SBLHS 2nd ed. §8.2)
# Maps common input forms (full name, common abbrev, lowercase) → SBL standard.
# ---------------------------------------------------------------------------

_SBL_BOOK = {
    # --- Old Testament / Hebrew Bible ---
    "genesis": "Gen",
    "gen": "Gen",
    "exodus": "Exod",
    "exod": "Exod",
    "leviticus": "Lev",
    "lev": "Lev",
    "numbers": "Num",
    "num": "Num",
    "deuteronomy": "Deut",
    "deut": "Deut",
    "joshua": "Josh",
    "josh": "Josh",
    "judges": "Judg",
    "judg": "Judg",
    "ruth": "Ruth",
    "1 samuel": "1 Sam",
    "1sam": "1 Sam",
    "i samuel": "1 Sam",
    "isam": "1 Sam",
    "2 samuel": "2 Sam",
    "2sam": "2 Sam",
    "ii samuel": "2 Sam",
    "iisam": "2 Sam",
    "1 kings": "1 Kgs",
    "1kgs": "1 Kgs",
    "i kings": "1 Kgs",
    "ikgs": "1 Kgs",
    "2 kings": "2 Kgs",
    "2kgs": "2 Kgs",
    "ii kings": "2 Kgs",
    "iikgs": "2 Kgs",
    "1 chronicles": "1 Chr",
    "1chr": "1 Chr",
    "i chronicles": "1 Chr",
    "ichr": "1 Chr",
    "2 chronicles": "2 Chr",
    "2chr": "2 Chr",
    "ii chronicles": "2 Chr",
    "iichr": "2 Chr",
    "ezra": "Ezra",
    "nehemiah": "Neh",
    "neh": "Neh",
    "esther": "Esth",
    "esth": "Esth",
    "job": "Job",
    "psalm": "Ps",
    "psalms": "Ps",
    "ps": "Ps",
    "psa": "Ps",
    "proverbs": "Prov",
    "prov": "Prov",
    "ecclesiastes": "Eccl",
    "eccl": "Eccl",
    "qoheleth": "Eccl",
    "song of solomon": "Song",
    "song of songs": "Song",
    "song": "Song",
    "canticles": "Song",
    "isaiah": "Isa",
    "isa": "Isa",
    "jeremiah": "Jer",
    "jer": "Jer",
    "lamentations": "Lam",
    "lam": "Lam",
    "ezekiel": "Ezek",
    "ezek": "Ezek",
    "daniel": "Dan",
    "dan": "Dan",
    "hosea": "Hos",
    "hos": "Hos",
    "joel": "Joel",
    "amos": "Amos",
    "obadiah": "Obad",
    "obad": "Obad",
    "jonah": "Jonah",
    "micah": "Mic",
    "mic": "Mic",
    "nahum": "Nah",
    "nah": "Nah",
    "habakkuk": "Hab",
    "hab": "Hab",
    "zephaniah": "Zeph",
    "zeph": "Zeph",
    "haggai": "Hag",
    "hag": "Hag",
    "zechariah": "Zech",
    "zech": "Zech",
    "malachi": "Mal",
    "mal": "Mal",
    # --- New Testament ---
    "matthew": "Matt",
    "matt": "Matt",
    "mark": "Mark",
    "luke": "Luke",
    "john": "John",
    "acts": "Acts",
    "romans": "Rom",
    "rom": "Rom",
    "1 corinthians": "1 Cor",
    "1cor": "1 Cor",
    "i corinthians": "1 Cor",
    "icor": "1 Cor",
    "2 corinthians": "2 Cor",
    "2cor": "2 Cor",
    "ii corinthians": "2 Cor",
    "iicor": "2 Cor",
    "galatians": "Gal",
    "gal": "Gal",
    "ephesians": "Eph",
    "eph": "Eph",
    "philippians": "Phil",
    "phil": "Phil",
    "colossians": "Col",
    "col": "Col",
    "1 thessalonians": "1 Thess",
    "1thess": "1 Thess",
    "i thessalonians": "1 Thess",
    "ithess": "1 Thess",
    "2 thessalonians": "2 Thess",
    "2thess": "2 Thess",
    "ii thessalonians": "2 Thess",
    "iithess": "2 Thess",
    "1 timothy": "1 Tim",
    "1tim": "1 Tim",
    "i timothy": "1 Tim",
    "itim": "1 Tim",
    "2 timothy": "2 Tim",
    "2tim": "2 Tim",
    "ii timothy": "2 Tim",
    "iitim": "2 Tim",
    "titus": "Titus",
    "philemon": "Phlm",
    "phlm": "Phlm",
    "hebrews": "Heb",
    "heb": "Heb",
    "james": "Jas",
    "jas": "Jas",
    "1 peter": "1 Pet",
    "1pet": "1 Pet",
    "i peter": "1 Pet",
    "ipet": "1 Pet",
    "2 peter": "2 Pet",
    "2pet": "2 Pet",
    "ii peter": "2 Pet",
    "iipet": "2 Pet",
    "1 john": "1 John",
    "1john": "1 John",
    "i john": "1 John",
    "ijohn": "1 John",
    "2 john": "2 John",
    "2john": "2 John",
    "ii john": "2 John",
    "iijohn": "2 John",
    "3 john": "3 John",
    "3john": "3 John",
    "iii john": "3 John",
    "iiijohn": "3 John",
    "jude": "Jude",
    "revelation": "Rev",
    "rev": "Rev",
    "apocalypse": "Rev",
}


def _normalize_book(name: str) -> str:
    """Normalize a book name to its SBL-standard abbreviation.

    Handles full names ("Genesis"), common abbreviations ("Gen"),
    and case-insensitive input. Returns the original input unchanged
    if no mapping is found.
    """
    if not name:
        return ""
    return _SBL_BOOK.get(name.strip().lower(), name)


# ---------------------------------------------------------------------------
# Journal Abbreviations (SBLHS 2nd ed. §8.4)
# At least 20+ common biblical-studies journals.
# ---------------------------------------------------------------------------

SBL_JOURNAL_ABBREVIATIONS: dict[str, str] = {
    # General biblical studies
    "JBL": "J. Biblic. Lit.",
    "CBQ": "Cath. Biblic. Q.",
    "JTS": "J. Theol. Stud.",
    "HTR": "Harv. Theol. Rev.",
    "JQR": "Jew. Q. Rev.",
    "ZAW": "Z. Alttestamentliche Wiss.",
    "ZNW": "Z. Neutestamentliche Wiss.",
    "Biblica": "Biblica",
    "RB": "Rev. Bib.",
    "Bib": "Biblica",
    "BTB": "Biblic. Theol. Bull.",
    "NTS": "New Testam. Stud.",
    "NovT": "Novum Testam.",
    "JSNT": "J. Study New Testam.",
    "JSOT": "J. Study Old Testam.",
    "VT": "Vetus Testam.",
    "TynBul": "Tyndale Bull.",
    "BBR": "Bull. Biblic. Res.",
    "JECS": "J. Early Christ. Stud.",
    "SJOT": "Scand. J. Old Testam.",
    "WTJ": "Wesleyan Theol. J.",
    "Neot": "Neotestamentica",
    "Semeia": "Semeia",
    "Proof": "Prooftexts",
    "DSD": "Dead Sea Discov.",
    "Hen": "Henoch",
    "USQR": "Union Semin. Q. Rev.",
    "CurBR": "Curr. Biblic. Res.",
    "JHebS": "J. Heb. Scr.",
    "TC": "TC: J. Biblic. Textual Crit.",
    "EstBib": "Estud. Bíblicos",
    "RBL": "Rev. Biblic. Lit.",
}


# ---------------------------------------------------------------------------
# Publisher Abbreviations (SBLHS 2nd ed. §6.1.4.1)
# At least 10 common academic publishers of biblical studies.
# ---------------------------------------------------------------------------

SBL_PUBLISHER_ABBREVIATIONS: dict[str, str] = {
    "Baker Academic": "Baker Academic",
    "Baker": "Baker Academic",
    "Brill": "Brill",
    "Cambridge University Press": "Cambridge University Press",
    "CUP": "Cambridge University Press",
    "de Gruyter": "de Gruyter",
    "Eerdmans": "Eerdmans",
    "Eisenbrauns": "Eisenbrauns",
    "Fortress": "Fortress",
    "Fortress Press": "Fortress",
    "Hendrickson": "Hendrickson",
    "Mohr Siebeck": "Mohr Siebeck",
    "Oxford University Press": "Oxford University Press",
    "OUP": "Oxford University Press",
    "SBL Press": "SBL Press",
    "Scholars Press": "Scholars Press",
    "Sheffield Academic Press": "Sheffield Academic",
    "Sheffield Academic": "Sheffield Academic",
    "T&T Clark": "T&T Clark",
    "Westminster John Knox": "Westminster John Knox",
    "WJK": "Westminster John Knox",
    "Zondervan": "Zondervan",
    "Yale University Press": "Yale University Press",
}


# ---------------------------------------------------------------------------
# USPS State Abbreviations (used by both SBL 2 and CMOS 18 for US locations)
# ---------------------------------------------------------------------------

_USPS_STATES: dict[str, str] = {
    "AL": "AL",
    "AK": "AK",
    "AZ": "AZ",
    "AR": "AR",
    "CA": "CA",
    "CO": "CO",
    "CT": "CT",
    "DE": "DE",
    "FL": "FL",
    "GA": "GA",
    "HI": "HI",
    "ID": "ID",
    "IL": "IL",
    "IN": "IN",
    "IA": "IA",
    "KS": "KS",
    "KY": "KY",
    "LA": "LA",
    "ME": "ME",
    "MD": "MD",
    "MA": "MA",
    "MI": "MI",
    "MN": "MN",
    "MS": "MS",
    "MO": "MO",
    "MT": "MT",
    "NE": "NE",
    "NV": "NV",
    "NH": "NH",
    "NJ": "NJ",
    "NM": "NM",
    "NY": "NY",
    "NC": "NC",
    "ND": "ND",
    "OH": "OH",
    "OK": "OK",
    "OR": "OR",
    "PA": "PA",
    "RI": "RI",
    "SC": "SC",
    "SD": "SD",
    "TN": "TN",
    "TX": "TX",
    "UT": "UT",
    "VT": "VT",
    "VA": "VA",
    "WA": "WA",
    "WV": "WV",
    "WI": "WI",
    "WY": "WY",
    # Traditional → USPS mapping for convenience
    "Ala.": "AL",
    "Alaska": "AK",
    "Ariz.": "AZ",
    "Ark.": "AR",
    "Calif.": "CA",
    "Colo.": "CO",
    "Conn.": "CT",
    "Del.": "DE",
    "Fla.": "FL",
    "Ga.": "GA",
    "Hawaii": "HI",
    "Idaho": "ID",
    "Ill.": "IL",
    "Ind.": "IN",
    "Iowa": "IA",
    "Kans.": "KS",
    "Ky.": "KY",
    "La.": "LA",
    "Maine": "ME",
    "Md.": "MD",
    "Mass.": "MA",
    "Mich.": "MI",
    "Minn.": "MN",
    "Miss.": "MS",
    "Mo.": "MO",
    "Mont.": "MT",
    "Nebr.": "NE",
    "Nev.": "NV",
    "N.H.": "NH",
    "N.J.": "NJ",
    "N.M.": "NM",
    "N.Y.": "NY",
    "N.C.": "NC",
    "N.D.": "ND",
    "Ohio": "OH",
    "Okla.": "OK",
    "Ore.": "OR",
    "Pa.": "PA",
    "R.I.": "RI",
    "S.C.": "SC",
    "S.D.": "SD",
    "Tenn.": "TN",
    "Tex.": "TX",
    "Utah": "UT",
    "Vt.": "VT",
    "Va.": "VA",
    "Wash.": "WA",
    "W.Va.": "WV",
    "Wis.": "WI",
    "Wyo.": "WY",
}


def _normalize_state(state: str) -> str:
    """Normalize a US state to its two-letter USPS postal code."""
    if not state:
        return ""
    clean = state.strip()
    return _USPS_STATES.get(clean, clean)


def _abbreviate_publisher(publisher: str, style: str) -> str:
    """Return the abbreviated publisher name for the given style.

    SBL: uses SBL_PUBLISHER_ABBREVIATIONS (e.g., "Sheffield Academic").
    CMOS: returns the full publisher name as-is.
    """
    if not publisher:
        return ""
    if style == "cmos":
        return publisher.strip()
    return SBL_PUBLISHER_ABBREVIATIONS.get(publisher.strip(), publisher.strip())


# ===========================================================================
# Existing functions (preserved)
# ===========================================================================


def lexicon_citation(headword: str, abbrev: str, title: str) -> str:
    """Inline citation for search index / single-line display.

    Format: "headword" — ABBREV (Title)
    """
    hw = headword if headword else "(unknown)"
    return f'"{hw}" \u2014 {abbrev} ({title})'


def lexicon_citation_markdown(headword: str, abbrev: str, title: str) -> str:
    """Markdown block citation for entry display.

    Format: ## headword
    *ABBREV — Title*
    """
    hw = headword if headword else "(unknown)"
    hw_escaped = hw.replace("#", "\\#")
    return f"## {hw_escaped}\n*{abbrev} \u2014 {title}*\n\n"


def commentary_citation(
    section_title: str, abbrev: str, title: str, reference: str
) -> str:
    """Inline citation for commentary search results.

    Format: "Section Title" — ABBREV (Title) — Reference
    Falls back to reference-only if no section title.
    """
    label = section_title if section_title else reference
    if section_title:
        return f'"{label}" \u2014 {abbrev} ({title}) \u2014 {reference}'
    else:
        return f"{reference} \u2014 {abbrev} ({title})"


# ===========================================================================
# New functions
# ===========================================================================


def format_bible_citation(
    book: str,
    chapter: int,
    verse: int | str,
    translation: str = "",
    style: str = "sbl",
) -> str:
    """Format a Bible citation.

    Args:
        book: Book name (full or abbreviated, e.g. "Genesis" or "Gen").
        chapter: Chapter number.
        verse: Verse number or range string (e.g. 1, "1-3", "1\u20133").
        translation: Optional translation abbreviation (e.g. "NRSV", "LXX").
        style: "sbl" or "cmos" (both use the same Bible abbreviations).

    Returns:
        SBL note:  "Gen 1:1 NRSV"
        SBL short: "Gen 1:1"  (when translation is empty)
    """
    abbr = _normalize_book(book)
    # Normalize verse to string; preserve en-dash if already present
    if isinstance(verse, int):
        vstr = str(verse)
    else:
        vstr = str(verse)

    base = f"{abbr} {chapter}:{vstr}"
    if translation:
        base += f" {translation}"
    return base


def format_commentary_citation_full(
    author: str = "",
    title: str = "",
    series: str = "",
    volume: str = "",
    publisher: str = "",
    location: str = "",
    year: str = "",
    page: str = "",
    style: str = "sbl",
) -> str:
    """Format a full commentary citation (footnote / bibliography style).

    SBL 2 example:
        Hooker, *Saint Mark*, BNTC 2 (Peabody, MA: Hendrickson, 1991), 223.

    CMOS 18 example:
        Morna D. Hooker, *The Gospel according to Saint Mark*,
        Black's New Testament Commentary 2 (Peabody, MA: Hendrickson, 1991), 223.

    Differences between SBL and CMOS:
        - SBL: last-name-only author, abbreviated series.
        - CMOS: full author name, full series name (no abbreviation).
        - Both use USPS two-letter state codes for US locations.
    """
    parts: list[str] = []

    # Author
    if author:
        parts.append(author)

    # Title (always italic markdown)
    if title:
        parts.append(f"*{title}*")

    # Series + Volume
    series_part = ""
    if style == "cmos":
        # CMOS: use full series name as provided (no abbreviation)
        if series:
            series_part = series
    else:
        # SBL: try to abbreviate series (use SBL_JOURNAL_ABBREVIATIONS as
        # a fallback for series — many commentary series share the same abbrevs)
        if series:
            series_part = SBL_JOURNAL_ABBREVIATIONS.get(series, series)
    if series_part and volume:
        series_part += f" {volume}"
    elif volume:
        series_part = volume
    elif series and not volume:
        series_part = series
    if series_part:
        parts.append(series_part)

    # Parenthetical: (Location: Publisher, Year)
    paren_parts: list[str] = []
    loc = location.strip()
    pub = _abbreviate_publisher(publisher, style)
    # Extract state if present in location (e.g., "Peabody, MA") and normalize
    if "," in loc:
        city, state = loc.split(",", 1)
        city = city.strip()
        state = _normalize_state(state.strip())
        loc = f"{city}, {state}"
    if loc and pub and year:
        paren_parts.append(f"{loc}: {pub}, {year}")
    elif loc and pub:
        paren_parts.append(f"{loc}: {pub}")
    elif pub and year:
        paren_parts.append(f"{pub}, {year}")
    elif pub:
        paren_parts.append(pub)

    # Build: Author, Title, SeriesVol (Location: Publisher, Year)
    result = ", ".join(parts)
    if paren_parts:
        result += f" ({'; '.join(paren_parts)})"

    # Page
    if page:
        result += f", {page}"

    return result + "."


def format_citation(
    book: str,
    ch: int,
    v: int | str,
    translation: str,
    module_type: str,
    module_name: str,
    style: str = "sbl",
) -> str:
    """Dispatches to the correct formatter based on module_type.

    Args:
        book: Book name or headword (depending on module_type).
        ch: Chapter number (unused for lexicon).
        v: Verse number (unused for lexicon).
        translation: Translation abbreviation (bible only).
        module_type: One of "bible", "commentary", "lexicon".
        module_name: Module abbreviation or label.
        style: "sbl" or "cmos".

    Returns:
        Formatted citation string.
    """
    mtype = module_type.strip().lower()
    if mtype == "bible":
        return format_bible_citation(book, ch, v, translation, style)
    elif mtype == "commentary":
        ref = format_bible_citation(book, ch, v, translation, style)
        return f"{module_name} ({ref})"
    elif mtype == "lexicon":
        return lexicon_citation(book, module_name, module_name)
    else:
        # Fallback: generic reference
        return f"{module_name} \u2014 {book} {ch}:{v}"
