"""Configuration writer for Flow SDK.

Persists configuration to disk and stores secrets securely in the system keychain.
"""

import os
import tempfile
from pathlib import Path
from typing import Any, Optional, Protocol

import keyring
import yaml

from flow.api.models import ValidationResult


class Keychain(Protocol):
    """Protocol for keychain/keyring operations."""

    def set_password(self, service: str, username: str, password: str) -> None:
        """Store a password in the keychain."""
        ...

    def get_password(self, service: str, username: str) -> Optional[str]:
        """Retrieve a password from the keychain."""
        ...


class ConfigWriter:
    """Writes Flow configuration securely.

    Features:
    - API keys stored in system keychain
    - Atomic file writes with rollback
    - Proper file permissions (0600)
    """

    KEYCHAIN_SERVICE = "com.mlfoundry.flow"
    KEYCHAIN_USERNAME = "api_key"
    # Legacy support
    LEGACY_KEYCHAIN_SERVICE = "com.mlfoundry.flow"
    LEGACY_KEYCHAIN_USERNAME = "api_key"

    def __init__(self, keychain: Optional[Keychain] = None, config_path: Optional[Path] = None):
        """Initialize writer.

        Args:
            keychain: Keychain implementation (defaults to system keyring)
            config_path: Path to config file (defaults to ~/.flow/config.yaml)
        """
        self.keychain = keychain or keyring.get_keyring()
        self.config_path = config_path or Path.home() / ".flow" / "config.yaml"

    def write(self, config: dict[str, Any], validation: ValidationResult) -> None:
        """Write configuration to disk and keychain.

        Args:
            config: Configuration dictionary
            validation: Validation result (for future use)

        Raises:
            OSError: If unable to write configuration
        """
        # Extract API key before writing to file
        api_key = config.pop("api_key", None)

        # Transform to new config format if provider is specified
        provider = config.get("provider")
        if provider:
            # New provider-based format
            file_config = {"provider": provider}

            # Move provider-specific settings
            if provider == "fcp":
                fcp_config = {}
                if "project" in config:
                    fcp_config["project"] = config.pop("project")
                if "region" in config:
                    fcp_config["region"] = config.pop("region")
                if "api_url" in config:
                    fcp_config["api_url"] = config.pop("api_url")
                if "default_ssh_key" in config:
                    fcp_config["ssh_keys"] = [config.pop("default_ssh_key")]

                if fcp_config:
                    file_config["fcp"] = fcp_config

            # Any remaining fields go at top level (for extensibility)
            file_config.update(config)
        else:
            # Legacy format - write as-is
            file_config = config

        # Ensure directory exists
        self.config_path.parent.mkdir(parents=True, exist_ok=True)

        # Write config file atomically
        self._write_config_file(file_config)

        # Store API key in keychain
        if api_key:
            try:
                self.keychain.set_password(self.KEYCHAIN_SERVICE, self.KEYCHAIN_USERNAME, api_key)
            except Exception as e:
                # If keychain fails, warn but don't fail entirely
                print(f"Warning: Could not store API key in keychain: {e}", file=os.sys.stderr)
                print(
                    "API key will need to be provided via FCP_API_KEY environment variable",
                    file=os.sys.stderr,
                )

    def read_api_key(self) -> Optional[str]:
        """Read API key from keychain.

        Returns:
            API key if found, None otherwise
        """
        try:
            return self.keychain.get_password(self.KEYCHAIN_SERVICE, self.KEYCHAIN_USERNAME)
        except Exception:
            return None

    def _write_config_file(self, config: dict[str, Any]) -> None:
        """Write config file atomically with proper permissions.

        Handles both legacy flat format and new provider-based format.

        Args:
            config: Configuration dictionary (without api_key)

        Raises:
            OSError: If unable to write file
        """
        # Write to temporary file first
        with tempfile.NamedTemporaryFile(
            mode="w", dir=self.config_path.parent, delete=False
        ) as tmp:
            yaml.dump(config, tmp, default_flow_style=False, sort_keys=False)
            tmp_path = Path(tmp.name)

        # Set proper permissions (0600 - read/write for owner only)
        try:
            tmp_path.chmod(0o600)
        except Exception:
            # Windows may not support chmod, continue anyway
            pass

        # Atomic rename
        try:
            tmp_path.replace(self.config_path)
        except Exception:
            # Clean up temp file on failure
            tmp_path.unlink(missing_ok=True)
            raise
