"""
Dual-mode path resolution for PULSE.

Dev mode (us):     Brain data in repo at ./Hippocampus/
Installed mode:    Brain data at ~/.pulse/brain/ (global per-user, all projects)

Detection: If we're inside a git repo with PULSE_OS.md, we're in dev mode.
"""

import hashlib
import json
import os
import sys
from datetime import datetime, timezone
from pathlib import Path


class ConsentDeclined(Exception):
    """User declined PULSE data consent."""
    pass


def _load_pulse_yaml(project_root: Path) -> dict:
    """Load .pulse.yaml project config (simple key: value parser, no pyyaml needed).

    Supported keys: provider, model, brain_dir, state_dir, tier
    Lines starting with # are comments. Nested keys use dot notation (search.timeout: 5).
    """
    yaml_file = project_root / ".pulse.yaml"
    if not yaml_file.exists():
        return {}
    config = {}
    try:
        for line in yaml_file.read_text(encoding="utf-8").splitlines():
            line = line.strip()
            if not line or line.startswith("#"):
                continue
            if ":" not in line:
                continue
            key, _, value = line.partition(":")
            key = key.strip()
            value = value.strip().strip("\"'")
            if value.lower() in ("true", "yes"):
                value = True
            elif value.lower() in ("false", "no"):
                value = False
            elif value.isdigit():
                value = int(value)
            config[key] = value
    except OSError:
        pass
    return config


# Cached project config from .pulse.yaml
_project_config: dict | None = None


def get_project_config() -> dict:
    """Get .pulse.yaml config for the current project (cached)."""
    global _project_config
    if _project_config is None:
        _project_config = _load_pulse_yaml(get_project_root())
    return _project_config


def _is_dev_mode() -> bool:
    """Check if running from a PULSE development checkout."""
    # Env override
    if os.environ.get("PULSE_DEV"):
        return True
    if os.environ.get("PULSE_INSTALLED"):
        return False
    # Check for repo markers relative to the pulse/ package
    pkg_dir = Path(__file__).resolve().parent.parent  # pulse/
    repo_root = pkg_dir.parent  # PULSE/
    return (repo_root / "PULSE_OS.md").exists() or (repo_root / ".git").exists()


def get_project_root() -> Path:
    """Find the root of the current project (dev repo or user project)."""
    curr = Path.cwd().resolve()
    for parent in [curr] + list(curr.parents):
        if (parent / ".git").exists() or (parent / "PULSE_OS.md").exists() or (parent / ".pulse.yaml").exists():
            return parent
    return curr


def get_project_hash() -> str:
    """Stable hash for the current project."""
    root = get_project_root()
    return hashlib.sha256(str(root).encode()).hexdigest()[:12]


def get_pulse_home() -> Path:
    """Return global ~/.pulse directory."""
    return Path.home() / ".pulse"


def get_license_file() -> Path:
    """Return path to license file: ~/.pulse/license.json."""
    return get_pulse_home() / "license.json"


def get_project_dir() -> Path:
    """Return the isolated project directory in ~/.pulse/projects/<hash>
    unless in dev mode, then return the repo root."""
    if _is_dev_mode():
        return Path(__file__).resolve().parent.parent.parent
    return get_pulse_home() / "projects" / get_project_hash()


def get_brain_dir() -> Path:
    """Return brain directory based on mode. Priority: env > .pulse.yaml > default."""
    env = os.environ.get("PULSE_BRAIN_DIR")
    if env:
        return Path(env)
    yaml_val = get_project_config().get("brain_dir")
    if yaml_val:
        return Path(yaml_val)
    if _is_dev_mode():
        return get_project_dir() / "Hippocampus"
    return get_project_dir() / "brain"


def get_state_dir() -> Path:
    """Return state directory based on mode. Priority: env > .pulse.yaml > default."""
    env = os.environ.get("PULSE_STATE_DIR")
    if env:
        return Path(env)
    yaml_val = get_project_config().get("state_dir")
    if yaml_val:
        return Path(yaml_val)
    if _is_dev_mode():
        return get_project_dir() / "state"
    return get_project_dir() / "state"


