"""Raw warehouse — durable JSONL storage for source envelopes.

Writes Envelopes from source adapters to JSONL files partitioned by
source and date.  Each line is a self-contained JSON object with full
provenance.  The raw warehouse preserves source structure with no
interpretation.

Layout::

    <warehouse_root>/raw/<source>/<YYYY-MM-DD>.jsonl

SQLite hydration loads these partitions into a ``raw_envelope`` table
for querying.
"""

import json
import sqlite3
from dataclasses import dataclass
from datetime import datetime
from pathlib import Path
from typing import Iterator

from byteb4rb1e.homeostat.envelope import Envelope


@dataclass
class RawWarehouse:
    """Reads and writes raw Envelopes to JSONL partitions."""

    root: Path

    def _partition_dir(self, source: str) -> Path:
        return self.root / "raw" / source

    def _partition_path(
        self, source: str, date: str
    ) -> Path:
        return self._partition_dir(source) / f"{date}.jsonl"

    # -- Write ---------------------------------------------------------------

    def append(self, envelope: Envelope, date: str) -> Path:
        """Append an Envelope to the JSONL partition for
        the given source and date.

        Returns the path of the partition file written to.
        """
        path = self._partition_path(envelope.source, date)
        path.parent.mkdir(parents=True, exist_ok=True)

        line = json.dumps(
            {
                "source": envelope.source,
                "endpoint": envelope.endpoint,
                "fetched_at": envelope.fetched_at,
                "payload": envelope.payload,
            },
            ensure_ascii=False,
        )

        with open(path, "a", encoding="utf-8") as f:
            f.write(line + "\n")

        return path

    # -- Read ----------------------------------------------------------------

    def read_partition(
        self, source: str, date: str
    ) -> list[Envelope]:
        """Read all Envelopes from a single partition."""
        path = self._partition_path(source, date)
        if not path.exists():
            return []

        envelopes = []
        for line in path.read_text(encoding="utf-8").splitlines():
            if not line.strip():
                continue
            row = json.loads(line)
            envelopes.append(Envelope(
                source=row["source"],
                endpoint=row["endpoint"],
                fetched_at=row["fetched_at"],
                payload=row["payload"],
            ))
        return envelopes

    def iter_partitions(
        self, source: str | None = None
    ) -> Iterator[tuple[str, str, Path]]:
        """Yield ``(source, date, path)`` for each partition file.

        If *source* is given, only yield partitions for that source.
        """
        raw_dir = self.root / "raw"
        if not raw_dir.exists():
            return

        source_dirs = (
            [raw_dir / source] if source
            else sorted(raw_dir.iterdir())
        )

        for source_dir in source_dirs:
            if not source_dir.is_dir():
                continue
            src_name = source_dir.name
            for partition in sorted(source_dir.glob("*.jsonl")):
                date = partition.stem
                yield src_name, date, partition

    # -- SQLite hydration ----------------------------------------------------

    def hydrate(self, db: sqlite3.Connection) -> int:
        """Load all raw partitions into a ``raw_envelope`` SQLite
        table.

        Creates the table if it does not exist.  Existing rows are
        cleared and reloaded (full rebuild).

        Returns the number of rows inserted.
        """
        db.execute("""
            CREATE TABLE IF NOT EXISTS raw_envelope (
                source     TEXT NOT NULL,
                endpoint   TEXT NOT NULL,
                fetched_at TEXT NOT NULL,
                date       TEXT NOT NULL,
                payload    TEXT NOT NULL
            )
        """)
        db.execute("DELETE FROM raw_envelope")

        count = 0
        for source, date, path in self.iter_partitions():
            for line in path.read_text(
                encoding="utf-8"
            ).splitlines():
                if not line.strip():
                    continue
                row = json.loads(line)
                db.execute(
                    """
                    INSERT INTO raw_envelope
                        (source, endpoint, fetched_at,
                         date, payload)
                    VALUES (?, ?, ?, ?, ?)
                    """,
                    (
                        row["source"],
                        row["endpoint"],
                        row["fetched_at"],
                        date,
                        json.dumps(
                            row["payload"],
                            ensure_ascii=False,
                        ) if not isinstance(
                            row["payload"], str
                        ) else row["payload"],
                    ),
                )
                count += 1

        db.commit()
        return count
