"""
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 CWD is inside the PULSE development repo.

    Dev mode = user is working in the PULSE repo itself.
    NOT dev mode = user is in any other project (even with editable install).
    """
    # Env override
    if os.environ.get("PULSE_DEV"):
        return True
    if os.environ.get("PULSE_INSTALLED"):
        return False
    # Check CWD: are we inside the PULSE dev repo?
    cwd = Path.cwd().resolve()
    for parent in [cwd] + list(cwd.parents):
        if (parent / "PULSE_OS.md").exists() and (parent / "pulse").is_dir():
            return True
    return False


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():
        # CWD is inside the PULSE dev repo — find the repo root
        cwd = Path.cwd().resolve()
        for parent in [cwd] + list(cwd.parents):
            if (parent / "PULSE_OS.md").exists() and (parent / "pulse").is_dir():
                return parent
        return cwd
    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_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


class NoProjectRoot(Exception):
    """No valid project root found (no .git, .pulse.yaml, or PULSE_OS.md)."""
    pass


def _has_project_marker(root: Path) -> bool:
    """Check if directory has a valid project marker."""
    return (
        (root / ".git").exists()
        or (root / ".pulse.yaml").exists()
        or (root / "PULSE_OS.md").exists()
    )


def init_user_brain(force: bool = False):
    """Create per-project scaffold at ~/.pulse/projects/<hash>/.

    Args:
        force: If True, skip project marker check (used by 'pulse init').
    """
    if not force:
        root = get_project_root()
        if not _has_project_marker(root):
            raise NoProjectRoot(
                f"No project found at {root}.\n"
                "  PULSE creates a separate brain per project.\n"
                "  To initialize here, run: pulse init\n"
                "  Or navigate to a git repository first."
            )

    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 + task board
    state_dir.mkdir(parents=True, exist_ok=True)
    (state_dir / "agent_registry.json").write_text("{}\n", encoding="utf-8")
    (state_dir / "task_board.json").write_text("{}\n", encoding="utf-8")

    # Migrate existing users: move task_board from old location
    old_tb = proj_dir / "Prefrontal_Cortex" / "Action_Queue" / "task_board.json"
    if old_tb.exists():
        import shutil
        shutil.move(str(old_tb), str(state_dir / "task_board.json"))
        # Clean up empty old dirs
        old_aq = old_tb.parent
        old_pf = old_aq.parent
        if old_aq.exists() and not any(old_aq.iterdir()):
            old_aq.rmdir()
        if old_pf.exists() and not any(old_pf.iterdir()):
            old_pf.rmdir()
    # Clean up old governance scaffold (no longer needed)
    old_cc = proj_dir / "Corpus_Callosum"
    if old_cc.exists() and not _is_dev_mode():
        import shutil
        shutil.rmtree(str(old_cc), ignore_errors=True)

    # 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"  Task board: {state_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()}")
