"""
Notes Store — developer annotations on files and modules.

Storage: .kodara/notes.json

This is the "WHY" layer. Code tells you WHAT.
Notes tell you WHY it was written this way.

Examples:
  kodara note auth/middleware.py "Rewritten after session leak incident Nov 2024"
  kodara note billing/ "PCI compliance requires all changes reviewed by @alice"
  kodara note db/migrations.py "Never run without backup — learned the hard way"

Notes surface in:
  - kodara ask  (when a noted file is in the context)
  - kodara history  (alongside git data)
  - kodara onboard  (important context for new devs)
"""

from __future__ import annotations

import getpass
import json
import time
from dataclasses import dataclass, asdict
from datetime import datetime
from pathlib import Path

from ..impact.analyzer import _resolve_path
from ..memory.store import KodaraIndex

NOTES_FILE = "notes.json"


@dataclass
class Note:
    ts: float
    author: str
    text: str

    @property
    def date(self) -> str:
        return datetime.fromtimestamp(self.ts).strftime("%Y-%m-%d")


class NotesStore:
    def __init__(self, kodara_dir: Path):
        self.path = kodara_dir / NOTES_FILE

    def add(self, file_key: str, text: str) -> Note:
        """Add a note to a file. file_key is the normalised path."""
        note = Note(
            ts=time.time(),
            author=_username(),
            text=text.strip(),
        )
        data = self._load()
        if file_key not in data:
            data[file_key] = []
        data[file_key].append(asdict(note))
        self._save(data)
        return note

    def get(self, file_key: str) -> list[Note]:
        """Return all notes for a file, newest first."""
        data = self._load()
        return [Note(**n) for n in reversed(data.get(file_key, []))]

    def get_all(self) -> dict[str, list[Note]]:
        """Return all notes keyed by file path."""
        data = self._load()
        return {
            k: [Note(**n) for n in reversed(v)]
            for k, v in data.items()
            if v
        }

    def delete(self, file_key: str, index: int) -> bool:
        """Delete note by index (0 = newest). Returns True if deleted."""
        data = self._load()
        notes = data.get(file_key, [])
        # index 0 = newest = last in list
        list_index = len(notes) - 1 - index
        if 0 <= list_index < len(notes):
            notes.pop(list_index)
            data[file_key] = notes
            self._save(data)
            return True
        return False

    def search(self, query: str) -> list[tuple[str, Note]]:
        """Find notes containing the query string."""
        q = query.lower()
        results: list[tuple[str, Note]] = []
        for file_key, notes in self.get_all().items():
            for note in notes:
                if q in note.text.lower() or q in file_key.lower():
                    results.append((file_key, note))
        return results

    def resolve_key(self, target: str, index: KodaraIndex) -> str:
        """Resolve user input to a canonical file key."""
        # Try exact match in index first
        resolved = _resolve_path(index, target)
        if resolved:
            return resolved
        # Fall back to raw input (for dirs or files not yet indexed)
        return target.replace("\\", "/")

    # ── Storage ───────────────────────────────────────────────────────────────

    def _load(self) -> dict:
        if not self.path.exists():
            return {}
        try:
            return json.loads(self.path.read_text(encoding="utf-8"))
        except Exception:
            return {}

    def _save(self, data: dict) -> None:
        self.path.parent.mkdir(parents=True, exist_ok=True)
        tmp = self.path.with_suffix(".tmp")
        tmp.write_text(json.dumps(data, indent=2, ensure_ascii=False), encoding="utf-8")
        tmp.replace(self.path)


def _username() -> str:
    try:
        return getpass.getuser()
    except Exception:
        return "unknown"
