"""
Feedback Store — SQLite persistence for all feedback records and quality profiles.

PRD §8 — Data Models:
    MemoryFeedbackRecord  (§8.1)
    WorkflowFeedbackRecord (§8.2)
    MemoryQualityProfile   (§8.3)
"""

import time
import uuid
import sqlite3
import threading
import logging
from dataclasses import dataclass, field, asdict
from typing import List, Optional

logger = logging.getLogger("openmemo.feedback")


# ── Data Models ───────────────────────────────────────────────────────────────

@dataclass
class MemoryFeedbackRecord:
    """PRD §8.1 — Per-memory feedback event."""
    memory_id: str
    task_id: str = ""
    agent_id: str = ""
    memory_layer: str = ""         # experience | user | context | task | team
    recalled: bool = False
    injected: bool = False
    token_size: int = 0
    position: str = ""             # e.g. "experience_block"
    adopted: bool = False
    success: bool = False
    feedback_id: str = field(default_factory=lambda: str(uuid.uuid4()))
    timestamp: float = field(default_factory=time.time)

    def to_dict(self) -> dict:
        return asdict(self)

    @classmethod
    def from_row(cls, row) -> "MemoryFeedbackRecord":
        return cls(
            feedback_id=row["feedback_id"],
            memory_id=row["memory_id"],
            task_id=row["task_id"],
            agent_id=row["agent_id"],
            memory_layer=row["memory_layer"],
            recalled=bool(row["recalled"]),
            injected=bool(row["injected"]),
            token_size=row["token_size"],
            position=row["position"],
            adopted=bool(row["adopted"]),
            success=bool(row["success"]),
            timestamp=row["timestamp"],
        )


@dataclass
class WorkflowFeedbackRecord:
    """PRD §8.2 — Per-workflow recommendation feedback event."""
    workflow_id: str
    task_id: str = ""
    agent_id: str = ""
    recommended: bool = True
    adopted: bool = False
    success: bool = False
    adoption_rate: float = 0.0     # portion of workflow steps adopted
    completion_time: float = 0.0   # seconds to complete
    feedback_id: str = field(default_factory=lambda: str(uuid.uuid4()))
    timestamp: float = field(default_factory=time.time)

    def to_dict(self) -> dict:
        return asdict(self)

    @classmethod
    def from_row(cls, row) -> "WorkflowFeedbackRecord":
        return cls(
            feedback_id=row["feedback_id"],
            workflow_id=row["workflow_id"],
            task_id=row["task_id"],
            agent_id=row["agent_id"],
            recommended=bool(row["recommended"]),
            adopted=bool(row["adopted"]),
            success=bool(row["success"]),
            adoption_rate=row["adoption_rate"],
            completion_time=row["completion_time"],
            timestamp=row["timestamp"],
        )


@dataclass
class MemoryQualityProfile:
    """PRD §8.3 — Per-memory quality accumulator."""
    memory_id: str
    usage_count: int = 0       # total times recalled
    injection_count: int = 0   # total times injected
    adoption_count: int = 0    # times actually adopted
    success_count: int = 0     # times led to success
    noise_count: int = 0       # recalled+injected but never adopted
    quality_score: float = 0.0
    archive_candidate: bool = False
    last_updated: float = field(default_factory=time.time)
    created_at: float = field(default_factory=time.time)

    @property
    def adoption_rate(self) -> float:
        return self.adoption_count / self.usage_count if self.usage_count else 0.0

    @property
    def success_rate(self) -> float:
        return self.success_count / self.adoption_count if self.adoption_count else 0.0

    def to_dict(self) -> dict:
        d = asdict(self)
        d["adoption_rate"] = self.adoption_rate
        d["success_rate"] = self.success_rate
        return d

    @classmethod
    def from_row(cls, row) -> "MemoryQualityProfile":
        return cls(
            memory_id=row["memory_id"],
            usage_count=row["usage_count"],
            injection_count=row["injection_count"],
            adoption_count=row["adoption_count"],
            success_count=row["success_count"],
            noise_count=row["noise_count"],
            quality_score=row["quality_score"],
            archive_candidate=bool(row["archive_candidate"]),
            last_updated=row["last_updated"],
            created_at=row["created_at"],
        )


