"""
Storage backends for artifact management (re-exports from core.backends.storage).

Legacy ArtifactStorage helper for backward compatibility.
"""

from __future__ import annotations

import io
import json
import os
import uuid
from typing import Any

# Re-export unified storage backends
from .backends.storage import InMemoryStorage, MinIOStorage


class ArtifactStorage:
    """
    Legacy artifact storage helper (v3 compatibility).
    
    Wraps the new backends for backward compatibility.
    Prefer using MinIOStorage or InMemoryStorage directly from core.backends.storage.
    """
    def __init__(self, backend: str = "local", **kwargs):
        self.backend = backend
        if backend == "minio":
            from minio import Minio  # type: ignore
            self.client = Minio(
                kwargs["endpoint"],
                access_key=kwargs.get("key"),
                secret_key=kwargs.get("secret"),
                secure=bool(kwargs.get("secure", False)),
            )
            self.bucket = kwargs.get("bucket", "evenage-artifacts")
            if not self.client.bucket_exists(self.bucket):
                self.client.make_bucket(self.bucket)
        else:
            self.path = kwargs.get("path", "./artifacts")
            os.makedirs(self.path, exist_ok=True)

    def put(self, data: Any) -> str:
        key = f"{uuid.uuid4()}.json"
        if self.backend == "local":
            target = os.path.join(self.path, key)
            with open(target, "w", encoding="utf-8") as f:
                json.dump(data, f)
            return f"file://{os.path.abspath(target)}"
        else:
            body = json.dumps(data).encode()
            self.client.put_object(self.bucket, key, io.BytesIO(body), len(body))
            return f"s3://{self.bucket}/{key}"


# Keep the rest of the async storage implementations below (MinIOStorage, InMemoryStorage)
# Note: These are duplicated from backends; the canonical versions are in core.backends.storage
"""
Storage backends for artifact management.

Provides MinIO/S3-compatible storage for large payloads and artifacts.
"""

import asyncio
from datetime import datetime
import io


class MinIOStorage:
    """MinIO/S3-compatible storage backend."""

    def __init__(
        self,
        endpoint: str,
        access_key: str,
        secret_key: str,
        secure: bool = False
    ):
        """
        Initialize MinIO storage.
        
        Args:
            endpoint: MinIO endpoint (e.g., "minio:9000")
            access_key: Access key
            secret_key: Secret key
            secure: Use HTTPS
        """
        self.endpoint = endpoint
        self.access_key = access_key
        self.secret_key = secret_key
        self.secure = secure
        self._client = None
        self._async_client = None

    def _get_client(self):
        """Get synchronous MinIO client."""
        if self._client is None:
            try:
                from minio import Minio
                self._client = Minio(
                    self.endpoint,
                    access_key=self.access_key,
                    secret_key=self.secret_key,
                    secure=self.secure
                )
            except ImportError:
                raise RuntimeError(
                    "minio package not installed. Install with: pip install minio"
                )
        return self._client

    async def ensure_bucket(self, bucket: str):
        """Ensure bucket exists, create if not."""
        def _ensure():
            client = self._get_client()
            if not client.bucket_exists(bucket):
                client.make_bucket(bucket)

        await asyncio.get_event_loop().run_in_executor(None, _ensure)

    async def put_object(
        self,
        bucket: str,
        key: str,
        data: bytes,
        metadata: dict | None = None
    ) -> bool:
        """
        Store object in MinIO.
        
        Args:
            bucket: Bucket name
            key: Object key/path
            data: Data bytes
            metadata: Optional metadata
        
        Returns:
            True if successful
        """
        await self.ensure_bucket(bucket)

        def _put():
            client = self._get_client()
            client.put_object(
                bucket,
                key,
                io.BytesIO(data),
                length=len(data),
                metadata=metadata
            )
            return True

        return await asyncio.get_event_loop().run_in_executor(None, _put)

    async def get_object(self, bucket: str, key: str) -> bytes:
        """
        Retrieve object from MinIO.
        
        Args:
            bucket: Bucket name
            key: Object key/path
        
        Returns:
            Object data as bytes
        """
        def _get():
            client = self._get_client()
            response = client.get_object(bucket, key)
            data = response.read()
            response.close()
            response.release_conn()
            return data

        return await asyncio.get_event_loop().run_in_executor(None, _get)

    async def list_objects(
        self,
        bucket: str,
        prefix: str = "",
        max_keys: int = 1000
    ) -> list[dict]:
        """
        List objects in bucket.
        
        Args:
            bucket: Bucket name
            prefix: Optional prefix filter
            max_keys: Max number of objects
        
        Returns:
            List of object metadata dicts
        """
        def _list():
            client = self._get_client()
            objects = []

            for obj in client.list_objects(bucket, prefix=prefix):
                objects.append({
                    "key": obj.object_name,
                    "size": obj.size,
                    "last_modified": obj.last_modified.isoformat() if obj.last_modified else None,
                    "etag": obj.etag
                })

                if len(objects) >= max_keys:
                    break

            return objects

        return await asyncio.get_event_loop().run_in_executor(None, _list)

    async def delete_object(self, bucket: str, key: str) -> bool:
        """
        Delete object from MinIO.
        
        Args:
            bucket: Bucket name
            key: Object key/path
        
        Returns:
            True if successful
        """
        def _delete():
            client = self._get_client()
            client.remove_object(bucket, key)
            return True

        return await asyncio.get_event_loop().run_in_executor(None, _delete)

    async def object_exists(self, bucket: str, key: str) -> bool:
        """
        Check if object exists.
        
        Args:
            bucket: Bucket name
            key: Object key/path
        
        Returns:
            True if object exists
        """
        def _exists():
            client = self._get_client()
            try:
                client.stat_object(bucket, key)
                return True
            except Exception:
                return False

        return await asyncio.get_event_loop().run_in_executor(None, _exists)


class InMemoryStorage:
    """In-memory storage backend for testing."""

    def __init__(self):
        """Initialize in-memory storage."""
        self._storage: dict[str, dict[str, tuple[bytes, dict]]] = {}

    async def ensure_bucket(self, bucket: str):
        """Ensure bucket exists."""
        if bucket not in self._storage:
            self._storage[bucket] = {}

    async def put_object(
        self,
        bucket: str,
        key: str,
        data: bytes,
        metadata: dict | None = None
    ) -> bool:
        """Store object in memory."""
        await self.ensure_bucket(bucket)
        self._storage[bucket][key] = (data, metadata or {})
        return True

    async def get_object(self, bucket: str, key: str) -> bytes:
        """Retrieve object from memory."""
        if bucket not in self._storage or key not in self._storage[bucket]:
            raise FileNotFoundError(f"Object {bucket}/{key} not found")
        return self._storage[bucket][key][0]

    async def list_objects(
        self,
        bucket: str,
        prefix: str = "",
        max_keys: int = 1000
    ) -> list[dict]:
        """List objects in memory."""
        if bucket not in self._storage:
            return []

        objects = []
        for key, (data, metadata) in self._storage[bucket].items():
            if key.startswith(prefix):
                objects.append({
                    "key": key,
                    "size": len(data),
                    "last_modified": datetime.utcnow().isoformat(),
                    "etag": str(hash(data))
                })

                if len(objects) >= max_keys:
                    break

        return objects

    async def delete_object(self, bucket: str, key: str) -> bool:
        """Delete object from memory."""
        if bucket in self._storage and key in self._storage[bucket]:
            del self._storage[bucket][key]
            return True
        return False

    async def object_exists(self, bucket: str, key: str) -> bool:
        """Check if object exists."""
        return bucket in self._storage and key in self._storage[bucket]
