from __future__ import annotations

from typing import Any

from .service import MemoryService, make_id, utcnow


class MemoryEngine:
    def __init__(self, service: MemoryService) -> None:
        self.service = service

    def process_pending(self, limit: int | None = None) -> list[dict[str, Any]]:
        events = self.service.list_pending_events()
        if limit is not None:
            events = events[:limit]

        processed: list[dict[str, Any]] = []
        for event in events:
            try:
                processed.append(self._process_event(event))
                self.service.mark_event(event["id"], "completed", outcome="extracted")
            except Exception as exc:  # pragma: no cover - defensive
                self.service.mark_event(event["id"], "failed", error_message=str(exc))
        return processed

    def run_due_maintenance(self, *, limit: int | None = None, job_id: str | None = None) -> list[dict[str, Any]]:
        if job_id:
            jobs = [self.service.get_maintenance_job(job_id)]
        else:
            jobs = self.service.list_due_maintenance_jobs(limit=limit)
        results: list[dict[str, Any]] = []
        for job in jobs:
            run = self.service.create_maintenance_run(job["id"])
            try:
                stats = self._run_maintenance_job(job)
                self.service.finish_maintenance_run(
                    run["id"],
                    status="completed",
                    summary=f"{job['job_type']} completed",
                    stats=stats,
                )
                next_due = self.service._compute_next_due(
                    utcnow(),
                    job["cadence"],
                    job.get("interval_minutes"),
                    job.get("window_start"),
                    job.get("window_end"),
                )
                self.service.update_maintenance_job(
                    job["id"],
                    next_due_at=next_due,
                    last_run_at=utcnow(),
                    last_status="completed",
                    last_summary=f"{job['job_type']} completed",
                )
                results.append({"job_id": job["id"], "status": "completed", "stats": stats})
            except Exception as exc:
                self.service.finish_maintenance_run(
                    run["id"],
                    status="failed",
                    summary=f"{job['job_type']} failed",
                    stats={},
                    error_message=str(exc),
                )
                self.service.update_maintenance_job(
                    job["id"],
                    last_run_at=utcnow(),
                    last_status="failed",
                    last_summary=str(exc),
                )
                results.append({"job_id": job["id"], "status": "failed", "error": str(exc)})
        return results

    def _run_maintenance_job(self, job: dict[str, Any]) -> dict[str, Any]:
        job_type = job.get("job_type")
        if job_type == "consolidation":
            return self.service.consolidate_candidates(config=job.get("metadata") or {})
        return {"skipped": True, "reason": f"unknown job_type {job_type}"}

    def _process_event(self, event: dict[str, Any]) -> dict[str, Any]:
        with self.service.store.connection() as conn:
            source = conn.execute("SELECT * FROM sources WHERE id = ?", (event["source_id"],)).fetchone()
            if source is None:
                raise KeyError(event["source_id"])
            source_dict = dict(source)

        content = source_dict["content"]
        summary = self.service._make_summary(content)
        memory = self.service.ingest(
            {
                "id": make_id("mem"),
                "type": "episode",
                "scope": self._normalize_scope(event["requested_scope"]),
                "status": "inbox",
                "source_kind": source_dict["source_kind"],
                "title": source_dict["title"],
                "content": content,
                "summary": summary,
                "confidence": 0.6,
                "freshness": 1.0,
                "observed_at": source_dict["created_at"],
                "source_ref": source_dict["source_ref"],
                "evidence_ref": source_dict["id"],
                "embedding": self.service._text_embedding(content),
                "metadata": {
                    "ingested_by": "memory-engine",
                    "trigger_type": event["trigger_type"],
                    "captured_at": utcnow(),
                },
            }
        )
        return {"event_id": event["id"], "memory_id": memory["id"]}

    def _normalize_scope(self, requested_scope: str) -> str:
        if requested_scope in {"global", "project", "repo", "agent", "session"}:
            return requested_scope
        return "session"