# ── Store ─────────────────────────────────────────────────────────────────────

class FeedbackStore:
    """
    SQLite-backed store for all feedback records and quality profiles.

    Usage:
        store = FeedbackStore("openmemo.db")
        store.record_memory_feedback(MemoryFeedbackRecord(
            memory_id="mem_123", task_id="task_001",
            recalled=True, injected=True, adopted=False
        ))
        profile = store.get_quality_profile("mem_123")
    """

    def __init__(self, db_path: str = "openmemo.db"):
        self.db_path = db_path
        self._local = threading.local()
        self._init_tables()

    @property
    def conn(self) -> sqlite3.Connection:
        if not hasattr(self._local, "conn") or self._local.conn is None:
            c = sqlite3.connect(self.db_path, check_same_thread=False)
            c.row_factory = sqlite3.Row
            c.execute("PRAGMA journal_mode=WAL")
            c.execute("PRAGMA synchronous=NORMAL")
            self._local.conn = c
        return self._local.conn

    def _init_tables(self):
        self.conn.executescript("""
            CREATE TABLE IF NOT EXISTS memory_feedback (
                feedback_id   TEXT PRIMARY KEY,
                memory_id     TEXT NOT NULL,
                task_id       TEXT DEFAULT '',
                agent_id      TEXT DEFAULT '',
                memory_layer  TEXT DEFAULT '',
                recalled      INTEGER DEFAULT 0,
                injected      INTEGER DEFAULT 0,
                token_size    INTEGER DEFAULT 0,
                position      TEXT DEFAULT '',
                adopted       INTEGER DEFAULT 0,
                success       INTEGER DEFAULT 0,
                timestamp     REAL NOT NULL
            );
            CREATE INDEX IF NOT EXISTS idx_mfb_memory
                ON memory_feedback(memory_id, timestamp);
            CREATE INDEX IF NOT EXISTS idx_mfb_task
                ON memory_feedback(task_id);

            CREATE TABLE IF NOT EXISTS workflow_feedback (
                feedback_id     TEXT PRIMARY KEY,
                workflow_id     TEXT NOT NULL,
                task_id         TEXT DEFAULT '',
                agent_id        TEXT DEFAULT '',
                recommended     INTEGER DEFAULT 1,
                adopted         INTEGER DEFAULT 0,
                success         INTEGER DEFAULT 0,
                adoption_rate   REAL DEFAULT 0.0,
                completion_time REAL DEFAULT 0.0,
                timestamp       REAL NOT NULL
            );
            CREATE INDEX IF NOT EXISTS idx_wfb_workflow
                ON workflow_feedback(workflow_id, timestamp);

            CREATE TABLE IF NOT EXISTS memory_quality_profiles (
                memory_id         TEXT PRIMARY KEY,
                usage_count       INTEGER DEFAULT 0,
                injection_count   INTEGER DEFAULT 0,
                adoption_count    INTEGER DEFAULT 0,
                success_count     INTEGER DEFAULT 0,
                noise_count       INTEGER DEFAULT 0,
                quality_score     REAL DEFAULT 0.0,
                archive_candidate INTEGER DEFAULT 0,
                last_updated      REAL NOT NULL,
                created_at        REAL NOT NULL
            );
        """)
        self.conn.commit()

    # ── Memory Feedback ────────────────────────────────────────────────────────

    def record_memory_feedback(self, fb: MemoryFeedbackRecord) -> str:
        self.conn.execute("""
            INSERT INTO memory_feedback
                (feedback_id, memory_id, task_id, agent_id, memory_layer,
                 recalled, injected, token_size, position,
                 adopted, success, timestamp)
            VALUES (?,?,?,?,?,?,?,?,?,?,?,?)
        """, (
            fb.feedback_id, fb.memory_id, fb.task_id, fb.agent_id,
            fb.memory_layer, int(fb.recalled), int(fb.injected),
            fb.token_size, fb.position, int(fb.adopted),
            int(fb.success), fb.timestamp,
        ))
        self.conn.commit()
        return fb.feedback_id

    def get_memory_feedback(self, memory_id: str,
                             limit: int = 100) -> List[MemoryFeedbackRecord]:
        rows = self.conn.execute(
            "SELECT * FROM memory_feedback WHERE memory_id=? ORDER BY timestamp DESC LIMIT ?",
            (memory_id, limit)
        ).fetchall()
        return [MemoryFeedbackRecord.from_row(r) for r in rows]

    def get_task_feedback(self, task_id: str) -> List[MemoryFeedbackRecord]:
        rows = self.conn.execute(
            "SELECT * FROM memory_feedback WHERE task_id=? ORDER BY timestamp ASC",
            (task_id,)
        ).fetchall()
        return [MemoryFeedbackRecord.from_row(r) for r in rows]

    # ── Workflow Feedback ──────────────────────────────────────────────────────

    def record_workflow_feedback(self, fb: WorkflowFeedbackRecord) -> str:
        self.conn.execute("""
            INSERT INTO workflow_feedback
                (feedback_id, workflow_id, task_id, agent_id,
                 recommended, adopted, success,
                 adoption_rate, completion_time, timestamp)
            VALUES (?,?,?,?,?,?,?,?,?,?)
        """, (
            fb.feedback_id, fb.workflow_id, fb.task_id, fb.agent_id,
            int(fb.recommended), int(fb.adopted), int(fb.success),
            fb.adoption_rate, fb.completion_time, fb.timestamp,
        ))
        self.conn.commit()
        return fb.feedback_id

    def get_workflow_feedback(self, workflow_id: str,
                               limit: int = 100) -> List[WorkflowFeedbackRecord]:
        rows = self.conn.execute(
            "SELECT * FROM workflow_feedback WHERE workflow_id=? ORDER BY timestamp DESC LIMIT ?",
            (workflow_id, limit)
        ).fetchall()
        return [WorkflowFeedbackRecord.from_row(r) for r in rows]

    # ── Quality Profiles ───────────────────────────────────────────────────────

    def get_quality_profile(self, memory_id: str) -> MemoryQualityProfile:
        row = self.conn.execute(
            "SELECT * FROM memory_quality_profiles WHERE memory_id=?",
            (memory_id,)
        ).fetchone()
        if row:
            return MemoryQualityProfile.from_row(row)
        now = time.time()
        return MemoryQualityProfile(memory_id=memory_id,
                                     last_updated=now, created_at=now)

    def save_quality_profile(self, profile: MemoryQualityProfile):
        self.conn.execute("""
            INSERT INTO memory_quality_profiles
                (memory_id, usage_count, injection_count, adoption_count,
                 success_count, noise_count, quality_score,
                 archive_candidate, last_updated, created_at)
            VALUES (?,?,?,?,?,?,?,?,?,?)
            ON CONFLICT(memory_id) DO UPDATE SET
                usage_count=excluded.usage_count,
                injection_count=excluded.injection_count,
                adoption_count=excluded.adoption_count,
                success_count=excluded.success_count,
                noise_count=excluded.noise_count,
                quality_score=excluded.quality_score,
                archive_candidate=excluded.archive_candidate,
                last_updated=excluded.last_updated
        """, (
            profile.memory_id, profile.usage_count, profile.injection_count,
            profile.adoption_count, profile.success_count, profile.noise_count,
            profile.quality_score, int(profile.archive_candidate),
            profile.last_updated, profile.created_at,
        ))
        self.conn.commit()

    def list_profiles(self, min_quality: float = 0.0,
                      limit: int = 100) -> List[MemoryQualityProfile]:
        rows = self.conn.execute(
            "SELECT * FROM memory_quality_profiles WHERE quality_score >= ? ORDER BY quality_score DESC LIMIT ?",
            (min_quality, limit)
        ).fetchall()
        return [MemoryQualityProfile.from_row(r) for r in rows]

    def list_noisy_profiles(self, min_noise: int = 3,
                             limit: int = 50) -> List[MemoryQualityProfile]:
        rows = self.conn.execute(
            "SELECT * FROM memory_quality_profiles WHERE noise_count >= ? ORDER BY noise_count DESC LIMIT ?",
            (min_noise, limit)
        ).fetchall()
        return [MemoryQualityProfile.from_row(r) for r in rows]

    def close(self):
        if hasattr(self._local, "conn") and self._local.conn:
            self._local.conn.close()
            self._local.conn = None