def get_root_dir() -> Path:
    """Return project root (dev) or isolated project dir (installed)."""
    env = os.environ.get("PULSE_ROOT")
    if env:
        return Path(env)
    return get_project_dir()


# === User brain initialization ===

BRAIN_SCAFFOLD = {
    "dirs": [
        "Lessons", "Failures", "Patterns", "Evidence",
        "Evidence/Sessions", "Evidence/Decisions", "Evidence/Intents",
        "Evidence/Anti_Patterns", "Evidence/Metrics",
        "Knowledge", "Private",
    ],
    "md_files": {
        "Lessons/auto_lessons.md": "# Auto Lessons\n",
        "Failures/auto_fails.md": "# Auto Fails\n",
        "Patterns/auto_patterns.md": "# Auto Patterns\n",
    },
    "json_files": {
        "Evidence/Sessions/current_session.json": "[]",
        "Evidence/Decisions/auto_decisions.json": "{}",
        "Evidence/Metrics/growth_metrics.json": "{}",
    },
}

MINIMAL_CONSTITUTION = """# PULSE Constitution
> The single sacred text. Every law enforced by code, not ceremony.

## Tier 0: The Immutables
0.1 Ahimsa (Non-Harm)
0.2 Sovereignty (Privacy)
0.3 Veracity (Truth)
0.4 Consensus (Human Override)

## Tier 1: The Sacred Laws
1. Immutable Constitution
2. Brain First
3. No Repeat Mistakes
4. Maximum Separation of Concerns
5. Small Files + Mandatory SoC Split
6. Agent Agnostic
7. No Regressions
8. Cryptographic Integrity
9. Silent Governance
10. Defense in Depth
"""


MINIMAL_ASOP = """# ASOP — Agent Self-Organization Protocol
> Minimal installed-mode copy. Full version lives in the dev repo.

## Rules
1. Check task board before starting work.
2. Claim tasks matching your tier (fast/standard/premium).
3. Save learnings after every task via pulse_save.py.
4. Do not duplicate work another agent has claimed.
"""

MINIMAL_ANTI_PATTERNS = """# Anti-Patterns Registry
| Pattern | Severity | Description |
|---------|----------|-------------|
"""


def _check_consent() -> bool:
    """Check for user consent. Returns True if consent given."""
    # 1. Env var override (CI/CD, Docker, scripted deploys)
    if os.environ.get("PULSE_CONSENT", "").lower() in ("yes", "1", "true"):
        return True

    # 2. Existing consent marker
    consent_file = get_pulse_home() / "consent.json"
    if consent_file.exists():
        try:
            data = json.loads(consent_file.read_text(encoding="utf-8"))
            if data.get("consent_given"):
                return True
        except (json.JSONDecodeError, OSError):
            pass

    # 3. Non-interactive terminal — cannot prompt
    if not sys.stdin.isatty():
        return False

    # 4. Interactive prompt
    print("\n  PULSE — Persistent Unified Learning & Synthesis Engine\n")
    print("  PULSE learns from your AI conversations to build persistent memory.")
    print("  All data is stored LOCALLY on your machine. Nothing leaves your device.")
    print("  You can revoke consent and delete data at any time with: pulse consent revoke\n")
    try:
        answer = input("  Continue? [Y/n] ").strip().lower()
    except (EOFError, KeyboardInterrupt):
        return False
    if answer and answer != "y":
        return False

    # 5. Save consent marker
    consent_file.parent.mkdir(parents=True, exist_ok=True)
    consent_file.write_text(json.dumps({
        "consent_given": True,
        "consent_date": datetime.now(timezone.utc).isoformat(),
        "version": "1.0",
    }, indent=2), encoding="utf-8")
    return True


