"""ISO-week-gated personal pattern observations from consolidation → proactive inbox."""

from __future__ import annotations

import hashlib
import json
import logging
import os
from datetime import datetime, timezone
from typing import Any, Callable

from utils.path_safety import safe_resolve_path

_log = logging.getLogger(__name__)

_STATE_REL = "memory/.personal_pattern_observation_state.json"


def _iso_week_id(dt: datetime) -> str:
    iso = dt.isocalendar()
    return f"{iso[0]:04d}-W{iso[1]:02d}"


def _state_path(workspace: str) -> str | None:
    abs_path, err = safe_resolve_path(
        workspace, _STATE_REL, allow_write=True, create_parents=True
    )
    if err:
        return None
    return abs_path


def _load_state(workspace: str) -> dict[str, Any]:
    path = _state_path(workspace)
    if not path or not os.path.isfile(path):
        return {}
    try:
        with open(path, encoding="utf-8") as f:
            data = json.load(f)
        return data if isinstance(data, dict) else {}
    except Exception:
        return {}


def _save_state(workspace: str, state: dict[str, Any]) -> bool:
    path = _state_path(workspace)
    if not path:
        return False
    try:
        from persistence import secure_atomic_write

        secure_atomic_write(path, json.dumps(state, indent=2))
        os.chmod(path, 0o600)
        return True
    except Exception:
        _log.warning("personal pattern observation state write failed", exc_info=True)
        return False


def _pattern_fingerprint(bullets: list[str]) -> str:
    joined = "|".join(sorted(b.strip().lower() for b in bullets if b.strip()))
    return hashlib.sha256(joined.encode("utf-8")).hexdigest()[:16]


def should_emit_personal_pattern_observation(
    workspace: str,
    bullets: list[str],
    *,
    now: datetime | None = None,
) -> bool:
    """True when ISO week advanced since last emit and bullets are non-empty."""
    cleaned = [b.strip() for b in bullets if len(b.strip()) >= 14]
    if not cleaned:
        return False
    ref = now or datetime.now(timezone.utc)
    week_id = _iso_week_id(ref)
    state = _load_state(workspace)
    if state.get("last_week_id") == week_id:
        return False
    return True


def maybe_emit_consolidation_pattern_observation(
    workspace: str,
    bullets: list[str],
    *,
    on_log: Callable[[str, str], None] | None = None,
    now: datetime | None = None,
) -> bool:
    """Emit one inbox observation for recurring patterns (≤1 per ISO week)."""
    cleaned = [b.strip() for b in bullets if len(b.strip()) >= 14]
    if not cleaned:
        return False
    if not should_emit_personal_pattern_observation(workspace, cleaned, now=now):
        if on_log:
            on_log("INFO", "[consolidation] personal pattern observation skipped (ISO-week gate)")
        return False

    ref = now or datetime.now(timezone.utc)
    week_id = _iso_week_id(ref)
    dedupe_key = f"personal_pattern:{week_id}"
    from utils.noticing_plaintext import sanitize_noticing_plaintext

    noticed = sanitize_noticing_plaintext("; ".join(cleaned[:3]), max_len=600)
    proposed_work = "discuss these patterns and whether you'd like me to act on any"

    try:
        from persistence import PROACTIVE_QUEUE_PATH
        from utils.proactive_queue import append_observation_queue

        ok = append_observation_queue(
            os.path.expanduser(PROACTIVE_QUEUE_PATH),
            noticed=noticed,
            proposed_work=proposed_work,
            source="consolidation",
            content=(
                "I've noticed a few recurring patterns in your recent work and conversations. "
                "Would you like to discuss them or have me follow up on any?"
            ),
            suggested_tier="in_thread",
            dedupe_key=dedupe_key,
        )
    except Exception:
        if on_log:
            on_log("DEBUG", "[consolidation] personal pattern observation emit failed")
        return False

    if not ok:
        return False

    state_payload = {
        "last_week_id": week_id,
        "last_hash": _pattern_fingerprint(cleaned),
        "last_emit_ts": ref.isoformat(),
    }
    if not _save_state(workspace, state_payload):
        if not _save_state(workspace, state_payload):
            _log.warning(
                "personal pattern observation emitted but state not saved; may re-emit for %s",
                week_id,
            )
            if on_log:
                on_log(
                    "WARNING",
                    f"[consolidation] pattern observation state write failed for {week_id}",
                )
    if on_log:
        on_log("INFO", f"[consolidation] emitted personal pattern observation for {week_id}")
    return True
