import httpx
import tenacity

from openhands.storage.files import FileStore
from openhands.utils.async_utils import EXECUTOR


class WebHookFileStore(FileStore):
    """File store which includes a web hook to be invoked after any changes occur.

    This class wraps another FileStore implementation and sends HTTP requests
    to a specified URL whenever files are written or deleted.

    Attributes:
        file_store: The underlying FileStore implementation
        base_url: The base URL for webhook requests
        client: The HTTP client used to make webhook requests
    """

    file_store: FileStore
    base_url: str
    client: httpx.Client

    def __init__(
        self, file_store: FileStore, base_url: str, client: httpx.Client | None = None
    ):
        """Initialize a WebHookFileStore.

        Args:
            file_store: The underlying FileStore implementation
            base_url: The base URL for webhook requests
            client: Optional HTTP client to use for requests. If None, a new client will be created.
        """
        self.file_store = file_store
        self.base_url = base_url
        if client is None:
            client = httpx.Client()
        self.client = client

    def write(self, path: str, contents: str | bytes) -> None:
        """Write contents to a file and trigger a webhook.

        Args:
            path: The path to write to
            contents: The contents to write
        """
        self.file_store.write(path, contents)
        EXECUTOR.submit(self._on_write, path, contents)

    def read(self, path: str) -> str:
        """Read contents from a file.

        Args:
            path: The path to read from

        Returns:
            The contents of the file
        """
        return self.file_store.read(path)

    def list(self, path: str) -> list[str]:
        """List files in a directory.

        Args:
            path: The directory path to list

        Returns:
            A list of file paths
        """
        return self.file_store.list(path)

    def delete(self, path: str) -> None:
        """Delete a file and trigger a webhook.

        Args:
            path: The path to delete
        """
        self.file_store.delete(path)
        EXECUTOR.submit(self._on_delete, path)

    @tenacity.retry(
        wait=tenacity.wait_fixed(1),
        stop=tenacity.stop_after_attempt(3),
    )
    def _on_write(self, path: str, contents: str | bytes) -> None:
        """Send a POST request to the webhook URL when a file is written.

        This method is retried up to 3 times with a 1-second delay between attempts.

        Args:
            path: The path that was written to
            contents: The contents that were written

        Raises:
            httpx.HTTPStatusError: If the webhook request fails
        """
        base_url = self.base_url + path
        response = self.client.post(base_url, content=contents)
        response.raise_for_status()

    @tenacity.retry(
        wait=tenacity.wait_fixed(1),
        stop=tenacity.stop_after_attempt(3),
    )
    def _on_delete(self, path: str) -> None:
        """Send a DELETE request to the webhook URL when a file is deleted.

        This method is retried up to 3 times with a 1-second delay between attempts.

        Args:
            path: The path that was deleted

        Raises:
            httpx.HTTPStatusError: If the webhook request fails
        """
        base_url = self.base_url + path
        response = self.client.delete(base_url)
        response.raise_for_status()
