"""Unit tests for the raw warehouse."""

import json
import sqlite3

import pytest

from byteb4rb1e.homeostat.envelope import Envelope
from byteb4rb1e.homeostat.warehouse.raw import RawWarehouse


# -- Fixtures ----------------------------------------------------------------


@pytest.fixture
def warehouse(tmp_path):
    return RawWarehouse(root=tmp_path)


@pytest.fixture
def fitbit_envelope():
    return Envelope(
        source="fitbit",
        endpoint="/1/user/-/activities/steps/date/today/1d.json",
        fetched_at="2026-03-19T15:00:00+00:00",
        payload={"activities-steps": [{"value": "8000"}]},
    )


@pytest.fixture
def fddb_envelope():
    return Envelope(
        source="fddb",
        endpoint="notepad/month/2026-03",
        fetched_at="2026-03-19T15:05:00+00:00",
        payload="<html><body>diary</body></html>",
    )


@pytest.fixture
def db():
    conn = sqlite3.connect(":memory:")
    yield conn
    conn.close()


# -- Append ------------------------------------------------------------------


class TestAppend:
    def test_creates_partition_file(
        self, warehouse, fitbit_envelope
    ):
        path = warehouse.append(fitbit_envelope, "2026-03-19")
        assert path.exists()
        assert path.name == "2026-03-19.jsonl"
        assert "raw/fitbit" in str(path)

    def test_appends_to_existing_partition(
        self, warehouse, fitbit_envelope
    ):
        warehouse.append(fitbit_envelope, "2026-03-19")
        warehouse.append(fitbit_envelope, "2026-03-19")

        path = warehouse._partition_path(
            "fitbit", "2026-03-19"
        )
        lines = path.read_text().strip().splitlines()
        assert len(lines) == 2

    def test_writes_valid_json_lines(
        self, warehouse, fitbit_envelope
    ):
        warehouse.append(fitbit_envelope, "2026-03-19")

        path = warehouse._partition_path(
            "fitbit", "2026-03-19"
        )
        line = path.read_text().strip()
        row = json.loads(line)
        assert row["source"] == "fitbit"
        assert row["endpoint"] == fitbit_envelope.endpoint
        assert row["fetched_at"] == fitbit_envelope.fetched_at
        assert row["payload"] == fitbit_envelope.payload

    def test_html_payload_stored_as_string(
        self, warehouse, fddb_envelope
    ):
        warehouse.append(fddb_envelope, "2026-03-19")

        path = warehouse._partition_path(
            "fddb", "2026-03-19"
        )
        row = json.loads(path.read_text().strip())
        assert row["payload"] == fddb_envelope.payload
        assert isinstance(row["payload"], str)

    def test_different_sources_different_dirs(
        self, warehouse, fitbit_envelope, fddb_envelope
    ):
        p1 = warehouse.append(fitbit_envelope, "2026-03-19")
        p2 = warehouse.append(fddb_envelope, "2026-03-19")
        assert p1.parent.name == "fitbit"
        assert p2.parent.name == "fddb"


# -- Read partition ----------------------------------------------------------


class TestReadPartition:
    def test_read_returns_envelopes(
        self, warehouse, fitbit_envelope
    ):
        warehouse.append(fitbit_envelope, "2026-03-19")
        envelopes = warehouse.read_partition(
            "fitbit", "2026-03-19"
        )
        assert len(envelopes) == 1
        assert envelopes[0].source == "fitbit"
        assert envelopes[0].payload == fitbit_envelope.payload

    def test_read_missing_partition_returns_empty(
        self, warehouse
    ):
        result = warehouse.read_partition("fitbit", "1999-01-01")
        assert result == []

    def test_read_multiple_lines(
        self, warehouse, fitbit_envelope
    ):
        warehouse.append(fitbit_envelope, "2026-03-19")
        warehouse.append(fitbit_envelope, "2026-03-19")
        envelopes = warehouse.read_partition(
            "fitbit", "2026-03-19"
        )
        assert len(envelopes) == 2

    def test_read_html_payload(
        self, warehouse, fddb_envelope
    ):
        warehouse.append(fddb_envelope, "2026-03-19")
        envelopes = warehouse.read_partition(
            "fddb", "2026-03-19"
        )
        assert envelopes[0].payload == fddb_envelope.payload


