"""
AIIR internal — GitHub Actions integration helpers.

Copyright 2025-2026 Invariant Systems, Inc.
SPDX-License-Identifier: Apache-2.0
"""

from __future__ import annotations

import os
import uuid
from typing import Any, Dict, List

from aiir._core import (
    CLI_VERSION,
    MAX_SUMMARY_SIZE,
    _sanitize_md,
)


def set_github_output(key: str, value: str) -> None:
    """Set a GitHub Actions output variable."""
    # Reject newlines, '=', control chars, and '<<' in keys
    if not key or any(c in key for c in '\n\r=') or any(ord(c) < 0x20 for c in key) or '<<' in key:
        raise ValueError(
            f"Invalid GitHub output key (contains forbidden characters): {key!r}"
        )
    # Cap value length — GitHub Actions limits GITHUB_OUTPUT to 8 MB total.
    _MAX_OUTPUT_VALUE_SIZE = 4 * 1024 * 1024  # 4 MB
    if len(value.encode("utf-8", errors="replace")) > _MAX_OUTPUT_VALUE_SIZE:
        raise ValueError(
            f"GitHub output value too large ({len(value)} chars, max {_MAX_OUTPUT_VALUE_SIZE})"
        )
    output_file = os.environ.get("GITHUB_OUTPUT")
    if output_file:
        with open(output_file, "a", encoding="utf-8") as f:
            # Use heredoc for values with control chars or '<<'
            needs_heredoc = (
                "\n" in value or "\r" in value
                or any(ord(c) < 0x20 for c in value)
                or "<<" in value
            )
            if needs_heredoc:
                delimiter = f"ghadelimiter_{uuid.uuid4()}"
                f.write(f"{key}<<{delimiter}\n{value}\n{delimiter}\n")
            else:
                f.write(f"{key}={value}\n")


def set_github_summary(markdown: str) -> None:
    """Append to the GitHub Actions step summary."""
    # Byte-aware truncation for multi-byte safety.
    md_bytes = markdown.encode("utf-8", errors="replace")
    if len(md_bytes) > MAX_SUMMARY_SIZE:
        _SUFFIX = b"\n\n*(truncated \xe2\x80\x94 exceeded 1 MB limit)*"
        budget = MAX_SUMMARY_SIZE - len(_SUFFIX)
        markdown = md_bytes[:budget].decode("utf-8", errors="ignore") + _SUFFIX.decode("utf-8")
    summary_file = os.environ.get("GITHUB_STEP_SUMMARY")
    if summary_file:
        with open(summary_file, "a", encoding="utf-8", newline="") as f:
            f.write(markdown + "\n")


def format_github_summary(receipts: List[Dict[str, Any]]) -> str:
    """Format receipts as a GitHub Actions step summary (Markdown table)."""
    # Guard ai_attestation type in header count (same as loop body).
    ai_count = sum(
        1
        for r in receipts
        if isinstance(r.get("ai_attestation"), dict)
        and r["ai_attestation"].get("is_ai_authored")
    )
    total = len(receipts)

    lines = [
        "## 🔐 AIIR Receipt Summary",
        "",
        f"**{total}** commit{'s' if total != 1 else ''} receipted"
        + (f" · **{ai_count}** AI-authored" if ai_count else ""),
        "",
        "| Commit | Subject | AI | Receipt ID |",
        "|--------|---------|-----|-----------|",
    ]

    for r in receipts:
        commit = r.get("commit", {})
        ai = r.get("ai_attestation", {})
        # Guard nested field types (same pattern as format_receipt_pretty).
        if not isinstance(commit, dict):
            commit = {}
        if not isinstance(ai, dict):
            ai = {}
        # Sanitize ALL user-controlled fields in the markdown table,
        # not just subject. A tampered receipt could have backticks/pipes in
        # sha or receipt_id, breaking the table or injecting content.
        sha_short = _sanitize_md(commit.get("sha", "")[:8])
        # Sanitize subject to prevent markdown/HTML injection
        subject = _sanitize_md(commit.get("subject", "")[:50])
        ai_flag = "🤖" if ai.get("is_ai_authored") else ""
        rid = _sanitize_md(r.get("receipt_id", "")[:16]) + "…"
        # Use double-backtick delimiters for code spans. In GFM,
        # backslash-escaped backticks inside single-backtick spans are literal
        # (not escapes), so a backtick in sha_short would break the code span.
        # Double-backtick delimiters with spaces handle embedded backticks safely.
        lines.append(f"| `` {sha_short} `` | {subject} | {ai_flag} | `` {rid} `` |")

    lines.extend(
        [
            "",
            f"*Generated by aiir v{CLI_VERSION}*",
        ]
    )
    return "\n".join(lines)
