from __future__ import annotations

from dataclasses import dataclass
from typing import Any

from .util import utc_now


@dataclass(frozen=True)
class ReconciliationResult:
    linked: list[dict[str, str]]
    unresolved_left: list[str]
    unresolved_right: list[str]
    warnings: list[str]

    def as_dict(self) -> dict[str, Any]:
        return {
            "linked": self.linked,
            "unresolved_left": self.unresolved_left,
            "unresolved_right": self.unresolved_right,
            "warnings": self.warnings,
        }


def _compatible(left: dict[str, Any], right: dict[str, Any]) -> bool:
    if left["type"] == right["type"]:
        return True
    prose = {"text", "diary_entry"}
    return left["type"] in prose and right["type"] in prose


def reconcile_adjacent_pages(
    left: dict[str, Any], right: dict[str, Any], *, created_at: str | None = None
) -> ReconciliationResult:
    left_number = left["source"]["pdf_page"]
    right_number = right["source"]["pdf_page"]
    if right_number != left_number + 1:
        raise ValueError("Reconciliation requires adjacent pages in ascending order")

    for block in left["blocks"]:
        if (block["continuation"].get("next") or {}).get("page_id") == right["page_id"]:
            block["continuation"]["next"] = None
    for block in right["blocks"]:
        if (block["continuation"].get("previous") or {}).get("page_id") == left["page_id"]:
            block["continuation"]["previous"] = None

    outgoing = [
        block
        for block in left["blocks"]
        if block["continuation"]["to_next"] in {"yes", "uncertain"}
    ]
    incoming = [
        block
        for block in right["blocks"]
        if block["continuation"]["from_previous"] in {"yes", "uncertain"}
    ]
    pairs: list[tuple[dict[str, Any], dict[str, Any]]] = []
    remaining_right = incoming.copy()
    for left_block in outgoing:
        compatible = [block for block in remaining_right if _compatible(left_block, block)]
        if len(compatible) == 1:
            right_block = compatible[0]
            pairs.append((left_block, right_block))
            remaining_right.remove(right_block)
    if not pairs and len(outgoing) == len(incoming):
        pairs = list(zip(outgoing, incoming, strict=True))
        remaining_right = []

    linked: list[dict[str, str]] = []
    for left_block, right_block in pairs:
        left_block["continuation"]["next"] = {
            "page_id": right["page_id"],
            "block_id": right_block["id"],
        }
        right_block["continuation"]["previous"] = {
            "page_id": left["page_id"],
            "block_id": left_block["id"],
        }
        linked.append({"left": left_block["id"], "right": right_block["id"]})

    paired_left = {left_block["id"] for left_block, _ in pairs}
    paired_right = {right_block["id"] for _, right_block in pairs}
    unresolved_left = [block["id"] for block in outgoing if block["id"] not in paired_left]
    unresolved_right = [block["id"] for block in incoming if block["id"] not in paired_right]
    warnings: list[str] = []
    if unresolved_left or unresolved_right:
        warnings.append(
            "Continuation declarations could not be paired unambiguously; links remain null"
        )

    if linked:
        timestamp = created_at or utc_now()
        artifact = f"{left['page_id']}--{right['page_id']}"
        for page in (left, right):
            if not any(
                step["kind"] == "reconciliation" and step["artifact"] == artifact
                for step in page["provenance"]["steps"]
            ):
                page["provenance"]["steps"].append(
                    {
                        "kind": "reconciliation",
                        "actor": "crowley-diary deterministic reconciler",
                        "artifact": artifact,
                        "created_at": timestamp,
                    }
                )
    return ReconciliationResult(linked, unresolved_left, unresolved_right, warnings)