# -- Iter partitions ---------------------------------------------------------


class TestIterPartitions:
    def test_yields_all_partitions(
        self, warehouse, fitbit_envelope, fddb_envelope
    ):
        warehouse.append(fitbit_envelope, "2026-03-19")
        warehouse.append(fddb_envelope, "2026-03-20")

        partitions = list(warehouse.iter_partitions())
        assert len(partitions) == 2

    def test_filter_by_source(
        self, warehouse, fitbit_envelope, fddb_envelope
    ):
        warehouse.append(fitbit_envelope, "2026-03-19")
        warehouse.append(fddb_envelope, "2026-03-20")

        partitions = list(
            warehouse.iter_partitions(source="fitbit")
        )
        assert len(partitions) == 1
        assert partitions[0][0] == "fitbit"

    def test_empty_warehouse_yields_nothing(self, warehouse):
        partitions = list(warehouse.iter_partitions())
        assert partitions == []

    def test_partition_tuple_structure(
        self, warehouse, fitbit_envelope
    ):
        warehouse.append(fitbit_envelope, "2026-03-19")
        source, date, path = next(
            warehouse.iter_partitions()
        )
        assert source == "fitbit"
        assert date == "2026-03-19"
        assert path.exists()


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


class TestHydrate:
    def test_creates_table_and_inserts_rows(
        self, warehouse, fitbit_envelope, db
    ):
        warehouse.append(fitbit_envelope, "2026-03-19")
        count = warehouse.hydrate(db)

        assert count == 1
        rows = db.execute(
            "SELECT * FROM raw_envelope"
        ).fetchall()
        assert len(rows) == 1

    def test_row_fields(
        self, warehouse, fitbit_envelope, db
    ):
        warehouse.append(fitbit_envelope, "2026-03-19")
        warehouse.hydrate(db)

        row = db.execute(
            "SELECT source, endpoint, fetched_at, date,"
            " payload FROM raw_envelope"
        ).fetchone()
        assert row[0] == "fitbit"
        assert row[1] == fitbit_envelope.endpoint
        assert row[2] == fitbit_envelope.fetched_at
        assert row[3] == "2026-03-19"
        payload = json.loads(row[4])
        assert payload == fitbit_envelope.payload

    def test_html_payload_stored_as_text(
        self, warehouse, fddb_envelope, db
    ):
        warehouse.append(fddb_envelope, "2026-03-19")
        warehouse.hydrate(db)

        row = db.execute(
            "SELECT payload FROM raw_envelope"
        ).fetchone()
        assert row[0] == fddb_envelope.payload

    def test_full_rebuild_clears_old_rows(
        self, warehouse, fitbit_envelope, db
    ):
        warehouse.append(fitbit_envelope, "2026-03-19")
        warehouse.hydrate(db)
        assert warehouse.hydrate(db) == 1

        rows = db.execute(
            "SELECT COUNT(*) FROM raw_envelope"
        ).fetchone()
        assert rows[0] == 1

    def test_multiple_sources_and_dates(
        self, warehouse, fitbit_envelope, fddb_envelope, db
    ):
        warehouse.append(fitbit_envelope, "2026-03-19")
        warehouse.append(fitbit_envelope, "2026-03-20")
        warehouse.append(fddb_envelope, "2026-03-19")

        count = warehouse.hydrate(db)
        assert count == 3

    def test_empty_warehouse_creates_empty_table(
        self, warehouse, db
    ):
        count = warehouse.hydrate(db)
        assert count == 0
        rows = db.execute(
            "SELECT COUNT(*) FROM raw_envelope"
        ).fetchone()
        assert rows[0] == 0
