"""Migration utilities for encryption at rest.

This module provides utilities for migrating existing unencrypted queue files
to encrypted format, and other security-related migration tasks.
"""

import json
import logging
import os
import shutil
from pathlib import Path
from typing import Optional

from .encryption import QueueEncryption
from ..utils.exceptions import ConfigurationError

logger = logging.getLogger(__name__)


def migrate_queue_to_encrypted(
    unencrypted_file: str,
    encryption: QueueEncryption,
    output_file: Optional[str] = None,
    backup: bool = True,
) -> str:
    """Migrate an unencrypted queue file to encrypted format.

    This utility reads an existing unencrypted queue JSON file, encrypts it,
    and writes to a new location (or replaces the original with backup).

    Args:
        unencrypted_file: Path to existing unencrypted queue file
        encryption: QueueEncryption instance with keys for encryption
        output_file: Path for encrypted output (default: same as input with .encrypted suffix)
        backup: Whether to create backup of original file (default: True)

    Returns:
        Path to the encrypted output file

    Raises:
        ConfigurationError: If file doesn't exist or is already encrypted
        ValueError: If file format is invalid

    Example:
        >>> from cryptography.fernet import Fernet
        >>> from mca_sdk.security.encryption import QueueEncryption
        >>> from mca_sdk.security.migration import migrate_queue_to_encrypted
        >>>
        >>> # Generate encryption key
        >>> key = Fernet.generate_key()
        >>> encryption = QueueEncryption(encryption_keys=[key])
        >>>
        >>> # Migrate unencrypted queue
        >>> encrypted_path = migrate_queue_to_encrypted(
        ...     unencrypted_file="~/.mca_sdk/queue.json",
        ...     encryption=encryption,
        ...     backup=True
        ... )
        >>> print(f"Migrated to: {encrypted_path}")
    """
    # Expand paths
    unencrypted_path = Path(os.path.expanduser(unencrypted_file))

    if not unencrypted_path.exists():
        raise ConfigurationError(
            f"Queue file not found: {unencrypted_path}. " f"Cannot migrate non-existent file."
        )

    # Determine output path
    if output_file:
        output_path = Path(os.path.expanduser(output_file))
    else:
        # Default: add .encrypted before extension
        output_path = unencrypted_path.with_suffix(".encrypted" + unencrypted_path.suffix)

    logger.info(f"Migrating queue file: {unencrypted_path} -> {output_path}")

    # Read unencrypted file
    try:
        with open(unencrypted_path, "rb") as f:
            raw_data = f.read()

        # Check if already encrypted
        if raw_data.startswith(QueueEncryption.MAGIC_HEADER):
            raise ConfigurationError(
                f"File {unencrypted_path} appears to be already encrypted "
                f"(contains magic header). Migration not needed."
            )

        # Try to parse as JSON to validate format
        try:
            json_str = raw_data.decode("utf-8")
            data = json.loads(json_str)
        except (UnicodeDecodeError, json.JSONDecodeError) as e:
            raise ValueError(
                f"Invalid queue file format: {e}. " f"File must be unencrypted JSON queue data."
            )

        # Validate queue structure
        if not isinstance(data, dict) or "_queue_format_version" not in data:
            raise ValueError("Invalid queue file structure. Missing _queue_format_version field.")

        logger.info(
            f"Validated unencrypted queue: {len(data.get('items', []))} items, "
            f"format version {data.get('_queue_format_version')}"
        )

    except Exception as e:
        logger.error(f"Failed to read unencrypted queue file: {e}")
        raise

    # Encrypt data
    try:
        json_bytes = json_str.encode("utf-8")
        encrypted_data = encryption.encrypt(json_bytes)
        logger.info(f"Encrypted {len(json_bytes)} bytes -> {len(encrypted_data)} bytes")
    except Exception as e:
        logger.error(f"Failed to encrypt queue data: {e}")
        raise ConfigurationError(f"Encryption failed: {e}")

    # Create backup if requested
    if backup and not output_file:
        # Only backup if overwriting original
        backup_path = unencrypted_path.with_suffix(".backup" + unencrypted_path.suffix)
        try:
            shutil.copy2(unencrypted_path, backup_path)
            logger.info(f"Created backup: {backup_path}")
        except Exception as e:
            logger.error(f"Failed to create backup: {e}")
            raise ConfigurationError(f"Backup failed: {e}")

    # Write encrypted file
    try:
        # Create parent directory if needed
        output_path.parent.mkdir(parents=True, exist_ok=True)

        with open(output_path, "wb") as f:
            f.write(encrypted_data)

        # Set restrictive permissions
        os.chmod(output_path, 0o600)

        logger.info(
            f"Successfully migrated queue to encrypted format: {output_path} "
            f"({len(encrypted_data)} bytes)"
        )

    except Exception as e:
        logger.error(f"Failed to write encrypted file: {e}")
        raise ConfigurationError(f"Failed to write encrypted file: {e}")

    return str(output_path)


def verify_encrypted_queue(encrypted_file: str, encryption: QueueEncryption) -> bool:
    """Verify that an encrypted queue file can be successfully decrypted.

    Args:
        encrypted_file: Path to encrypted queue file
        encryption: QueueEncryption instance with decryption keys

    Returns:
        True if file can be decrypted and is valid queue format

    Example:
        >>> from mca_sdk.security.migration import verify_encrypted_queue
        >>>
        >>> is_valid = verify_encrypted_queue(
        ...     encrypted_file="~/.mca_sdk/queue.encrypted.json",
        ...     encryption=encryption
        ... )
        >>> print(f"Queue valid: {is_valid}")
    """
    try:
        encrypted_path = Path(os.path.expanduser(encrypted_file))

        if not encrypted_path.exists():
            logger.error(f"File not found: {encrypted_path}")
            return False

        # Read encrypted file
        with open(encrypted_path, "rb") as f:
            encrypted_data = f.read()

        # Check magic header
        if not encrypted_data.startswith(QueueEncryption.MAGIC_HEADER):
            logger.error("File missing magic header - may not be encrypted")
            return False

        # Try to decrypt
        decrypted_data = encryption.decrypt(encrypted_data)
        json_str = decrypted_data.decode("utf-8")
        data = json.loads(json_str)

        # Validate structure
        if not isinstance(data, dict) or "_queue_format_version" not in data:
            logger.error("Decrypted data has invalid queue structure")
            return False

        logger.info(
            f"✅ Queue file valid: {encrypted_path} " f"({len(data.get('items', []))} items)"
        )
        return True

    except Exception as e:
        logger.error(f"Verification failed: {e}")
        return False
