"""source_health 테이블 ORM (A-5, plan crawl-24x7-1)

news_sources 와 1:1. 소스별 fetch 성공/실패 기록 + circuit breaker 상태.
silent skip 누적 방지의 가시성 기반 — A-8 헬스 패널이 읽는다.
"""

from datetime import datetime

from sqlalchemy import BigInteger, Boolean, DateTime, ForeignKey, Integer, String, Text
from sqlalchemy.orm import Mapped, mapped_column

from core.database import Base


class SourceHealth(Base):
    __tablename__ = "source_health"

    id: Mapped[int] = mapped_column(primary_key=True)
    source_id: Mapped[int] = mapped_column(
        Integer, ForeignKey("news_sources.id", ondelete="CASCADE"), nullable=False
    )
    consecutive_failures: Mapped[int] = mapped_column(Integer, default=0)
    total_fetches: Mapped[int] = mapped_column(BigInteger, default=0)
    total_failures: Mapped[int] = mapped_column(BigInteger, default=0)
    last_success_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
    last_error: Mapped[str | None] = mapped_column(Text)
    last_error_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
    last_fetch_items: Mapped[int | None] = mapped_column(Integer)
    # 200 인데 entries 0 인 연속 fetch 횟수 (304/해시동일은 미집계 — 피드 부패 신호 전용)
    empty_streak: Mapped[int] = mapped_column(Integer, default=0)
    # closed(정상) / open(연속 실패 → 지수 backoff) / disabled(임계 초과, 수동 복구 대상)
    circuit_state: Mapped[str] = mapped_column(String(10), default="closed")
    circuit_opened_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
    updated_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), default=datetime.now
    )

    # ── B-3 구독 세션 상태 계약 — migration 325 ──
    # 쓰기 1종 플래그: A-8 버튼이 기록만, 어댑터가 소비(수동 half-open).
    # 소비 위치 = open-스킵 분기보다 앞 (r5 함정 고정 — 데드 버튼 방지).
    relogin_requested: Mapped[bool] = mapped_column(Boolean, default=False)
    # 내용 기반 probe 결과 (시간 기반 만료 판정 금지 — 페이월 안내문 silent corruption 차단)
    last_probe_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
    last_probe_ok: Mapped[bool | None] = mapped_column(Boolean)
