# -*- coding: utf-8 -*-
"""anu_v3.artifact_root_resolver — canonical ANU workspace root resolver.

task-2553+46 (구현목표 C). Standalone, read-only, pure stdlib.

Problem (회장 §2.2 / diagnosis report_items.2): the +39 fallback / dead-man
collector first looked at the *autoset / current cwd* (`/home/jay/.cokacdir/
workspace/autoset`, which has NO `memory/events`) and concluded the artifact
was missing — but the real CANONICAL workspace root
`/home/jay/workspace/memory/events` actually contained the +39
result/collector-result.

This module hard-codes the canonical ANU workspace root as executable code
(CLAUDE.md §1: "모든 코드는 `/home/jay/workspace/` 하위에 작성") and gives
collectors a *single* place to resolve the events / reports / schedule_history
lookup roots, so that an artifact-missing verdict is NEVER reached on an
autoset-cwd false negative without first re-checking the canonical root
(§3.C, regression 10/11/12).

NO-CRON / Layer A (9-R.1): this module performs ZERO write, ZERO cron
register/remove, ZERO dispatch, ZERO merge. It only resolves paths and
stat/exists-checks files. It never executes ``cokacdir``/``subprocess``.
"""
from __future__ import annotations

import os
from dataclasses import dataclass, field
from pathlib import Path
from typing import List, Optional

RESOLVER_SCHEMA = "anu_v3.artifact_root_resolution.v1"

# ── canonical anchor (회장 §3 사실 + CLAUDE.md §1) ────────────────────────────
# ANU canonical workspace root. Confirmed: the +39 result / collector-result
# live here, NOT under the autoset/current cwd.
CANONICAL_ANU_WORKSPACE_ROOT = Path("/home/jay/workspace")

# autoset / current cwd anchor (회장 §3 사실): has NO memory/events.
AUTOSET_CWD_ROOT = Path("/home/jay/.cokacdir/workspace/autoset")

# Shared (cross-chat) schedule-history dir. Read-only; chat isolation is the
# caller's responsibility (collector_artifact_lookup enforces chat_id).
DEFAULT_SCHEDULE_HISTORY_DIR = Path("/home/jay/.cokacdir/schedule_history")

_EVENTS_SUBDIR = "memory/events"
_REPORTS_SUBDIR = "memory/reports"


@dataclass(frozen=True)
class ResolvedRoots:
    """Unified, canonical-first lookup roots for collector artifact checks."""

    schema: str
    canonical_root: str
    autoset_cwd_root: str
    events_dir: str
    reports_dir: str
    schedule_history_dir: str
    canonical_root_present: bool
    events_dir_present: bool
    # Whether the autoset/current cwd itself has a memory/events dir. A
    # False here must NEVER alone yield RESULT_MISSING (§3.C/regression 11).
    autoset_events_dir_present: bool = False
    # The roots that were searched, canonical FIRST (§3.C ordering proof).
    search_order: List[str] = field(default_factory=list)

    def to_json(self) -> dict:
        return {
            "schema": self.schema,
            "canonical_root": self.canonical_root,
            "autoset_cwd_root": self.autoset_cwd_root,
            "events_dir": self.events_dir,
            "reports_dir": self.reports_dir,
            "schedule_history_dir": self.schedule_history_dir,
            "canonical_root_present": self.canonical_root_present,
            "events_dir_present": self.events_dir_present,
            "autoset_events_dir_present": self.autoset_events_dir_present,
            "search_order": list(self.search_order),
        }


def _exists_dir(p: Path) -> bool:
    try:
        return p.is_dir()
    except OSError:
        return False


def canonical_root() -> Path:
    """The single source of truth for the ANU workspace root (§3.C)."""
    return CANONICAL_ANU_WORKSPACE_ROOT


def is_autoset_cwd(cwd: Optional[os.PathLike] = None) -> bool:
    """True iff the (current) cwd is the artifact-poor autoset workspace.

    Used to assert that an autoset cwd alone must NEVER yield RESULT_MISSING
    (regression 11) — the canonical root has to be consulted first.
    """
    c = Path(cwd) if cwd is not None else Path.cwd()
    try:
        return c.resolve() == AUTOSET_CWD_ROOT.resolve() or str(c).startswith(
            str(AUTOSET_CWD_ROOT)
        )
    except OSError:
        return str(c).startswith(str(AUTOSET_CWD_ROOT))


def resolve_roots(
    *,
    autoset_cwd: Optional[os.PathLike] = None,
    schedule_history_dir: Optional[os.PathLike] = None,
) -> ResolvedRoots:
    """Resolve the canonical-first lookup roots.

    The canonical root is always searched FIRST; the autoset cwd is recorded
    for provenance only and is NEVER used to decide artifact-missing on its
    own (§3.C / regression 10/11). Read-only.
    """
    croot = CANONICAL_ANU_WORKSPACE_ROOT
    aroot = (
        Path(autoset_cwd) if autoset_cwd is not None else AUTOSET_CWD_ROOT
    )
    sh_dir = (
        Path(schedule_history_dir)
        if schedule_history_dir is not None
        else DEFAULT_SCHEDULE_HISTORY_DIR
    )
    events_dir = croot / _EVENTS_SUBDIR
    reports_dir = croot / _REPORTS_SUBDIR
    return ResolvedRoots(
        schema=RESOLVER_SCHEMA,
        canonical_root=str(croot),
        autoset_cwd_root=str(aroot),
        events_dir=str(events_dir),
        reports_dir=str(reports_dir),
        schedule_history_dir=str(sh_dir),
        canonical_root_present=_exists_dir(croot),
        events_dir_present=_exists_dir(events_dir),
        autoset_events_dir_present=_exists_dir(aroot / _EVENTS_SUBDIR),
        # canonical FIRST, autoset second — proves §3.C re-check ordering.
        search_order=[str(croot), str(aroot)],
    )


def canonical_events_dir() -> Path:
    return CANONICAL_ANU_WORKSPACE_ROOT / _EVENTS_SUBDIR


def canonical_reports_dir() -> Path:
    return CANONICAL_ANU_WORKSPACE_ROOT / _REPORTS_SUBDIR
