from __future__ import annotations

from dataclasses import dataclass

from .document_models import DocumentSpec, ParagraphBlock, TableBlock


@dataclass(frozen=True)
class DocumentQualityIssue:
    category: str
    message: str
    repair_action: str
    block_id: str | None = None


class DocumentQualityFailure(ValueError):
    def __init__(self, issues: list[DocumentQualityIssue]):
        self.issues = issues
        super().__init__("Document quality checks failed:\n- " + "\n- ".join(issue.message for issue in issues))


def collect_document_quality_issues(spec: DocumentSpec) -> list[DocumentQualityIssue]:
    issues: list[DocumentQualityIssue] = []
    previous_heading_level = 0
    consecutive_tables = 0
    current_h1_id: str | None = None
    current_h1_has_content = True

    for block in spec.blocks:
        if block.type == "heading":
            if block.level > previous_heading_level + 1:
                issues.append(
                    DocumentQualityIssue(
                        category="heading_level_jump",
                        message=f"Heading '{block.text}' jumps from level {previous_heading_level} to {block.level}",
                        repair_action="Use sequential heading levels without skipping a level.",
                        block_id=block.id,
                    )
                )
            previous_heading_level = block.level
            if block.level == 1:
                if current_h1_id is not None and not current_h1_has_content:
                    issues.append(_empty_section_issue(current_h1_id))
                current_h1_id = block.id
                current_h1_has_content = False
        elif block.type != "page_break" and current_h1_id is not None:
            current_h1_has_content = True

        if block.type == "paragraph":
            issues.extend(_check_paragraph(block))
        elif block.type == "table":
            consecutive_tables += 1
            issues.extend(_check_table(block))
            if consecutive_tables >= 3:
                issues.append(
                    DocumentQualityIssue(
                        category="table_overuse",
                        message="The document contains three or more consecutive tables",
                        repair_action="Convert at least one adjacent table to prose, a list, or a callout.",
                        block_id=block.id,
                    )
                )
        else:
            consecutive_tables = 0

    if current_h1_id is not None and not current_h1_has_content:
        issues.append(_empty_section_issue(current_h1_id))

    return issues


def _empty_section_issue(block_id: str) -> DocumentQualityIssue:
    return DocumentQualityIssue(
        category="empty_section",
        message=f"Level-1 section '{block_id}' has no substantive content",
        repair_action="Add prose, a list, a table, a callout, a quote, or an available image before the next level-1 section.",
        block_id=block_id,
    )


def _check_paragraph(block: ParagraphBlock) -> list[DocumentQualityIssue]:
    text = block.text if block.text is not None else "".join(run.text for run in block.runs)
    if len(text) <= 1800:
        return []
    return [
        DocumentQualityIssue(
            category="paragraph_too_dense",
            message=f"Paragraph '{block.id}' is too long for comfortable document reading",
            repair_action="Split the paragraph into shorter paragraphs or a suitable list.",
            block_id=block.id,
        )
    ]


def _check_table(block: TableBlock) -> list[DocumentQualityIssue]:
    populated_cells = [cell.strip() for row in block.rows for cell in row if cell.strip()]
    if not populated_cells:
        return []
    prose_cells = [cell for cell in populated_cells if len(cell) > 300]
    if len(prose_cells) * 2 <= len(populated_cells):
        return []
    return [
        DocumentQualityIssue(
            category="table_contains_prose",
            message=f"Table '{block.id}' packages paragraph-length prose into most cells",
            repair_action="Use prose sections, lists, or callouts instead of a table for narrative content.",
            block_id=block.id,
        )
    ]


def format_quality_gate_findings(issues: list[DocumentQualityIssue]) -> str:
    lines: list[str] = []
    for issue in issues:
        lines.append(f"- category: {issue.category}")
        if issue.block_id:
            lines.append(f"  block_id: {issue.block_id}")
        lines.append(f"  issue: {issue.message}")
        lines.append(f"  repair: {issue.repair_action}")
    return "\n".join(lines)
