"""Customer-safe weekly reporting source values."""

from __future__ import annotations

from dataclasses import dataclass, field
from datetime import date
from typing import Literal, Protocol

class WeeklyActionOutcome(Protocol):
    """Action result fields consumed by weekly reporting."""

    action_text: str
    outcome: Literal["met", "not_met", "pending", "insufficient"]


class WeeklyReviewInputError(ValueError):
    """Weekly review input cannot produce a customer-safe source."""


@dataclass(frozen=True, slots=True)
class WeeklySummary:
    """The owner-facing weekly operating summary for one customer stream."""

    starts_on: date
    ends_on: date
    eligible_weekdays: tuple[date, ...]
    checkin_dates: tuple[date, ...]
    checkin_rate_percent: float
    trends: tuple[str, ...]
    keep_behaviors: tuple[str, ...]
    change_behaviors: tuple[str, ...]
    next_decision: str
    average_weight_kg: float | None = None
    weight_change_kg: float | None = None
    weight_trend: str = "insufficient_data"

    @property
    def completion_rate_percent(self) -> float:
        return self.checkin_rate_percent

    @property
    def rate_percent(self) -> float:
        return self.checkin_rate_percent

    @property
    def checkin_rate(self) -> float:
        return self.checkin_rate_percent

    @property
    def keep(self) -> tuple[str, ...]:
        return self.keep_behaviors

    @property
    def change(self) -> tuple[str, ...]:
        return self.change_behaviors

    @property
    def next_action(self) -> str:
        return self.next_decision


@dataclass(frozen=True, slots=True)
class CustomerWeeklyReviewSource:
    """Typed customer-safe weekly review source without delivery authority."""

    source_kind: Literal["customer_weekly_review"] = field(
        init=False, default="customer_weekly_review"
    )
    customer_key: str
    period_start: date
    period_end: date
    last_focus: str
    observed_execution_or_change: str
    judgement: str
    next_action: str
    next_review_criterion: str
    next_review_date: date

    def render(self) -> str:
        return "\n".join(
            (
                f"지난 집중: {self.last_focus}",
                f"실행과 변화: {self.observed_execution_or_change}",
                f"이번 판단: {self.judgement}",
                f"다음 행동: {self.next_action}",
                f"다음 확인: {self.next_review_criterion} · {self.next_review_date.isoformat()}",
            )
        )

    def render_customer_body(self) -> str:
        return self.render()


@dataclass(frozen=True, slots=True)
class WeeklyReviewRequest:
    """Inputs required to derive one customer-safe weekly review source."""

    summary: WeeklySummary
    customer_key: str
    latest_action: WeeklyActionOutcome | None
    next_review_date: date


def build_customer_weekly_review_source(
    request: WeeklyReviewRequest,
) -> CustomerWeeklyReviewSource:
    """Derive a narrow weekly customer source from a summary and action outcome."""
    summary = request.summary
    customer_key = request.customer_key
    latest_action = request.latest_action
    next_review_date = request.next_review_date
    if not customer_key.strip() or type(next_review_date) is not date:
        raise WeeklyReviewInputError("weekly customer review input is invalid")
    if latest_action is None:
        focus = "이전 승인 행동이 아직 없습니다."
        observed = (
            "이번 주 기록으로 다음 변화를 확인합니다."
            if summary.weight_trend != "insufficient_data"
            else "이번 주 기록이 더 쌓이면 변화를 확인할 수 있습니다."
        )
    else:
        focus = latest_action.action_text
        observed = {
            "met": "지난 행동의 확인 기준이 기록에서 확인되었습니다.",
            "not_met": "지난 행동의 확인 기준이 아직 충족되지 않았습니다.",
            "pending": "지난 행동의 확인 시점 전입니다.",
            "insufficient": "지난 행동을 판단할 기록이 충분하지 않습니다.",
        }[latest_action.outcome]
    judgement = (
        "이번 주 계획을 유지합니다."
        if summary.next_decision.startswith("유지:")
        else "이번 주에는 한 가지 행동을 바꿔 확인합니다."
    )
    return CustomerWeeklyReviewSource(
        customer_key.strip(),
        summary.starts_on,
        summary.ends_on,
        focus,
        observed,
        judgement,
        summary.next_decision,
        "다음 주 기록의 변화",
        next_review_date,
    )
