# Copyright (c) 2025 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.

from keystoneauth1 import loading as ks_loading
from oslo_config import cfg
from oslo_log import log as logging
from mistralclient.api import client as mistral_client

LOG = logging.getLogger(__name__)

class MistralClient(object):
    """Client for interacting with Mistral workflow service."""
    
    def __init__(self, CONF):
        self.CONF = CONF
        self.client = None
        self._initialize_client()
    
    def _initialize_client(self):
        """Initialize Mistral client."""
        try:
            # Determine which configuration section to use
            if self.CONF.worker.mistral_use_keystone_auth:
                mistralclient_auth_section = "keystone_authtoken"
                LOG.info(f"⚙ Using keystone_authtoken section for Mistral authentication")
            else:
                mistralclient_auth_section = "mistral"
                LOG.info(f"⚙ Using mistral section for Mistral authentication")
            
            LOG.debug(f"🐛 Loading auth from section: {mistralclient_auth_section}")
            
            # Load auth
            auth = ks_loading.load_auth_from_conf_options(self.CONF, mistralclient_auth_section)
            LOG.debug(f"🐛 Auth loaded successfully: {type(auth)}")
            
            # Load session with fallback for missing timeout option
            try:
                session = ks_loading.load_session_from_conf_options(
                    self.CONF, mistralclient_auth_section, auth=auth, verify=False)
            except Exception as session_e:
                LOG.debug(f"🐛 Falling back to manual session creation due to: {session_e}")
                from keystoneauth1 import session as ka_session
                session = ka_session.Session(auth=auth, verify=False)
            
            LOG.debug(f"🐛 Session loaded successfully")
            
            # Create adapter - pass both session and auth like in your working example
            try:
                adapter = ks_loading.load_adapter_from_conf_options(
                    self.CONF, mistralclient_auth_section, session=session, auth=auth)
            except Exception as adapter_e:
                LOG.debug(f"🐛 Falling back to manual adapter creation due to: {adapter_e}")
                # Get basic adapter configuration from the section
                section_conf = getattr(self.CONF, mistralclient_auth_section)
                interface = getattr(section_conf, 'interface', 'public')
                region_name = getattr(section_conf, 'region_name', None)
                
                # Manual adapter creation
                from keystoneauth1.adapter import Adapter
                adapter = Adapter(
                    session=session,
                    auth=auth,
                    service_type='workflowv2',
                    interface=interface,
                    region_name=region_name
                )
            
            LOG.debug(f"🐛 Adapter loaded successfully")
            
            # Get Mistral URL - use session catalog instead of adapter.catalog
            try:
                # Try endpoint_override first
                if hasattr(adapter, 'endpoint_override') and adapter.endpoint_override:
                    mistral_url = adapter.endpoint_override
                else:
                    # Use session to get URL from service catalog
                    mistral_url = session.get_endpoint(service_type='workflowv2', interface=adapter.interface, region_name=adapter.region_name)
            except Exception as url_e:
                LOG.error(f"⧱ Failed to discover Mistral URL: {url_e}")
                # Fallback: try to get from configuration
                section_conf = getattr(self.CONF, mistralclient_auth_section)
                mistral_url = getattr(section_conf, 'mistral_url', None)
                if not mistral_url:
                    raise Exception(f"Could not determine Mistral URL: {url_e}")
            
            LOG.debug(f"🐛 Mistral URL: {mistral_url}")
            
            # Create client
            self.client = mistral_client.client(
                mistral_url=mistral_url,
                session=session
            )
            LOG.debug(f"🐛 Mistral client created successfully")
            
            LOG.info(f"⚙ Successfully initialized Mistral client using {mistralclient_auth_section} section")
            
        except Exception as e:
            LOG.error(f"⧱ Failed to initialize Mistral client: {e}", exc_info=True)
            self.client = None
    
    def start_workflow(self, workflow_input=None):
        """Start a Mistral workflow execution using configured workflow name."""
        if not self.client:
            LOG.error(f"⧱ Mistral client not initialized")
            return None
            
        try:
            workflow_name = self.CONF.worker.mistral_workflow_name
            LOG.debug(f"🐛 Starting workflow {workflow_name}")
            LOG.debug(f"🐛 Workflow input: {workflow_input}")
            
            # Use the correct parameter names from the signature
            execution = self.client.executions.create(
                wf_identifier=workflow_name,
                workflow_input=workflow_input or {}
            )
                
            LOG.debug(f"🐛 Started workflow {workflow_name} with execution ID {execution.id}")
            return execution.to_dict()
        except Exception as e:
            LOG.error(f"⧱ Failed to start workflow {workflow_name}: {e}")
            import traceback
            LOG.error(f"⧱ Full traceback: {traceback.format_exc()}")
            return None
    
    def get_execution(self, execution_id):
        """Get workflow execution details."""
        if not self.client:
            LOG.error(f"⧱ Mistral client not initialized")
            return None
            
        try:
            execution = self.client.executions.get(execution_id)
            LOG.debug(f"🐛 Retrieved execution {execution_id} with state {execution.state}")
            return execution.to_dict()
        except Exception as e:
            LOG.error(f"⧱ Failed to get execution {execution_id}: {e}")
            return None

    def get_execution_output(self, execution_id):
        """Get workflow execution output details."""
        if not self.client:
            LOG.error(f"⧱ Mistral client not initialized")
            return None
            
        try:
            # Get full execution (fields parameter not supported in this version)
            execution = self.client.executions.get(execution_id)
            return execution.to_dict()
        except Exception as e:
            LOG.error(f"⧱ Failed to get execution output {execution_id}: {e}")
            return None
    
    def list_executions(self, **kwargs):
        """List workflow executions."""
        if not self.client:
            LOG.error(f"⧱ Mistral client not initialized")
            return []
            
        try:
            executions = self.client.executions.list(**kwargs)
            LOG.debug(f"🐛 Listed {len(executions)} executions")
            return [exec.to_dict() for exec in executions]
        except Exception as e:
            LOG.error(f"⧱ Failed to list executions: {e}")
            return []

    def get_migration_output(self, execution_id):
        """Get output from the execute_command task of a migration workflow.
        
        :param execution_id: Workflow execution ID
        :returns: Dictionary containing stdout, stderr, and exit_code
        """
        if not self.client:
            LOG.error(f"⧱ Mistral client not initialized")
            raise Exception("Mistral client not initialized")
            
        try:
            LOG.debug(f"🐛 Getting migration output for execution {execution_id}")
            
            # First, get all task executions for this workflow
            task_executions = self.client.tasks.list(workflow_execution_id=execution_id)
            LOG.debug(f"🐛 Found {len(task_executions)} task executions")
            
            # Find the execute_command task
            execute_command_task = None
            for task in task_executions:
                if task.name == 'execute_command':
                    execute_command_task = task
                    break
            
            if not execute_command_task:
                LOG.error(f"⧱ No execute_command task found in workflow execution {execution_id}")
                raise Exception(f"No execute_command task found in workflow execution {execution_id}")
            
            LOG.debug(f"🐛 Found execute_command task {execute_command_task.id}")
            
            # Get the full task object with result using tasks.get()
            full_task = self.client.tasks.get(execute_command_task.id)
            task_result = full_task.result
            LOG.debug(f"🐛 Task result retrieved: {type(task_result)}")
            
            # Your SSHAction returns properly structured result, just return it
            if isinstance(task_result, dict):
                LOG.debug(f"✅ Retrieved migration output with exit_code {task_result.get('exit_code', 0)}")
                return task_result
            else:
                # Handle string results or other formats
                LOG.warning(f"⚠ Unexpected task result type: {type(task_result)}")
                # Try to parse as JSON if it's a string
                if isinstance(task_result, str):
                    try:
                        import json
                        parsed_result = json.loads(task_result)
                        if isinstance(parsed_result, dict):
                            LOG.debug(f"✅ Parsed JSON result")
                            return parsed_result
                    except Exception:
                        LOG.debug(f"🐛 Result is string but not valid JSON")
                        pass
                # Fallback
                return {
                    'stdout': str(task_result) if task_result else '',
                    'stderr': '',
                    'exit_code': 0
                }
                
        except Exception as e:
            # Log the error for debugging
            LOG.error(f"⧱ Failed to get migration output for execution {execution_id}: {str(e)}")
            raise Exception(f"Failed to retrieve migration output: {str(e)}")
