"""SharedWorkspace - Agent 间共享工作空间

基于 Redis 的共享状态存储，支持读写、订阅、锁操作。
"""

from __future__ import annotations

import json
import logging
import time
from typing import Any

logger = logging.getLogger(__name__)


class SharedWorkspace:
    """Agent 间共享工作空间

    基于 Redis 的共享状态存储，支持：
    - write/read: 读写共享数据
    - lock/unlock: 分布式锁
    - 版本控制：每次写入递增版本号
    """

    def __init__(self, redis_client: Any = None, prefix: str = "workspace"):
        """
        Args:
            redis_client: aioredis.Redis 实例，None 时使用内存字典
            prefix: Redis key 前缀
        """
        self._redis = redis_client
        self._prefix = prefix
        self._local_store: dict[str, dict[str, Any]] = {}
        self._locks: dict[str, str] = {}  # key -> lock_owner

    def _make_key(self, key: str) -> str:
        return f"{self._prefix}:{key}"

    async def write(
        self, key: str, value: Any, agent_id: str, ttl: int | None = None
    ) -> int:
        """写入共享数据

        Args:
            key: 数据键
            value: 数据值
            agent_id: 写入者 ID
            ttl: 过期时间（秒），None 表示不过期

        Returns:
            版本号
        """
        entry = {
            "value": value,
            "agent_id": agent_id,
            "version": await self._get_version(key) + 1,
            "timestamp": time.time(),
        }

        if self._redis:
            redis_key = self._make_key(key)
            data = json.dumps(entry, default=str)
            if ttl:
                await self._redis.setex(redis_key, ttl, data)
            else:
                await self._redis.set(redis_key, data)
        else:
            self._local_store[key] = entry

        return entry["version"]

    async def read(self, key: str) -> dict[str, Any] | None:
        """读取共享数据

        Returns:
            {"value": ..., "agent_id": ..., "version": ..., "timestamp": ...} 或 None
        """
        if self._redis:
            redis_key = self._make_key(key)
            data = await self._redis.get(redis_key)
            if data is None:
                return None
            return json.loads(data)
        else:
            return self._local_store.get(key)

    async def delete(self, key: str) -> bool:
        """删除共享数据"""
        if self._redis:
            redis_key = self._make_key(key)
            result = await self._redis.delete(redis_key)
            return result > 0
        else:
            return self._local_store.pop(key, None) is not None

    async def lock(self, key: str, agent_id: str, timeout: float = 30.0) -> bool:
        """获取分布式锁

        Args:
            key: 要锁定的数据键
            agent_id: 请求锁的 Agent ID
            timeout: 锁超时时间（秒）

        Returns:
            是否成功获取锁
        """
        lock_key = f"{self._prefix}:lock:{key}"

        if self._redis:
            # Redis SET with NX (only if not exists) and EX (expiry)
            result = await self._redis.set(lock_key, agent_id, nx=True, ex=int(timeout))
            return result is not None
        else:
            if key in self._locks:
                return False
            self._locks[key] = agent_id
            return True

    async def unlock(self, key: str, agent_id: str) -> bool:
        """释放分布式锁

        只有锁的持有者才能释放锁。
        """
        lock_key = f"{self._prefix}:lock:{key}"

        if self._redis:
            current_owner = await self._redis.get(lock_key)
            if current_owner and current_owner.decode() == agent_id:
                await self._redis.delete(lock_key)
                return True
            return False
        else:
            if self._locks.get(key) == agent_id:
                del self._locks[key]
                return True
            return False

    async def _get_version(self, key: str) -> int:
        """获取当前版本号"""
        data = await self.read(key)
        if data is None:
            return 0
        return data.get("version", 0)

    async def list_keys(self) -> list[str]:
        """列出所有键"""
        if self._redis:
            pattern = f"{self._prefix}:*"
            keys = []
            async for key in self._redis.scan_iter(match=pattern):
                # Strip prefix
                k = key.decode() if isinstance(key, bytes) else key
                k = k[len(self._prefix) + 1:]  # Remove "prefix:"
                # Skip lock keys
                if not k.startswith("lock:"):
                    keys.append(k)
            return keys
        else:
            return list(self._local_store.keys())
