from __future__ import annotations

import io
import logging

from muleline.storage.base import StorageBackend

log = logging.getLogger(__name__)


class SFTPStorage(StorageBackend):
    """SFTP storage backend. Maintains a persistent connection with auto-reconnect."""

    def __init__(
        self,
        host: str,
        username: str,
        password: str = "",
        key_path: str = "",
        port: int = 22,
        base_path: str = "/",
    ):
        try:
            import paramiko
        except ImportError:
            raise ImportError("Install paramiko for SFTP storage: pip install muleline[sftp]")

        self._paramiko = paramiko
        self.host = host
        self.username = username
        self.password = password
        self.key_path = key_path
        self.port = port
        self.base_path = base_path.rstrip("/")

        self._transport: paramiko.Transport | None = None
        self._sftp: paramiko.SFTPClient | None = None
        self._connect()

    def _connect(self) -> None:
        paramiko = self._paramiko
        transport = paramiko.Transport((self.host, self.port))
        if self.key_path:
            key = paramiko.RSAKey.from_private_key_file(self.key_path)
            transport.connect(username=self.username, pkey=key)
        else:
            transport.connect(username=self.username, password=self.password)
        self._transport = transport
        self._sftp = paramiko.SFTPClient.from_transport(transport)

    def _sftp_client(self) -> "paramiko.SFTPClient":
        if self._transport is None or not self._transport.is_active():
            log.info("SFTP reconnecting to %s", self.host)
            self._connect()
        return self._sftp

    def _remote(self, key: str) -> str:
        return f"{self.base_path}/{key}"

    def _mkdir_p(self, sftp: "paramiko.SFTPClient", remote_dir: str) -> None:
        parts = remote_dir.split("/")
        path = ""
        for part in parts:
            if not part:
                path = "/"
                continue
            path = f"{path}/{part}" if path != "/" else f"/{part}"
            try:
                sftp.stat(path)
            except FileNotFoundError:
                sftp.mkdir(path)

    def save(self, key: str, content: bytes, mime_type: str = "") -> bool:
        try:
            sftp = self._sftp_client()
            remote = self._remote(key)
            parent = remote.rsplit("/", 1)[0]
            self._mkdir_p(sftp, parent)
            with sftp.open(remote, "wb") as f:
                f.write(content)
            return True
        except Exception as e:
            log.error("SFTP save failed for %s: %s", key, e)
            return False

    def read(self, key: str) -> bytes | None:
        try:
            sftp = self._sftp_client()
            buf = io.BytesIO()
            sftp.getfo(self._remote(key), buf)
            return buf.getvalue()
        except FileNotFoundError:
            return None
        except Exception as e:
            log.error("SFTP read failed for %s: %s", key, e)
            return None

    def delete(self, key: str) -> bool:
        try:
            self._sftp_client().remove(self._remote(key))
            return True
        except FileNotFoundError:
            return False
        except Exception as e:
            log.error("SFTP delete failed for %s: %s", key, e)
            return False

    def exists(self, key: str) -> bool:
        try:
            self._sftp_client().stat(self._remote(key))
            return True
        except FileNotFoundError:
            return False
        except Exception:
            return False

    def url(self, key: str, expires: int = 3600) -> str | None:
        return None
