"""
File-based human feedback reader for human feedback during discovery process.

The human edits a markdown file via the dashboard or any text editor.
The discovery loop reads it each iteration -- if it has content,
that content is appended to (or replaces) the LLM system message.
"""

import logging
import os
import time as _time

logger = logging.getLogger(__name__)

_INITIAL_TEMPLATE = """\
# Human Feedback for SkyDiscover
# Edit this file to guide the discovery process.
# Your text will be APPENDED to the LLM system message at the next iteration.
# Toggle between Append and Replace mode in the dashboard.
# Clear this file (or delete all non-comment lines) to revert to the default.
# Lines starting with # are ignored.
#
# Examples:
#   Focus on hexagonal packing and computational geometry approaches.
#   Use numpy vectorization, avoid loops. Prioritize cache-friendly access patterns.
"""

MAX_FEEDBACK_CHARS = 4000


class HumanFeedbackReader:
    """
    Reads human feedback from a markdown file on disk.

    The dashboard writes via write_from_dashboard(); the discovery loop
    reads via read(). External editors can also modify the file directly.

    Supports two modes:
    - "append" (default): feedback is appended to the system message
    - "replace": feedback replaces the system message entirely
    """

    def __init__(self, feedback_file_path: str, mode: str = "append"):
        self.path = os.path.abspath(feedback_file_path)
        self.mode = mode if mode in ("append", "replace") else "append"
        self._last_content: str = ""
        self._current_system_prompt: str = ""
        self._history: list = []
        self._create_initial_file()

    def _create_initial_file(self) -> None:
        """Create the feedback file with instructions if it doesn't exist."""
        if not os.path.exists(self.path):
            os.makedirs(os.path.dirname(self.path), exist_ok=True)
            with open(self.path, "w") as f:
                f.write(_INITIAL_TEMPLATE)
            logger.info(f"Created human feedback file: {self.path}")

    def read(self) -> str:
        """
        Read current feedback, stripping comment lines.
        Returns empty string if file is empty, missing, or only has comments.
        """
        try:
            with open(self.path, "r") as f:
                raw = f.read()
        except (FileNotFoundError, PermissionError):
            return ""

        lines = []
        for line in raw.splitlines():
            stripped = line.strip()
            if stripped and not stripped.startswith("#"):
                lines.append(line)

        content = "\n".join(lines).strip()
        if len(content) > MAX_FEEDBACK_CHARS:
            content = content[:MAX_FEEDBACK_CHARS]

        if content != self._last_content:
            if content:
                logger.info(f"Human feedback updated ({len(content)} chars)")
            elif self._last_content:
                logger.info("Human feedback cleared")
            self._last_content = content

        return content

    def write_from_dashboard(self, text: str) -> None:
        """
        Write feedback from the dashboard UI.
        Pass empty string to clear feedback.
        """
        self._write_feedback(text)

    def set_mode(self, mode: str) -> None:
        """Set feedback mode: 'append' or 'replace'."""
        if mode not in ("append", "replace"):
            logger.warning(f"Invalid human feedback mode '{mode}', ignoring")
            return
        self.mode = mode
        logger.info(f"Human feedback mode set to: {mode}")

    def apply_feedback(self, prompt: dict) -> dict:
        """Apply current feedback to a prompt dict.

        In append mode, feedback is added after the system message.
        In replace mode, feedback replaces the system message entirely.
        Returns the modified prompt.
        """
        feedback = self.read()
        if not feedback:
            return prompt

        if self.mode == "replace":
            prompt["system"] = feedback
        else:
            prompt["system"] = (
                prompt["system"]
                + "\n\n## Human Guidance\n"
                + feedback
            )
        return prompt

    def set_current_prompt(self, system_prompt: str) -> None:
        """Store the current system prompt for dashboard visibility."""
        self._current_system_prompt = system_prompt

    def get_current_prompt(self) -> str:
        """Return the current system prompt."""
        return self._current_system_prompt

    def log_usage(self, iteration: int, feedback_text: str, mode: str) -> None:
        """Record that feedback was applied at a given iteration."""
        entry = {
            "iteration": iteration,
            "timestamp": _time.time(),
            "text": feedback_text,
            "mode": mode,
        }
        self._history.append(entry)
        logger.info(
            f"Human feedback logged: iteration={iteration}, mode={mode}, "
            f"chars={len(feedback_text)}"
        )

    def get_history(self) -> list:
        """Return the full feedback usage history."""
        return list(self._history)

    def to_serializable(self) -> dict:
        """Return current state for pickling to Island workers."""
        return {
            "feedback_text": self._last_content,
            "mode": self.mode,
            "current_prompt": self._current_system_prompt,
        }

    def _write_feedback(self, text: str) -> None:
        """Write feedback text to the file, preserving the comment header."""
        with open(self.path, "w") as f:
            if text:
                f.write(_INITIAL_TEMPLATE + "\n" + text + "\n")
            else:
                f.write(_INITIAL_TEMPLATE)
