# 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.

"""Worker Manager for Vigie.

Implements the business logic for the vigie-worker daemon.
"""

from oslo_log import log as logging
from oslo_service import periodic_task

from vigie import manager
from vigie import context
from vigie.db.sqlalchemy.database import get_session
from vigie.db.sqlalchemy.models import VigieTasks, TaskState, TaskType
from vigie.config import get_config

# Import the external logic library
from vigietools.validate_compute import validate_compute as tools_validate_compute
# Import the connection helper from your API module
from vigie.api import get_openstack_connection

CONF = get_config()
LOG = logging.getLogger(__name__)


class WorkerManager(manager.Manager):
    """Manager for the Vigie Worker service.

    This manager handles long-running tasks like compute validation and evacuation.
    It listens on the 'vigie-worker.<hostname>' RPC topic.
    """

    def __init__(self, host=None, **kwargs):
        super(WorkerManager, self).__init__(host=host, **kwargs)
        LOG.info(f"WorkerManager initialized on host {self.host}")

    @property
    def RPC_HEARTBEAT_TOPIC(self):
        """Dynamic topic based on hostname."""
        return f'vigie-worker.{self.host}'

    @property
    def RPC_HEARTBEAT_SERVICE_NAME(self):
        """Service binary name."""
        return 'vigie-worker'

    def init_host(self, service_id=None):
        """Called by the Service wrapper when the service starts."""
        LOG.info(f"WorkerManager init_host called for service ID: {service_id}")
        super(WorkerManager, self).init_host(service_id)

    # NOTE: is_working() is inherited from manager.Manager.

    # ------------------------------------------------------------------
    # RPC Methods (Called by vigie-api)
    # ------------------------------------------------------------------

    def validate_compute(self, ctxt, task_id, compute_hostname, project_id, project_name):
        """Validate a compute node by spawning a VM.

        This method is called remotely via RPC from the API.
        It updates the task state in the database as it progresses.
        """
        LOG.info(f"Starting validate_compute task {task_id} for {compute_hostname}")

        session = None
        try:
            session = get_session()

            # Fetch the task record
            task = session.query(VigieTasks).filter_by(id=task_id).first()
            if not task:
                LOG.error(f"Task {task_id} not found in database.")
                return

            # Update state to RUNNING
            task.state = TaskState.RUNNING
            session.commit()

            conn = get_openstack_connection()

            use_jump_host = CONF.validate_compute.ssh_use_jump_host
            jump_host = None
            jump_username = None
            jump_port = None
            jump_private_key = None
 
            if use_jump_host:
                jump_host = CONF.validate_compute.ssh_jump_host_hostname
                jump_username = CONF.validate_compute.ssh_jump_host_username
                jump_port = CONF.validate_compute.ssh_jump_host_port
                jump_private_key = CONF.validate_compute.ssh_jump_host_key_file

            # Validate required config values
            if not CONF.validate_compute.glance_image:
                raise ValueError("glance_image configuration is required")
            if not CONF.validate_compute.flavor:
                raise ValueError("flavor configuration is required")
            if not CONF.validate_compute.network:
                raise ValueError("network configuration is required")

            tools_validate_compute(conn=conn, hostname=compute_hostname,
                                   image_name=CONF.validate_compute.glance_image,
                                   flavor_name=CONF.validate_compute.flavor,
                                   network_name=CONF.validate_compute.network,
                                   ssh_username=CONF.validate_compute.ssh_username,
                                   keep_enabled=CONF.validate_compute.keep_enabled,
                                   jump_host=jump_host,
                                   jump_username=jump_username,
                                   jump_port=jump_port,
                                   jump_private_key=jump_private_key)

            # If we reach here, no exception was raised, so it succeeded
            task.state = TaskState.COMPLETED
            LOG.info(f"Validation successful for {compute_hostname}")
            session.commit()

        except Exception as e:
            LOG.exception(f"Error during validate_compute task {task_id}: {e}")
            if session:
                session.rollback()
                # Update state to FAILED
                try:
                    task = session.query(VigieTasks).filter_by(id=task_id).first()
                    if task:
                        task.state = TaskState.FAILED
                        # Optionally store the error message if your model supports it
                        # task.error_message = str(e) 
                        session.commit()
                except Exception:
                    pass
        finally:
            if session:
                session.close()

    def evacuate_compute(self, ctxt, source_hostname, target_hostname=None):
        """Evacuate instances from a compute node.

        TODO: Implement evacuation logic.
        """
        LOG.info(f"Starting evacuation for {source_hostname} (target: {target_hostname})")
        # Implementation pending
        pass

    # ------------------------------------------------------------------
    # Periodic Tasks
    # ------------------------------------------------------------------

    @periodic_task.periodic_task(spacing=60)
    def _cleanup_old_tasks(self, ctxt):
        """Periodically clean up old completed/failed tasks."""
        LOG.debug("Running periodic task: cleanup old tasks")
        # TODO: Implement logic to archive or delete old tasks from DB
        pass

    @periodic_task.periodic_task(spacing=300)
    def _check_stale_tasks(self, ctxt):
        """Check for tasks that have been RUNNING too long."""
        LOG.debug("Running periodic task: check stale tasks")
        # TODO: Implement logic to detect and fail tasks stuck in RUNNING state
        pass