def init_user_brain():
    """Create per-project scaffold at ~/.pulse/projects/<hash>/."""
    if not _check_consent():
        raise ConsentDeclined("PULSE consent not given. Set PULSE_CONSENT=yes or run interactively.")
    proj_dir = get_project_dir()
    brain_dir = proj_dir / "brain"
    state_dir = proj_dir / "state"

    if (brain_dir / "Lessons").exists():
        print(f"[PULSE] Brain already initialized at {proj_dir}")
        return

    print(f"[PULSE] Initializing brain at {proj_dir}")

    # Brain directories + files
    for d in BRAIN_SCAFFOLD["dirs"]:
        (brain_dir / d).mkdir(parents=True, exist_ok=True)
    for path, content in BRAIN_SCAFFOLD["md_files"].items():
        (brain_dir / path).write_text(content, encoding="utf-8")
    for path, content in BRAIN_SCAFFOLD["json_files"].items():
        (brain_dir / path).write_text(content + "\n", encoding="utf-8")

    # State
    state_dir.mkdir(parents=True, exist_ok=True)
    (state_dir / "agent_registry.json").write_text("{}\n", encoding="utf-8")

    # Governance: Constitution + Protocols
    cc_dir = proj_dir / "Corpus_Callosum" / "Protocols"
    cc_dir.mkdir(parents=True, exist_ok=True)
    (cc_dir.parent / "CONSTITUTION.md").write_text(MINIMAL_CONSTITUTION, encoding="utf-8")
    (cc_dir / "ASOP.md").write_text(MINIMAL_ASOP, encoding="utf-8")

    # Task board
    pf_dir = proj_dir / "Prefrontal_Cortex" / "Action_Queue"
    pf_dir.mkdir(parents=True, exist_ok=True)
    (pf_dir / "task_board.json").write_text("{}\n", encoding="utf-8")

    # Anti-patterns
    (brain_dir / "Patterns" / "anti_patterns.md").write_text(
        MINIMAL_ANTI_PATTERNS, encoding="utf-8"
    )

    # Copy default brain_config.json to user project (customizable)
    user_config_dir = proj_dir / "config"
    user_config_dir.mkdir(parents=True, exist_ok=True)
    user_config = user_config_dir / "brain_config.json"
    if not user_config.exists():
        pkg_config = Path(__file__).resolve().parent.parent / "config" / "brain_config.json"
        if pkg_config.exists():
            import shutil
            shutil.copy2(pkg_config, user_config)

    # Auto-generate .env with BRAIN_KEY (zero manual setup)
    env_file = proj_dir / ".env"
    if not env_file.exists():
        import secrets
        brain_key = secrets.token_hex(32)
        api_token = secrets.token_hex(16)
        env_file.write_text(
            f"# PULSE auto-generated environment\n"
            f"PULSE_BRAIN_KEY={brain_key}\n"
            f"PULSE_API_TOKEN={api_token}\n",
            encoding="utf-8",
        )

    # User profile (uses UserProfile dataclass → ~/.pulse/profile.json)
    from pulse.core.user_profile import UserProfile
    profile = UserProfile.load()
    _detected = profile.detect_providers()
    profile.save()

    print(f"  Brain:      {brain_dir}")
    print(f"  State:      {state_dir}")
    print(f"  Laws:       {cc_dir.parent / 'CONSTITUTION.md'}")
    print(f"  Task board: {pf_dir / 'task_board.json'}")
    if _detected:
        print(f"  Providers:  {', '.join(_detected)}")
    else:
        print("\n  No API keys detected. Set one or more to get started:")
        print("    export ANTHROPIC_API_KEY=sk-...")
        print("    export GEMINI_API_KEY=AI...")
        print("    export OPENAI_API_KEY=sk-...")
        print("  Or install Ollama for local models: https://ollama.com")
    print(f"  Config:     {proj_dir / 'config' / 'brain_config.json'}")
    print(f"  License:    Run 'pulse activate <KEY>' to activate your subscription")
    print("\n[PULSE] Done. Run `pulse status` to verify.")


if __name__ == "__main__":
    print(f"Dev mode: {_is_dev_mode()}")
    print(f"Brain dir: {get_brain_dir()}")
    print(f"State dir: {get_state_dir()}")
    print(f"Root dir: {get_root_dir()}")
