"""Security module for verified file write operations.

This module ensures that file writes only occur when explicitly allowed by configuration,
maintaining the encryption-at-rest security model.
"""

import json
from pathlib import Path
from typing import Any

from loguru import logger

# Keys that should never be written to disk in clear text
SENSITIVE_KEYS = frozenset(
    {
        "password",
        "api_key",
        "apikey",
        "api-key",
        "secret",
        "secret_key",
        "secretkey",
        "token",
        "access_token",
        "refresh_token",
        "private_key",
        "privatekey",
        "credentials",
        "auth",
        "authorization",
    }
)


def _sanitize_sensitive_data(data: Any) -> Any:
    """Remove sensitive keys from data before writing to disk.

    Args:
        data: Data to sanitize (dict, list, or primitive)

    Returns:
        Sanitized copy of the data with sensitive keys redacted
    """
    if isinstance(data, dict):
        result = {}
        for key, value in data.items():
            key_lower = key.lower() if isinstance(key, str) else key
            if key_lower in SENSITIVE_KEYS:
                result[key] = "[REDACTED]"
            else:
                result[key] = _sanitize_sensitive_data(value)
        return result
    if isinstance(data, list):
        return [_sanitize_sensitive_data(item) for item in data]
    return data


class FileWriteSecurityError(Exception):
    """Raised when a file write operation is not allowed by security settings."""

    pass


def write_file_verified(
    filepath: str | Path,
    content: str,
    setting_name: str,
    required_value: Any = True,
    context: str = "",
    mode: str = "w",
    encoding: str = "utf-8",
    settings_snapshot: dict = None,
) -> None:
    """Write content to a file only if security settings allow it.

    Args:
        filepath: Path to the file to write
        content: Content to write to the file
        setting_name: Configuration setting name to check (e.g., "api.allow_file_output")
        required_value: Required value for the setting (default: True)
        context: Description of what's being written (for error messages)
        mode: File open mode (default: "w")
        encoding: File encoding (default: "utf-8")
        settings_snapshot: Optional settings snapshot for programmatic mode

    Raises:
        FileWriteSecurityError: If the security setting doesn't match required value

    Example:
        >>> write_file_verified(
        ...     "report.md",
        ...     markdown_content,
        ...     "api.allow_file_output",
        ...     context="API research report"
        ... )
    """
    from ..config.search_config import get_setting_from_snapshot

    try:
        actual_value = get_setting_from_snapshot(
            setting_name, settings_snapshot=settings_snapshot
        )
    except Exception:
        # Setting doesn't exist - default deny
        actual_value = None

    if actual_value != required_value:
        error_msg = (
            f"File write not allowed: {context or 'file operation'}. "
            f"Set '{setting_name}={required_value}' in config to enable this feature."
        )
        logger.warning(error_msg)
        raise FileWriteSecurityError(error_msg)

    # Don't pass encoding for binary mode
    # Note: This function writes non-sensitive data (PDFs, reports) after security check.
    # CodeQL false positive: content is PDF binary or markdown, not passwords.
    if "b" in mode:
        with open(filepath, mode) as f:  # nosec B603
            f.write(content)
    else:
        with open(filepath, mode, encoding=encoding) as f:  # nosec B603
            f.write(content)

    logger.debug(
        f"Verified file write: {filepath} (setting: {setting_name}={required_value})"
    )


def write_json_verified(
    filepath: str | Path,
    data: dict | list,
    setting_name: str,
    required_value: Any = True,
    context: str = "",
    settings_snapshot: dict = None,
    **json_kwargs,
) -> None:
    """Write JSON data to a file only if security settings allow it.

    Args:
        filepath: Path to the file to write
        data: Dictionary or list to serialize as JSON
        setting_name: Configuration setting name to check
        required_value: Required value for the setting (default: True)
        context: Description of what's being written (for error messages)
        settings_snapshot: Optional settings snapshot for programmatic mode
        **json_kwargs: Additional keyword arguments to pass to json.dumps()
                      (e.g., indent=2, ensure_ascii=False, sort_keys=True, default=custom_serializer)

    Raises:
        FileWriteSecurityError: If the security setting doesn't match required value

    Example:
        >>> write_json_verified(
        ...     "results.json",
        ...     {"accuracy": 0.95},
        ...     "benchmark.allow_file_output",
        ...     context="benchmark results",
        ...     indent=2,
        ...     sort_keys=True
        ... )
    """
    # Default to indent=2 if not specified for readability
    if "indent" not in json_kwargs:
        json_kwargs["indent"] = 2

    # Sanitize sensitive data before writing to disk
    sanitized_data = _sanitize_sensitive_data(data)
    content = json.dumps(sanitized_data, **json_kwargs)
    write_file_verified(
        filepath,
        content,
        setting_name,
        required_value,
        context,
        mode="w",
        encoding="utf-8",
        settings_snapshot=settings_snapshot,
    )
