import httpx
from typing import Any, List, Dict, Optional
from .base import PIServiceBase
from .config import WebAPISettings

class PIWebAPIService(PIServiceBase):
    """
    Implementation of PI Service using PI Web API (REST).
    """

    def __init__(self, settings: WebAPISettings):
        self.settings = settings
        self.base_url = settings.PI_WEB_API_URL
        if not self.base_url:
            raise ValueError("PI_WEB_API_URL must be set in configuration")
            
        self.client = httpx.AsyncClient(
            auth=(settings.PI_WEB_API_USERNAME, settings.PI_WEB_API_PASSWORD) if settings.PI_WEB_API_USERNAME else None,
            verify=settings.PI_WEB_API_VERIFY_SSL,
            timeout=30.0
        )

    async def _get_data_server_web_id(self) -> str:
        """Helper to get the WebId of the PI Data Server."""
        url = f"{self.base_url}/dataservers"
        params = {"name": self.settings.PI_SERVER_NAME}
        response = await self.client.get(url, params=params)
        response.raise_for_status()
        items = response.json().get("Items", [])
        if not items:
            # Try searching all servers if exact name match fails
            response = await self.client.get(url)
            items = response.json().get("Items", [])
            for item in items:
                if item["Name"].lower() == self.settings.PI_SERVER_NAME.lower():
                    return item["WebId"]
            raise Exception(f"PI Data Server '{self.settings.PI_SERVER_NAME}' not found.")
        return items[0]["WebId"]

    async def _get_web_id(self, tag_name: str) -> str:
        """Helper to get WebID for a tag name with fallback."""
        try:
            path = f"\\\\{self.settings.PI_SERVER_NAME}\\{tag_name}"
            url = f"{self.base_url}/points"
            params = {"path": path}
            response = await self.client.get(url, params=params)
            if response.status_code == 200:
                return response.json().get("WebId")
        except Exception as e:
            pass

        try:
            url = f"{self.base_url}/search/query"
            params = {"q": f"name:\"{tag_name}\""}
            response = await self.client.get(url, params=params)
            if response.status_code == 200:
                items = response.json().get("Items", [])
                if items:
                    return items[0]["WebId"]
        except Exception as e:
            pass
            
        raise Exception(f"Tag '{tag_name}' not found via PI Web API.")

    async def get_current_value(self, tag_name: str) -> Dict[str, Any]:
        """Fetch current value via PI Web API."""
        web_id = await self._get_web_id(tag_name)
        url = f"{self.base_url}/streams/{web_id}/value"
        response = await self.client.get(url)
        response.raise_for_status()
        
        data = response.json()
        return {
            "tag": tag_name,
            "value": data.get("Value"),
            "timestamp": data.get("Timestamp"),
            "provider": "WebAPI"
        }

    async def get_recorded_values(self, tag_name: str, start_time: str, end_time: str) -> List[Dict[str, Any]]:
        """Fetch historical values via PI Web API."""
        web_id = await self._get_web_id(tag_name)
        url = f"{self.base_url}/streams/{web_id}/recorded"
        params = {"startTime": start_time, "endTime": end_time}
        response = await self.client.get(url, params=params)
        response.raise_for_status()
        
        items = response.json().get("Items", [])
        return [
            {"timestamp": item.get("Timestamp"), "value": item.get("Value")}
            for item in items
        ]

    async def write_value(self, tag_name: str, value: Any, timestamp: Any = None) -> bool:
        """Write value via PI Web API."""
        try:
            web_id = await self._get_web_id(tag_name)
            url = f"{self.base_url}/streams/{web_id}/value"
            data = {"Timestamp": timestamp or "*", "Value": value}
            response = await self.client.post(url, json=data)
            return response.status_code in [200, 202, 204]
        except:
            return False

    async def search_tags(self, query: str) -> List[str]:
        """Search tags via PI Web API using nameFilter."""
        try:
            server_web_id = await self._get_data_server_web_id()
            url = f"{self.base_url}/dataservers/{server_web_id}/points"
            params = {"nameFilter": query}
            response = await self.client.get(url, params=params)
            response.raise_for_status()
            
            items = response.json().get("Items", [])
            return [item.get("Name") for item in items]
        except Exception as e:
            return []

    async def _get_asset_server_web_id(self, server_name: str) -> str:
        """Helper to get the WebId of a PI AF Asset Server."""
        url = f"{self.base_url}/assetservers"
        params = {"name": server_name}
        response = await self.client.get(url, params=params)
        response.raise_for_status()
        webid = None
        # PI Web API might return a list of items or a single item depending on response structure
        data = response.json()
        if "Items" in data:
            for item in data["Items"]:
                 if item["Name"].lower() == server_name.lower():
                     return item["WebId"]
        elif data.get("Name", "").lower() == server_name.lower():
            return data["WebId"]

        raise Exception(f"PI AF Asset Server '{server_name}' not found.")

    async def get_asset_servers(self) -> List[str]:
        """Fetch PI AF Asset Server names via PI Web API."""
        url = f"{self.base_url}/assetservers"
        response = await self.client.get(url)
        response.raise_for_status()
        items = response.json().get("Items", [])
        return [item.get("Name") for item in items]

    async def get_organizations(self, asset_server: str) -> List[str]:
        """Fetch AF Database names (Organizations) for a given Asset Server."""
        server_web_id = await self._get_asset_server_web_id(asset_server)
        url = f"{self.base_url}/assetservers/{server_web_id}/assetdatabases"
        response = await self.client.get(url)
        response.raise_for_status()
        items = response.json().get("Items", [])
        return [item.get("Name") for item in items]

    async def test_connection(self) -> Dict[str, Any]:
        """Test PI Web API connection."""
        try:
            url = f"{self.base_url}/system/status"
            response = await self.client.get(url)
            if response.status_code == 200:
                return {
                    "status": "connected", 
                    "type": "WebAPI", 
                    "url": self.base_url,
                    "server_time": response.json().get("ServerTime")
                }
            return {"status": "error", "code": response.status_code}
        except Exception as e:
            return {"status": "failed", "error": str(e)}

    async def close(self):
        """Close the httpx client."""
        await self.client.aclose()
