"""What the overlap matcher decides about each table on a page.

Fusion matches the Set-of-Mark/Camelot tables against docling's tables and
pictures one page at a time, by bounding-box overlap. A `RegionMatch` records
one outcome: which SoM tables and which docling items overlapped, and what that
cardinality means. The graft and annotation passes act on these records; they
make no geometric decisions of their own.

The seven kinds correspond one-to-one to the fusion matrix:

- `replace` — one SoM table over one docling table, both agree. The Camelot body
  replaces the docling body; the SoM table is unchanged.
- `som_merged` — more docling tables than SoM tables in the region: SoM fused
  stacked tables. `split_table` splits the SoM table; the splits graft in.
- `docling_undercount` — more SoM tables than docling tables: docling dropped or
  merged a complex table. The docling table(s) in the region are deleted and the
  Camelot tables are inserted in their place, in vertical order.
- `image_table` — a docling table with no text layer under it (read by OCR,
  Camelot empty): a real table rendered as an image. Docling's OCR'd table is
  kept; the SoM table is annotated as an image table.
- `chart` — a SoM table over a docling picture and no docling table: SoM mistook
  an image for a table. Nothing grafts; the matched picture records that
  Set-of-Mark read it, and the SoM table is annotated as a chart.
- `docling_miss` — a SoM table over nothing in docling: docling missed it. The
  Camelot table is inserted into the document.
- `som_miss` — a docling table no SoM table matched: SoM missed a table docling
  found. Reported as an error; nothing is synthesized from docling.
"""

from __future__ import annotations

from dataclasses import dataclass, field
from typing import TYPE_CHECKING, List, Literal

from pydantic import BaseModel, Field

if TYPE_CHECKING:
    from docling_core.types.doc.document import DoclingDocument

    from quber.core.extractors.base import CellFlag, ExtractedTable
    from quber.core.parsers import ParseResult

MatchKind = Literal[
    "replace",
    "som_merged",
    "docling_undercount",
    "image_table",
    "chart",
    "docling_miss",
    "som_miss",
]


class RegionMatch(BaseModel):
    """One reconciliation outcome for a region on a page.

    `som_indices` index into the Set-of-Mark `ExtractedTable` list handed to the
    matcher. `docling_table_refs` / `docling_picture_refs` are docling
    `self_ref` strings. The combination of which lists are populated and `kind`
    drives the graft and annotation passes.

    After grafting, `docling_table_refs` reference the *unified* document:
    the graft deletes and inserts tables, which renumbers every later table's
    `self_ref`, so it rewrites each match's refs to the grafted tables' final
    positions. A consumer resolving these refs against the unified document
    (the report always ships beside it) lands on the table the match is about.
    """

    page: int
    kind: MatchKind
    som_indices: List[int] = Field(default_factory=list)
    docling_table_refs: List[str] = Field(default_factory=list)
    docling_picture_refs: List[str] = Field(default_factory=list)
    picture_classes: List[str] = Field(
        default_factory=list,
        description="Predicted classes of the matched docling pictures (for chart kind)",
    )
    overlap: float = Field(
        default=0.0, description="Representative box-overlap coverage for the region (0..1)"
    )
    detail: str = Field(default="", description="Human-readable note on the match")


@dataclass
class FusionResult:
    """The two corrected outputs of one fusion.

    `document` is the unified `DoclingDocument`: docling's spine with the Camelot
    table bodies grafted in. `tables` is the corrected Set-of-Mark/Camelot output:
    merged regions split, charts and image tables annotated via `ExtractedTable.kind`.
    `matches` is the final per-region classification (after any split), and
    `errors` surfaces the regions that could not be fused: a table docling found
    that SoM missed, a merge the split pass declined, or a SoM table in a
    `docling_miss` or `docling_undercount` region left out of the unified
    document because Camelot read no body.
    """

    document: "DoclingDocument"
    tables: List["ExtractedTable"]
    matches: List[RegionMatch] = field(default_factory=list)
    errors: List[str] = field(default_factory=list)
    # Headings the review demoted to page decoration, one flag per heading —
    # written into the run's flags artifact alongside the cell flags.
    heading_flags: List["CellFlag"] = field(default_factory=list)
    # The document extraction the fusion consumed. Carried so a caller that ran
    # the parse inside fuse can persist the parse-side artifacts — above all
    # the positioned page cells, which the figure stage's value reconciliation
    # requires beside the parse it is handed.
    parse: "ParseResult | None" = None
