"""Tahoe-LAFS client operations for RedundaNet."""

from __future__ import annotations

from dataclasses import dataclass
from pathlib import Path
from typing import Any

from jinja2 import Template

from redundanet.core.config import TahoeConfig
from redundanet.core.exceptions import StorageError
from redundanet.utils.files import ensure_dir, write_file
from redundanet.utils.logging import get_logger
from redundanet.utils.process import is_command_available, run_command

logger = get_logger(__name__)

TAHOE_CLIENT_CFG_TEMPLATE = """# Tahoe-LAFS client configuration
# Generated by RedundaNet

[node]
nickname = {{ nickname }}
web.port = tcp:{{ web_port }}:interface=127.0.0.1
tub.port = tcp:{{ tub_port }}
tub.location = {{ tub_location }}

[client]
introducer.furl = {{ introducer_furl }}
shares.needed = {{ shares_needed }}
shares.happy = {{ shares_happy }}
shares.total = {{ shares_total }}

[storage]
enabled = false

[helper]
enabled = false
"""


@dataclass
class TahoeClientConfig:
    """Configuration for Tahoe-LAFS client."""

    nickname: str
    node_dir: Path
    introducer_furl: str
    web_port: int = 3456
    tub_port: int = 34560
    tub_location: str = "AUTO"
    shares_needed: int = 3
    shares_happy: int = 7
    shares_total: int = 10

    @classmethod
    def from_tahoe_config(
        cls,
        nickname: str,
        introducer_furl: str,
        tahoe_config: TahoeConfig,
        node_dir: Path | None = None,
    ) -> TahoeClientConfig:
        """Create client config from TahoeConfig."""
        return cls(
            nickname=nickname,
            node_dir=node_dir or Path("/var/lib/tahoe-client"),
            introducer_furl=introducer_furl,
            shares_needed=tahoe_config.shares_needed,
            shares_happy=tahoe_config.shares_happy,
            shares_total=tahoe_config.shares_total,
        )


