"""Tests for S3Store — requires MinIO running via docker compose.

Run with: pytest -m s3
Skip by default in normal test runs.

Start MinIO:
  cd examples/anthropic-s3 && docker compose up -d
"""

from __future__ import annotations

import os
import uuid

import pytest

from bandito.models import Span, Trace

# Skip all tests in this module unless explicitly requested
pytestmark = pytest.mark.s3


def _s3_available() -> bool:
    """Check if MinIO is reachable."""
    try:
        import boto3
        client = boto3.client(
            "s3",
            endpoint_url=os.environ.get("BANDITO_S3_ENDPOINT", "http://localhost:9000"),
            aws_access_key_id=os.environ.get("AWS_ACCESS_KEY_ID", "bandito"),
            aws_secret_access_key=os.environ.get("AWS_SECRET_ACCESS_KEY", "banditodev"),
        )
        client.head_bucket(Bucket=os.environ.get("BANDITO_S3_BUCKET", "bandito-traces"))
        return True
    except Exception:
        return False


@pytest.fixture
def s3_store():
    """Create an S3Store with a unique prefix for test isolation."""
    if not _s3_available():
        pytest.skip("MinIO not available")

    from bandito.storage.s3 import S3Store

    prefix = f"test-{uuid.uuid4().hex[:8]}"
    store = S3Store(
        bucket=os.environ.get("BANDITO_S3_BUCKET", "bandito-traces"),
        prefix=prefix,
        endpoint_url=os.environ.get("BANDITO_S3_ENDPOINT", "http://localhost:9000"),
        aws_access_key_id=os.environ.get("AWS_ACCESS_KEY_ID", "bandito"),
        aws_secret_access_key=os.environ.get("AWS_SECRET_ACCESS_KEY", "banditodev"),
    )
    yield store

    # Cleanup: delete all objects under the test prefix
    import boto3
    client = boto3.client(
        "s3",
        endpoint_url=os.environ.get("BANDITO_S3_ENDPOINT", "http://localhost:9000"),
        aws_access_key_id=os.environ.get("AWS_ACCESS_KEY_ID", "bandito"),
        aws_secret_access_key=os.environ.get("AWS_SECRET_ACCESS_KEY", "banditodev"),
    )
    bucket = os.environ.get("BANDITO_S3_BUCKET", "bandito-traces")
    paginator = client.get_paginator("list_objects_v2")
    for page in paginator.paginate(Bucket=bucket, Prefix=prefix):
        for obj in page.get("Contents", []):
            client.delete_object(Bucket=bucket, Key=obj["Key"])


class TestWriteAndReadTrace:
    def test_write_and_get(self, s3_store):
        trace = Trace(project="test", trace_id="tr-1", input="hello")
        trace.spans.append(Span(name="llm", kind="llm"))
        s3_store.write_trace(trace)

        result = s3_store.get_trace("tr-1")
        assert result is not None
        assert result["trace_id"] == "tr-1"
        assert result["input"] == "hello"
        assert len(result["spans"]) == 1

    def test_get_missing(self, s3_store):
        assert s3_store.get_trace("nonexistent") is None

    def test_iter_traces(self, s3_store):
        for i in range(3):
            s3_store.write_trace(Trace(project="test", trace_id=f"tr-{i}"))

        traces = list(s3_store.iter_traces())
        assert len(traces) == 3


class TestDerivedMethods:
    def test_list_projects(self, s3_store):
        s3_store.write_trace(Trace(project="bot-a", trace_id="t-1"))
        s3_store.write_trace(Trace(project="bot-b", trace_id="t-2"))
        s3_store.write_trace(Trace(project="bot-a", trace_id="t-3"))

        assert s3_store.list_projects() == ["bot-a", "bot-b"]

    def test_write_dispatch(self, s3_store):
        trace = Trace(project="test", trace_id="tr-dispatch")
        s3_store.write(trace)
        assert s3_store.get_trace("tr-dispatch") is not None
