# Copyright (c) 2026 Thomas Goirand <zigo@debian.org>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import socket
import sys

from keystonemiddleware import auth_token
from keystoneauth1 import loading as ks_loading
from oslo_config import cfg
from oslo_log import log as logging

from oslo_db import options as db_options
from oslo_messaging import transport as messaging_transport
import oslo_policy
from oslo_policy import opts as policy_opts

# Global variable to cache the CONF object
_CONF = None

# DEFAULT group options
vigie_default_opts = [
    cfg.IntOpt('rpc_heartbeat_timeout',
               default=5,
               min=2,
               help='Timeout (in seconds) for the RPC heartbeat ping.'),
    cfg.StrOpt('availability_zone',
               default='vigie',
               help='Availability zone for the service.'),
    cfg.IntOpt('report_interval',
               default=10,
               help='Interval, in seconds, between nodes reporting state '
                    'to datastore'),
    cfg.IntOpt('periodic_interval',
               default=60,
               help='Interval, in seconds, between running periodic tasks'),
    cfg.IntOpt('periodic_fuzzy_delay',
               default=5,
               help='Range, in seconds, to randomly delay when starting the'
                    ' periodic task scheduler to reduce stampeding.'
                    ' (Disable by setting to 0)'),
    cfg.StrOpt('host',
               default=socket.getfqdn(),
               sample_default='cl1-controller-1.example.com',
               help='Name of this node.  This can be an opaque identifier. '
                    'It is not necessarily a hostname, FQDN, or IP address.'),
]

# Worker configuration
worker_group = cfg.OptGroup(name='worker',
                            title='Worker options')

worker_opts = [
    cfg.StrOpt('coordination_url',
               default=None,
               help='Tooz coordination URL (e.g., zookeeper://host:port)'),
    cfg.StrOpt('my_hostname',
               default=None,
               sample_default='cl1-controller-1.example.com',
               help='FQDN of this worker instance. Used for coordination. '
                    'Must be unique across the worker cluster. If None, the server FQDN is used.'),
    cfg.IntOpt('parallel_jobs',
               default=3,
               help='Maximum number of parallel jobs'),
    cfg.IntOpt('check_interval',
               default=120,
               help='Periodic check interval in seconds'),
]

# Validate compute configuration
validate_compute_group = cfg.OptGroup(name='validate_compute',
                                      title='Validate compute options')

validate_compute_opts = [
    cfg.StrOpt('glance_image',
                default=None,
                help='Image to use when validating compute nodes.'),
    cfg.StrOpt('flavor',
               default=None,
               help='Flavor to use when validating compute nodes.'),
    cfg.StrOpt('network',
               default=None,
               help='Network name to use when validating compute nodes.'),
    cfg.StrOpt('ssh_username',
               default='debian',
               help='SSH username to use when validating compute nodes.'),
    cfg.BoolOpt('keep_enabled',
                default=False,
                help='Should compute hosts stay enabled after validation?'),
    cfg.BoolOpt('ssh_use_jump_host',
                default=False,
                help='Use SSH jump host when connecting to compute nodes'),
    cfg.StrOpt('ssh_jump_host_hostname',
               default=None,
               sample_default='jump.example.com',
               help='Hostname of the SSH jump host to use when connecting to '
                    'compute nodes'),
    cfg.IntOpt('ssh_jump_host_port',
               default=22,
               help='Port to use for the jump host.'),
    cfg.StrOpt('ssh_jump_host_key_file',
               default='/etc/vigie/.ssh/id_rsa',
               help='Path to the SSH private key used to connect to the jump '
                    'host when validating compute nodes.'),
]

def init_config(conf=None):
    """Initialize configuration and return CONF object"""
    
    # Use provided CONF or create new one
    if conf is not None:
        CONF = conf
    else:
        CONF = cfg.CONF
    
    # Register worker options
    CONF.register_group(worker_group)
    CONF.register_opts(worker_opts, group=worker_group)

    # Register validate_compute options
    CONF.register_group(validate_compute_group)
    CONF.register_opts(validate_compute_opts, group=validate_compute_group)

    # DEFAULT group
    CONF.register_opts(vigie_default_opts)

    # Register oslo.log options
    logging.register_options(CONF)

    # Register oslo.db options (this includes [database]/connection)
    db_options.set_defaults(CONF)

    # Register oslo.policy options
    policy_opts.set_defaults(CONF) 

    # Register oslo.messaging options for RPC
    messaging_transport.set_transport_defaults('vigie')

    # Register keystone middleware options for API auth
    auth_token.CONF = CONF
    try:
        auth_token_opts = auth_token._opts
        CONF.register_opts(auth_token_opts, group='keystone_authtoken')
    except (AttributeError, TypeError):
        # Fallback if _opts doesn't work as expected
        pass

    # Load configuration from file (only if no conf was provided)
    if conf is None:
        CONF(sys.argv[1:], project='vigie')

    # Set hostname if not provided
    if CONF.worker.my_hostname is None:
        CONF.worker.my_hostname = socket.getfqdn()

    return CONF

def get_config():
    """Get configuration, parsing it only once.

    This is the canonical entry point for retrieving the CONF object.
    It ensures init_config() is called exactly once per process.
    """
    global _CONF
    if _CONF is None:
        _CONF = init_config()
    return _CONF

def list_opts():
    """Return a list of oslo.config options available in this module.

    Only returns vigie-specific options. Oslo library options are
    automatically discovered by oslo-config-generator via entry points.
    """
    return [
        ("DEFAULT", vigie_default_opts),
        ('worker', worker_opts),
        ('validate_compute', validate_compute_opts),
    ]
