#!/usr/bin/env python3
"""
LockStock Guard CLI - Enterprise Secrets Daemon

This is the command-line interface for the LockStock Guard daemon,
which implements the "Secure Enclave in Software" pattern.

Usage:
    lockstock-guard start [--vault PATH] [--socket PATH] [--foreground]
    lockstock-guard stop [--socket PATH]
    lockstock-guard status [--socket PATH]
    lockstock-guard bind --agent AGENT_ID --token GENESIS_TOKEN
    lockstock-guard export --agent AGENT_ID --transfer-key KEY [--output FILE]
    lockstock-guard import --file BLOB_FILE --transfer-key KEY
"""

import argparse
import sys
import os
from datetime import datetime, timezone

from .daemon import LibertyDaemon

# Version
__version__ = "1.2.1"

# Default paths for enterprise deployment
DEFAULT_SOCKET_PATH = "/var/run/liberty/liberty.sock"
DEFAULT_VAULT_PATH = "/var/lib/liberty"
DEFAULT_PID_FILE = "/var/run/liberty/liberty.pid"


def main():
    """Main CLI entry point."""
    parser = argparse.ArgumentParser(
        description='LockStock Guard - Enterprise Secrets Daemon',
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
Architecture:
    This daemon implements the "Secure Enclave in Software" pattern.
    It holds secrets in memory and serves them via Unix socket IPC.
    Agent applications connect to the socket to request secrets.

Security:
    - Daemon runs as privileged user (liberty-user) with vault access
    - Agent apps run as unprivileged user with socket access only
    - Secrets never touch agent process memory until explicitly requested
    - Compromised agent cannot dump the vault

Examples:
    # Start daemon (as liberty-user or root)
    lockstock-guard start

    # Start with custom paths
    lockstock-guard start --vault ~/.liberty --socket /tmp/liberty.sock

    # Start in foreground for debugging
    lockstock-guard start --foreground

    # Stop daemon
    lockstock-guard stop

    # Check status
    lockstock-guard status

Agent Usage:
    from lockstock_guard import client

    # Connect to daemon
    secret = client.get("DATABASE_URL", socket_path="/var/run/liberty/liberty.sock")

Migration (Teleportation):
    # On source machine: export agent with encrypted blob
    lockstock-guard export --agent my_agent --transfer-key "shared-secret" --output migrate.blob

    # On target machine: import and re-bind to new hardware
    lockstock-guard import --file migrate.blob --transfer-key "shared-secret"

    The transfer key encrypts the secret during transit. Use a strong key
    and transmit it separately from the blob file.
"""
    )

    parser.add_argument(
        "--version",
        action="version",
        version=f"lockstock-guard {__version__}"
    )

    parser.add_argument(
        "command",
        choices=["start", "stop", "status", "bind", "export", "import"],
        help="Command to run"
    )
    parser.add_argument(
        "--socket",
        default=DEFAULT_SOCKET_PATH,
        help=f"Socket path (default: {DEFAULT_SOCKET_PATH})"
    )
    parser.add_argument(
        "--vault",
        default=DEFAULT_VAULT_PATH,
        help=f"Vault path (default: {DEFAULT_VAULT_PATH})"
    )
    parser.add_argument(
        "--group",
        default="agents",
        help="Socket group for agent access (default: agents)"
    )
    parser.add_argument(
        "--foreground", "-f",
        action="store_true",
        help="Run in foreground (don't daemonize)"
    )
    parser.add_argument(
        "--pid",
        default=None,
        help="PID file path (default: alongside socket)"
    )
    parser.add_argument(
        "--agent",
        help="Agent ID (for bind command)"
    )
    parser.add_argument(
        "--token",
        help="Genesis token (for bind command)"
    )
    parser.add_argument(
        "--api",
        default="https://lockstock-api-i9kp.onrender.com",
        help="LockStock API URL (for bind command)"
    )
    parser.add_argument(
        "--transfer-key",
        help="Transport encryption key (for export/import commands)"
    )
    parser.add_argument(
        "--file",
        help="Migration blob file path (for import command)"
    )
    parser.add_argument(
        "--output",
        help="Output file path (for export command, defaults to stdout)"
    )

    args = parser.parse_args()

    # Expand paths
    socket_path = os.path.expanduser(args.socket)
    vault_path = os.path.expanduser(args.vault)

    # Derive PID path from socket path if not specified
    pid_file = args.pid
    if pid_file is None:
        from pathlib import Path
        socket_dir = Path(socket_path).parent
        pid_file = str(socket_dir / "liberty.pid")

    daemon = LibertyDaemon(
        socket_path=socket_path,
        vault_path=vault_path,
        socket_group=args.group,
        pid_file=pid_file,
    )

    if args.command == "start":
        print(f"Starting LockStock Guard daemon...")
        print(f"  Vault: {vault_path}")
        print(f"  Socket: {socket_path}")
        daemon.start(foreground=args.foreground)

    elif args.command == "stop":
        daemon.stop()

    elif args.command == "status":
        if daemon._is_running():
            pid = daemon.pid_file.read_text().strip()
            print(f"LockStock Guard is running (PID: {pid})")
            print(f"  Socket: {daemon.socket_path}")
            print(f"  Vault: {daemon.vault_path}")
        else:
            print("LockStock Guard is not running")
            sys.exit(1)

    elif args.command == "bind":
        # Import dependencies
        import requests
        import platform
        import hashlib
        from pathlib import Path

        try:
            from liberty import SecretVault
        except ImportError:
            print("ERROR: liberty-secrets not found")
            print("This should have been installed as a dependency.")
            sys.exit(1)

        # Use EnterpriseFingerprint for cloud-native binding
        try:
            from lockstock_guard.enterprise_fingerprint import EnterpriseFingerprint
            use_enterprise_fp = True
        except ImportError:
            use_enterprise_fp = False

        if not args.agent or not args.token:
            print("ERROR: --agent and --token are required for bind command")
            print("")
            print("Usage: lockstock-guard bind --agent AGENT_ID --token GENESIS_TOKEN")
            sys.exit(1)

        print(f"🔗 Binding agent: {args.agent}")
        print(f"📍 Vault: {vault_path}")

        # Get enterprise fingerprint (cloud/container/bare-metal aware)
        if use_enterprise_fp:
            try:
                hw_fp = EnterpriseFingerprint.generate()
                fp_type = EnterpriseFingerprint.get_fingerprint_type()
                print(f"🖥️  Environment: {fp_type}")
            except Exception as e:
                # Fallback fingerprint
                info = f"{platform.node()}-{platform.machine()}-{platform.system()}"
                hw_fp = hashlib.sha256(info.encode()).hexdigest()
                print(f"⚠️  Using fallback fingerprint: {e}")
        else:
            # Legacy fallback
            try:
                from liberty import HardwareFingerprint
                fp = HardwareFingerprint()
                hw_fp = fp.generate()
            except Exception as e:
                info = f"{platform.node()}-{platform.machine()}-{platform.system()}"
                hw_fp = hashlib.sha256(info.encode()).hexdigest()
                print(f"⚠️  Using fallback fingerprint (couldn't access hardware)")

        print(f"🖥️  Hardware: {hw_fp[:16]}...")
        print(f"🌐 Calling API: {args.api}/v1/agents/claim")

        # Call backend to claim secret
        try:
            response = requests.post(
                f"{args.api}/v1/agents/claim",
                json={
                    "genesis_token": args.token,
                    "hardware_fingerprint": hw_fp
                },
                timeout=30
            )

            if response.status_code != 200:
                print(f"❌ API error: {response.status_code}")
                try:
                    error_data = response.json()
                    print(f"   {error_data.get('message', response.text)}")
                except:
                    print(f"   {response.text}")
                sys.exit(1)

            data = response.json()
            claimed_agent_id = data.get("agent_id")
            secret = data.get("secret")

            if not secret:
                print("❌ No secret returned from API")
                sys.exit(1)

            print(f"✅ Secret claimed!")

        except requests.exceptions.RequestException as e:
            print(f"❌ Network error: {e}")
            sys.exit(1)

        # Initialize vault if needed
        vault_path_obj = Path(vault_path)
        vault_path_obj.mkdir(parents=True, exist_ok=True)

        vault = SecretVault(str(vault_path_obj))

        # Store secret WITH fingerprint binding
        # This enables the daemon to verify the environment on each access
        # Key is the raw agent_id for SDK compatibility with from_liberty()
        secret_key = args.agent
        secret_data = {
            "value": secret,
            "fingerprint": hw_fp,
            "bound_at": datetime.now(timezone.utc).isoformat(),
            "agent_id": args.agent,
        }

        # Store as JSON structure for fingerprint-aware retrieval
        import json
        vault.add(secret_key, json.dumps(secret_data))

        print(f"💾 Secret stored as: {secret_key}")
        print(f"🔐 Bound to fingerprint: {hw_fp[:16]}...")

        # Initialize chain state with genesis hash (roots chain in provisioning token)
        genesis_hash = hashlib.sha256(args.token.encode()).hexdigest()
        chain_state_file = vault_path_obj / "chain_state.json"
        chain_state_file.parent.mkdir(parents=True, exist_ok=True)

        # Read existing chain state (if any) to avoid overwriting other agents
        existing_chain_state = {}
        if chain_state_file.exists():
            try:
                with open(chain_state_file) as f:
                    existing_chain_state = json.load(f)
            except (json.JSONDecodeError, OSError):
                # Corrupted or unreadable - start fresh
                pass

        # Identity matrix for genesis
        identity_matrix = {"a11": 1, "a12": 0, "a21": 0, "a22": 1}

        # Merge in new agent's genesis state
        existing_chain_state[args.agent] = {
            "agent_id": args.agent,
            "current_hash": genesis_hash,
            "sequence": 0,
            "state_matrix": identity_matrix,
            "last_server_timestamp": 0,
            "last_synced": datetime.now(timezone.utc).timestamp(),
        }

        with open(chain_state_file, "w") as f:
            json.dump(existing_chain_state, f, indent=2)

        print(f"🔗 Genesis hash: {genesis_hash[:16]}...")
        print(f"💾 Chain state initialized: {chain_state_file}")
        print(f"")
        print(f"✅ Agent bound successfully!")
        print(f"🔑 Vault: {vault_path_obj}/.liberty/secrets.enc")

        # Check if daemon is running and reload it
        daemon_check = LibertyDaemon(
            socket_path=socket_path,
            vault_path=vault_path,
            socket_group=args.group,
            pid_file=pid_file,
        )

        if daemon_check._is_running():
            try:
                import signal as sig
                pid = int(daemon_check.pid_file.read_text().strip())
                os.kill(pid, sig.SIGHUP)
                print(f"🔄 Daemon reloaded (PID: {pid})")
            except Exception as e:
                print(f"⚠️  Could not reload daemon: {e}")
                print(f"   Run: kill -HUP {pid}")
        else:
            print(f"")
            print(f"⚠️  Daemon not running. Start it with:")
            print(f"   lockstock-guard start --vault {vault_path}")

    elif args.command == "export":
        # Import dependencies
        import base64
        from pathlib import Path
        from cryptography.hazmat.primitives.ciphers.aead import AESGCM
        from cryptography.hazmat.primitives import hashes
        from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC

        try:
            from liberty import SecretVault
        except ImportError:
            print("ERROR: liberty-secrets not found")
            sys.exit(1)

        if not args.agent:
            print("ERROR: --agent is required for export command")
            print("")
            print("Usage: lockstock-guard export --agent AGENT_ID --transfer-key KEY")
            sys.exit(1)

        if not args.transfer_key:
            print("ERROR: --transfer-key is required for export command")
            print("")
            print("Usage: lockstock-guard export --agent AGENT_ID --transfer-key KEY")
            sys.exit(1)

        print(f"📤 Exporting agent: {args.agent}")
        print(f"📍 Vault: {vault_path}")

        # Load vault
        vault_path_obj = Path(vault_path)
        if not vault_path_obj.exists():
            print(f"❌ Vault not found at {vault_path}")
            sys.exit(1)

        vault = SecretVault(str(vault_path_obj))

        # Find the secret key for this agent
        # Key is the raw agent_id for SDK compatibility with from_liberty()
        secret_key = args.agent

        # Load and decrypt secrets from vault
        secrets = vault._load_secrets()
        if secret_key not in secrets:
            print(f"❌ Agent '{args.agent}' not found in vault")
            print(f"   Expected key: {secret_key}")
            sys.exit(1)

        # Extract the secret data
        secret_data = secrets[secret_key]
        if isinstance(secret_data, dict) and 'value' in secret_data:
            # New format with metadata
            raw_value = secret_data['value']
        else:
            raw_value = secret_data

        # Parse the stored JSON (contains value, fingerprint, bound_at, agent_id)
        import json
        try:
            agent_data = json.loads(raw_value)
        except json.JSONDecodeError:
            # Old format - just the secret value
            agent_data = {"value": raw_value, "agent_id": args.agent}

        print(f"✅ Found agent secret")
        print(f"   Bound fingerprint: {agent_data.get('fingerprint', 'unknown')[:16]}...")

        # Prepare migration blob
        migration_blob = {
            "version": "1.0",
            "agent_id": agent_data.get("agent_id", args.agent),
            "secret": agent_data.get("value"),
            "original_fingerprint": agent_data.get("fingerprint"),
            "original_bound_at": agent_data.get("bound_at"),
            "exported_at": datetime.now(timezone.utc).isoformat(),
            "source_hostname": os.uname().nodename,
        }

        # Encrypt with transfer key using AES-GCM
        # Derive key from transfer key using PBKDF2
        kdf = PBKDF2HMAC(
            algorithm=hashes.SHA256(),
            length=32,
            salt=b'lockstock_migrate_v1',
            iterations=100000,
        )
        encryption_key = kdf.derive(args.transfer_key.encode())

        # Encrypt
        aesgcm = AESGCM(encryption_key)
        nonce = os.urandom(12)
        plaintext = json.dumps(migration_blob).encode()
        ciphertext = aesgcm.encrypt(nonce, plaintext, None)

        # Combine nonce + ciphertext and base64 encode
        encrypted_blob = base64.b64encode(nonce + ciphertext).decode()

        # Output
        if args.output:
            output_path = Path(args.output)
            output_path.write_text(encrypted_blob)
            print(f"📄 Migration blob written to: {args.output}")
        else:
            print(f"")
            print(f"--- MIGRATION BLOB START ---")
            print(encrypted_blob)
            print(f"--- MIGRATION BLOB END ---")
            print(f"")

        print(f"✅ Export complete!")
        print(f"")
        print(f"On the target machine, run:")
        print(f"   lockstock-guard import --file <blob_file> --transfer-key <same_key>")

    elif args.command == "import":
        # Import dependencies
        import base64
        from pathlib import Path
        from cryptography.hazmat.primitives.ciphers.aead import AESGCM
        from cryptography.hazmat.primitives import hashes
        from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
        import platform
        import hashlib

        try:
            from liberty import SecretVault
        except ImportError:
            print("ERROR: liberty-secrets not found")
            sys.exit(1)

        # Use EnterpriseFingerprint for cloud-native binding
        try:
            from lockstock_guard.enterprise_fingerprint import EnterpriseFingerprint
            use_enterprise_fp = True
        except ImportError:
            use_enterprise_fp = False

        if not args.file:
            print("ERROR: --file is required for import command")
            print("")
            print("Usage: lockstock-guard import --file BLOB_FILE --transfer-key KEY")
            sys.exit(1)

        if not args.transfer_key:
            print("ERROR: --transfer-key is required for import command")
            print("")
            print("Usage: lockstock-guard import --file BLOB_FILE --transfer-key KEY")
            sys.exit(1)

        print(f"📥 Importing agent from: {args.file}")
        print(f"📍 Vault: {vault_path}")

        # Read migration blob
        blob_path = Path(args.file)
        if not blob_path.exists():
            print(f"❌ File not found: {args.file}")
            sys.exit(1)

        encrypted_blob = blob_path.read_text().strip()

        # Derive decryption key from transfer key
        kdf = PBKDF2HMAC(
            algorithm=hashes.SHA256(),
            length=32,
            salt=b'lockstock_migrate_v1',
            iterations=100000,
        )
        decryption_key = kdf.derive(args.transfer_key.encode())

        # Decrypt
        try:
            data = base64.b64decode(encrypted_blob)
            nonce = data[:12]
            ciphertext = data[12:]

            aesgcm = AESGCM(decryption_key)
            plaintext = aesgcm.decrypt(nonce, ciphertext, None)
            import json
            migration_blob = json.loads(plaintext.decode())
        except Exception as e:
            print(f"❌ Decryption failed - wrong transfer key?")
            print(f"   Error: {e}")
            sys.exit(1)

        agent_id = migration_blob.get("agent_id")
        secret = migration_blob.get("secret")
        original_fp = migration_blob.get("original_fingerprint")
        source_host = migration_blob.get("source_hostname")

        print(f"✅ Decrypted migration blob")
        print(f"   Agent: {agent_id}")
        print(f"   From: {source_host}")
        print(f"   Original fingerprint: {original_fp[:16] if original_fp else 'unknown'}...")

        # Generate new fingerprint for this machine
        if use_enterprise_fp:
            try:
                hw_fp = EnterpriseFingerprint.generate()
                fp_type = EnterpriseFingerprint.get_fingerprint_type()
                print(f"🖥️  Environment: {fp_type}")
            except Exception as e:
                info = f"{platform.node()}-{platform.machine()}-{platform.system()}"
                hw_fp = hashlib.sha256(info.encode()).hexdigest()
                print(f"⚠️  Using fallback fingerprint: {e}")
        else:
            try:
                from liberty import HardwareFingerprint
                fp = HardwareFingerprint()
                hw_fp = fp.generate()
            except Exception as e:
                info = f"{platform.node()}-{platform.machine()}-{platform.system()}"
                hw_fp = hashlib.sha256(info.encode()).hexdigest()
                print(f"⚠️  Using fallback fingerprint")

        print(f"🖥️  New fingerprint: {hw_fp[:16]}...")

        # Initialize vault if needed
        vault_path_obj = Path(vault_path)
        vault_path_obj.mkdir(parents=True, exist_ok=True)

        vault = SecretVault(str(vault_path_obj))

        # Store secret with NEW fingerprint binding
        # Key is the raw agent_id for SDK compatibility with from_liberty()
        secret_key = agent_id
        import json
        secret_data = {
            "value": secret,
            "fingerprint": hw_fp,
            "bound_at": datetime.now(timezone.utc).isoformat(),
            "agent_id": agent_id,
            "migrated_from": {
                "fingerprint": original_fp,
                "hostname": source_host,
                "migrated_at": datetime.now(timezone.utc).isoformat(),
            }
        }

        # Store as JSON structure for fingerprint-aware retrieval
        vault.add(secret_key, json.dumps(secret_data))

        print(f"💾 Secret stored as: {secret_key}")
        print(f"🔐 Bound to fingerprint: {hw_fp[:16]}...")

        # Notify API of migration (optional - for audit trail)
        try:
            import requests
            migrate_response = requests.post(
                f"{args.api}/v1/agents/migrate",
                json={
                    "agent_id": agent_id,
                    "old_fingerprint": original_fp,
                    "new_fingerprint": hw_fp,
                    "source_hostname": source_host,
                    "target_hostname": os.uname().nodename,
                },
                timeout=10
            )
            if migrate_response.status_code == 200:
                print(f"🌐 API notified of migration")
            else:
                print(f"⚠️  API notification failed: {migrate_response.status_code}")
        except Exception as e:
            print(f"⚠️  Could not notify API: {e}")
            print(f"   (Migration still successful locally)")

        print(f"")
        print(f"✅ Agent migrated successfully!")
        print(f"🔑 Vault: {vault_path_obj}/.liberty/secrets.enc")

        # Check if daemon is running and reload it
        daemon_check = LibertyDaemon(
            socket_path=socket_path,
            vault_path=vault_path,
            socket_group=args.group,
            pid_file=pid_file,
        )

        if daemon_check._is_running():
            try:
                import signal as sig
                pid = int(daemon_check.pid_file.read_text().strip())
                os.kill(pid, sig.SIGHUP)
                print(f"🔄 Daemon reloaded (PID: {pid})")
            except Exception as e:
                print(f"⚠️  Could not reload daemon: {e}")
        else:
            print(f"")
            print(f"⚠️  Daemon not running. Start it with:")
            print(f"   lockstock-guard start --vault {vault_path}")


if __name__ == "__main__":
    main()