class TahoeClient:
    """Manages Tahoe-LAFS client node."""

    def __init__(self, config: TahoeClientConfig) -> None:
        self.config = config
        self._tahoe_cfg = config.node_dir / "tahoe.cfg"

    def ensure_tahoe_installed(self) -> bool:
        """Check if Tahoe-LAFS is installed."""
        if not is_command_available("tahoe"):
            raise StorageError("Tahoe-LAFS is not installed. Please install tahoe-lafs first.")
        return True

    def create_node(self) -> None:
        """Create a new Tahoe-LAFS client node."""
        logger.info("Creating Tahoe client node", nickname=self.config.nickname)

        self.ensure_tahoe_installed()
        ensure_dir(self.config.node_dir, mode=0o700)

        # Create the node using tahoe command
        result = run_command(
            f"tahoe create-client --basedir={self.config.node_dir} "
            f"--nickname={self.config.nickname}",
            check=False,
        )

        if not result.success and "already exists" not in result.stderr:
            raise StorageError(f"Failed to create client node: {result.stderr}")

        # Write custom configuration
        self._write_config()

        logger.info("Tahoe client node created", node_dir=str(self.config.node_dir))

    def _write_config(self) -> None:
        """Write tahoe.cfg configuration file."""
        template = Template(TAHOE_CLIENT_CFG_TEMPLATE)
        content = template.render(
            nickname=self.config.nickname,
            web_port=self.config.web_port,
            tub_port=self.config.tub_port,
            tub_location=self.config.tub_location,
            introducer_furl=self.config.introducer_furl,
            shares_needed=self.config.shares_needed,
            shares_happy=self.config.shares_happy,
            shares_total=self.config.shares_total,
        )

        write_file(self._tahoe_cfg, content, mode=0o600)
        logger.debug("Wrote tahoe.cfg", path=str(self._tahoe_cfg))

    def start(self) -> bool:
        """Start the Tahoe client node."""
        logger.info("Starting Tahoe client", nickname=self.config.nickname)

        result = run_command(f"tahoe start {self.config.node_dir}", check=False)

        if not result.success:
            logger.error("Failed to start Tahoe client", error=result.stderr)
            return False

        logger.info("Tahoe client started")
        return True

    def stop(self) -> bool:
        """Stop the Tahoe client node."""
        logger.info("Stopping Tahoe client", nickname=self.config.nickname)

        result = run_command(f"tahoe stop {self.config.node_dir}", check=False)

        if not result.success:
            logger.error("Failed to stop Tahoe client", error=result.stderr)
            return False

        logger.info("Tahoe client stopped")
        return True

    def restart(self) -> bool:
        """Restart the Tahoe client node."""
        result = run_command(f"tahoe restart {self.config.node_dir}", check=False)
        return result.success

    def is_running(self) -> bool:
        """Check if the Tahoe client is running."""
        result = run_command(f"tahoe status {self.config.node_dir}", check=False)
        return result.success and "RUNNING" in result.stdout

    def get_node_url(self) -> str:
        """Get the web UI URL for this node."""
        return f"http://127.0.0.1:{self.config.web_port}"

    def upload_file(self, local_path: Path, format_: str = "CHK") -> str:
        """Upload a file to the grid.

        Args:
            local_path: Path to the file to upload
            format_: Upload format (CHK, SDMF, MDMF)

        Returns:
            The capability string for the uploaded file
        """
        if not local_path.exists():
            raise StorageError(f"File not found: {local_path}")

        result = run_command(
            f"tahoe put --format={format_} {local_path}",
            cwd=self.config.node_dir,
            check=False,
        )

        if not result.success:
            raise StorageError(f"Upload failed: {result.stderr}")

        # The capability is the last line of output
        return result.stdout.strip().split("\n")[-1]

    def download_file(self, cap: str, local_path: Path) -> Path:
        """Download a file from the grid.

        Args:
            cap: The capability string
            local_path: Where to save the file

        Returns:
            Path to the downloaded file
        """
        ensure_dir(local_path.parent)

        result = run_command(
            f"tahoe get {cap} {local_path}",
            cwd=self.config.node_dir,
            check=False,
        )

        if not result.success:
            raise StorageError(f"Download failed: {result.stderr}")

        return local_path

    def list_directory(self, cap: str) -> list[dict[str, Any]]:
        """List contents of a directory capability.

        Args:
            cap: Directory capability

        Returns:
            List of entries with name, type, and size
        """
        result = run_command(
            f"tahoe ls --json {cap}",
            cwd=self.config.node_dir,
            check=False,
        )

        if not result.success:
            raise StorageError(f"List failed: {result.stderr}")

        import json

        try:
            data = json.loads(result.stdout)
            entries = []
            for name, info in data.get("children", {}).items():
                entry_type, metadata = info
                entries.append(
                    {
                        "name": name,
                        "type": entry_type,
                        "size": metadata.get("size", 0),
                        "mutable": metadata.get("mutable", False),
                    }
                )
            return entries
        except json.JSONDecodeError:
            return []

    def check_file(self, cap: str, repair: bool = False) -> dict[str, Any]:
        """Check the health of a file.

        Args:
            cap: Capability to check
            repair: Whether to repair if unhealthy

        Returns:
            Health check results
        """
        cmd = "tahoe check"
        if repair:
            cmd += " --repair"
        cmd += f" {cap}"

        result = run_command(cmd, cwd=self.config.node_dir, check=False)

        return {
            "healthy": result.success,
            "output": result.stdout,
        }

    def mount_filesystem(self, mountpoint: Path, cap: str | None = None) -> bool:
        """Mount the Tahoe filesystem using FUSE.

        Args:
            mountpoint: Where to mount
            cap: Optional root capability (uses alias if not provided)

        Returns:
            True if mount succeeded
        """
        ensure_dir(mountpoint)

        cmd = f"tahoe start {self.config.node_dir}"
        if cap:
            cmd += f" --mountpoint={mountpoint} --cap={cap}"
        else:
            cmd += f" --mountpoint={mountpoint}"

        # Note: tahoe-lafs FUSE mounting is complex, this is simplified
        result = run_command(cmd, check=False)
        return result.success

    def unmount_filesystem(self, mountpoint: Path) -> bool:
        """Unmount the Tahoe filesystem.

        Args:
            mountpoint: Mountpoint to unmount

        Returns:
            True if unmount succeeded
        """
        result = run_command(f"fusermount -u {mountpoint}", check=False)
        return result.success
