"""
Template analysis functionality for step dependencies and visibility conditions
"""

from typing import List, Optional, Dict, Any
from .base import TemplateManagerBase
from ..models import Template, TallyfyError


class TemplateAnalysis(TemplateManagerBase):
    """Handles template analysis for step dependencies and visibility conditions"""

    def get_step_dependencies(self, org_id: str, template_id: str, step_id: str) -> Dict[str, Any]:
        """
        Analyze step dependencies and automation effects.

        Args:
            org_id: Organization ID
            template_id: Template ID
            step_id: Step ID to analyze dependencies for

        Returns:
            Dictionary containing dependency analysis with step info, automation rules, dependencies, and affected elements

        Raises:
            TallyfyError: If the request fails
        """
        self._validate_org_id(org_id)
        self._validate_template_id(template_id)

        try:
            # Get template with full data including steps and automations
            template_endpoint = f"organizations/{org_id}/checklists/{template_id}"
            template_params = {'with': 'steps,automated_actions,prerun'}
            template_response = self.sdk._make_request('GET', template_endpoint, params=template_params)

            template_data = self._extract_data(template_response)
            if not template_data:
                raise TallyfyError("Unable to retrieve template data for dependency analysis")

            # Find the target step
            steps = self._normalize_steps(template_data.get('steps', []))
            target_step = None
            for step in steps:
                if step.get('id') == step_id:
                    target_step = step
                    break

            if not target_step:
                raise TallyfyError(f"Step {step_id} not found in template {template_id}")

            # Analyze automations for dependencies
            automations = template_data.get('automated_actions', [])
            dependencies = {
                'incoming': [],  # Steps that affect this step
                'outgoing': [],  # Steps this step affects
                'field_dependencies': [],  # Form fields this step depends on
                'conditional_visibility': []  # Visibility conditions
            }

            step_position = target_step.get('position', 0)
            step_name_map = {s.get('id'): s.get('title', 'Unknown') for s in steps}

            # Analyze each automation rule
            for automation in automations:
                conditions = automation.get('conditions', [])
                # API may return actions under 'then_actions' or 'actions'
                actions = automation.get('then_actions') or automation.get('actions', [])

                # Check if this step is referenced in conditions
                step_in_conditions = False
                for condition in conditions:
                    # API uses conditionable_id; fall back to step_id for legacy
                    condition_step = condition.get('conditionable_id') or condition.get('step_id')
                    operation = condition.get('operation') or condition.get('type', '')

                    if condition_step == step_id:
                        step_in_conditions = True
                    elif condition_step:
                        # Check if this condition triggers actions that affect our step
                        for action in actions:
                            action_target = action.get('target_step_id') or action.get('step_id')
                            if action_target == step_id:
                                step_title = step_name_map.get(condition_step, 'Unknown')
                                dependencies['incoming'].append({
                                    'step_id': condition_step,
                                    'step_title': step_title,
                                    'condition_type': operation,
                                    'automation_id': automation.get('id'),
                                    'description': f"Step depends on '{step_title}' being {operation.replace('_', ' ')}"
                                })

                    # Check for field dependencies
                    condition_type = condition.get('type') or condition.get('operation', '')
                    if condition_type == 'field_value':
                        field_label = condition.get('field_label', 'Unknown Field')
                        field_value = condition.get('value', '')
                        dependencies['field_dependencies'].append({
                            'field_label': field_label,
                            'expected_value': field_value,
                            'condition_type': 'field_value',
                            'automation_id': automation.get('id'),
                            'description': f"Depends on field '{field_label}' having value '{field_value}'"
                        })

                # Check if this step triggers actions on other steps
                if step_in_conditions:
                    for action in actions:
                        action_target = action.get('target_step_id') or action.get('step_id')
                        if action_target and action_target != step_id:
                            action_type = action.get('action_type') or action.get('type', '')
                            action_verb = action.get('action_verb', '')
                            effective_action = f"{action_type}_{action_verb}" if action_verb else action_type

                            affected_title = step_name_map.get(action_target, 'Unknown')
                            dependencies['outgoing'].append({
                                'step_id': action_target,
                                'step_title': affected_title,
                                'action_type': effective_action,
                                'automation_id': automation.get('id'),
                                'description': f"Step triggers '{effective_action}' on '{affected_title}'"
                            })

                # Check for visibility conditions affecting this step
                for action in actions:
                    action_target = action.get('target_step_id') or action.get('step_id')
                    action_type = action.get('action_type') or action.get('type', '')
                    action_verb = action.get('action_verb', '')

                    is_visibility = (
                        action_type == 'visibility'
                        or action_type in ['show_step', 'hide_step']
                    )

                    if action_target == step_id and is_visibility:
                        if action_verb in ('show', 'hide'):
                            effective_verb = action_verb
                        elif action_type == 'show_step':
                            effective_verb = 'show'
                        elif action_type == 'hide_step':
                            effective_verb = 'hide'
                        else:
                            effective_verb = action_type

                        dependencies['conditional_visibility'].append({
                            'action_type': f'{effective_verb}_step',
                            'automation_id': automation.get('id'),
                            'description': f"Step visibility controlled by automation conditions"
                        })

            # Calculate dependency complexity score
            total_deps = (len(dependencies['incoming']) + len(dependencies['outgoing']) +
                         len(dependencies['field_dependencies']) + len(dependencies['conditional_visibility']))

            complexity_score = min(100, total_deps * 10)  # Cap at 100

            complexity_level = "Low"
            if complexity_score > 70:
                complexity_level = "High"
            elif complexity_score > 30:
                complexity_level = "Medium"

            return {
                'step_info': {
                    'id': step_id,
                    'title': target_step.get('title', 'Unknown'),
                    'position': step_position,
                    'summary': target_step.get('summary', '')
                },
                'dependencies': dependencies,
                'complexity_analysis': {
                    'score': complexity_score,
                    'level': complexity_level,
                    'total_dependencies': total_deps,
                    'incoming_count': len(dependencies['incoming']),
                    'outgoing_count': len(dependencies['outgoing']),
                    'field_dependencies_count': len(dependencies['field_dependencies']),
                    'visibility_conditions_count': len(dependencies['conditional_visibility'])
                },
                'recommendations': self._generate_dependency_recommendations(dependencies, complexity_level),
                'template_id': template_id
            }

        except TallyfyError:
            raise
        except Exception as e:
            self._handle_api_error(e, "analyze step dependencies", org_id=org_id, template_id=template_id, step_id=step_id)

    def get_step_visibility_conditions(self, org_id: str, template_id: str, step_id: str) -> Dict[str, Any]:
        """
        Analyze when/how steps become visible based on automations.

        Args:
            org_id: Organization ID
            template_id: Template ID
            step_id: Step ID to analyze visibility for

        Returns:
            Dictionary containing step visibility analysis, rules, behavior patterns, and recommendations

        Raises:
            TallyfyError: If the request fails
        """
        self._validate_org_id(org_id)
        self._validate_template_id(template_id)

        try:
            # Get template with automations and steps
            template_endpoint = f"organizations/{org_id}/checklists/{template_id}"
            template_params = {'with': 'steps,automated_actions,prerun'}
            template_response = self.sdk._make_request('GET', template_endpoint, params=template_params)

            template_data = self._extract_data(template_response)
            if not template_data:
                raise TallyfyError("Unable to retrieve template data for visibility analysis")

            # Find the target step
            steps = self._normalize_steps(template_data.get('steps', []))
            target_step = None
            for step in steps:
                if step.get('id') == step_id:
                    target_step = step
                    break

            if not target_step:
                raise TallyfyError(f"Step {step_id} not found in template {template_id}")

            automations = template_data.get('automated_actions', [])

            visibility_rules = {
                'show_rules': [],
                'hide_rules': [],
                'conditional_rules': []
            }

            step_name_map = {step.get('id'): step.get('title', 'Unknown') for step in steps}

            # Analyze automation rules affecting this step's visibility
            for automation in automations:
                automation_id = automation.get('id')
                conditions = automation.get('conditions', [])
                # API may return actions under 'then_actions' or 'actions'
                actions = automation.get('then_actions') or automation.get('actions', [])

                # Check if this automation affects our target step's visibility.
                for action in actions:
                    action_step_id = action.get('target_step_id') or action.get('step_id')
                    if action_step_id == step_id:
                        # API serializes visibility actions with action_type='visibility'
                        # and action_verb='show' or 'hide' (AutomatedActionTransformer)
                        action_type = action.get('action_type', '') or action.get('type', '')
                        action_verb = action.get('action_verb', '')

                        is_visibility = (
                            action_type == 'visibility'
                            or action_type in ['show_step', 'hide_step']
                        )

                        if is_visibility:
                            # Resolve effective verb
                            if action_verb in ('show', 'hide'):
                                effective_verb = action_verb
                            elif action_type == 'show_step':
                                effective_verb = 'show'
                            elif action_type == 'hide_step':
                                effective_verb = 'hide'
                            else:
                                effective_verb = ''

                            # Analyze conditions that trigger this visibility change
                            rule_conditions = []
                            for condition in conditions:
                                condition_summary = self._summarize_condition(condition, step_name_map)
                                rule_conditions.append(condition_summary)

                            condition_logic = automation.get('condition_logic', 'AND')
                            joiner = ' AND ' if condition_logic == 'AND' else ' OR '

                            rule_info = {
                                'automation_id': automation_id,
                                'action_type': f"{effective_verb}_step" if effective_verb else action_type,
                                'conditions': rule_conditions,
                                'conditions_count': len(conditions),
                                'rule_logic': condition_logic,
                                'description': f"Step will be {effective_verb}n when: {joiner.join(rule_conditions)}"
                            }

                            if effective_verb == 'show':
                                visibility_rules['show_rules'].append(rule_info)
                            elif effective_verb == 'hide':
                                visibility_rules['hide_rules'].append(rule_info)

            # Determine step's default visibility
            default_visibility = "visible"
            if visibility_rules['show_rules']:
                default_visibility = "conditionally_visible"

            # Analyze visibility complexity
            total_rules = len(visibility_rules['show_rules']) + len(visibility_rules['hide_rules'])

            complexity_level = "Simple"
            if total_rules > 3:
                complexity_level = "Complex"
            elif total_rules > 1:
                complexity_level = "Moderate"

            # Generate visibility behavior analysis
            behavior_patterns = []

            if not visibility_rules['show_rules'] and not visibility_rules['hide_rules']:
                behavior_patterns.append("Step is always visible (no conditional visibility rules)")

            if visibility_rules['show_rules']:
                behavior_patterns.append(f"Step becomes visible based on {len(visibility_rules['show_rules'])} condition(s)")

            if visibility_rules['hide_rules']:
                behavior_patterns.append(f"Step can be hidden based on {len(visibility_rules['hide_rules'])} condition(s)")

            if visibility_rules['show_rules'] and visibility_rules['hide_rules']:
                behavior_patterns.append("Step has both show and hide rules - visibility depends on rule evaluation order")

            return {
                'step_info': {
                    'id': step_id,
                    'title': target_step.get('title', 'Unknown'),
                    'default_visibility': default_visibility
                },
                'visibility_rules': visibility_rules,
                'behavior_analysis': {
                    'patterns': behavior_patterns,
                    'complexity_level': complexity_level,
                    'total_rules': total_rules,
                    'has_conditional_visibility': total_rules > 0
                },
                'recommendations': self._generate_visibility_recommendations(visibility_rules, complexity_level),
                'template_id': template_id
            }

        except TallyfyError:
            raise
        except Exception as e:
            self._handle_api_error(e, "analyze step visibility conditions", org_id=org_id, template_id=template_id, step_id=step_id)

    def _summarize_condition(self, condition: Dict, step_lookup: Dict[str, str]) -> str:
        """
        Creates a human-readable summary of a single condition.

        Handles both the current API format (conditionable_id, operation, statement)
        and legacy format (type, step_id, value).

        Args:
            condition: Condition dictionary from the API
            step_lookup: Mapping of step IDs to step titles

        Returns:
            Human-readable condition description
        """
        # Current API format: conditionable_id + operation + statement
        conditionable_id = condition.get('conditionable_id')
        operation = condition.get('operation', '')
        statement = condition.get('statement', '')

        # Legacy format fallback: type + step_id + value
        condition_type = condition.get('type', '')
        step_id = condition.get('step_id', '')

        # Resolve the step reference
        effective_step_id = conditionable_id or step_id
        step_name = step_lookup.get(effective_step_id, f"Step {effective_step_id}") if effective_step_id else None

        # Current API format
        if conditionable_id and operation:
            if operation == 'completed':
                return f"'{step_name}' is completed"
            elif operation == 'started':
                return f"'{step_name}' is started"
            elif operation == 'field_value':
                field_label = condition.get('field_label', 'Unknown Field')
                value = condition.get('value', '')
                return f"Field '{field_label}' equals '{value}'"
            else:
                qualifier = f" ({statement})" if statement else ""
                return f"'{step_name}' {operation}{qualifier}"

        # Legacy format
        if condition_type == 'step_completed':
            return f"'{step_name}' is completed"
        elif condition_type == 'step_started':
            return f"'{step_name}' is started"
        elif condition_type == 'field_value':
            field_label = condition.get('field_label', 'Unknown Field')
            value = condition.get('value', '')
            return f"Field '{field_label}' equals '{value}'"
        elif condition_type:
            return f"{condition_type} condition on '{step_name}'" if step_name else f"{condition_type} condition"

        # Fallback: return whatever info we have
        return f"Condition: {operation or condition_type or 'unknown'}"

    def _generate_visibility_recommendations(self, visibility_rules: Dict, complexity_level: str) -> List[str]:
        """
        Generates visibility-specific recommendations.

        Args:
            visibility_rules: Dictionary containing visibility rules
            complexity_level: String indicating complexity level

        Returns:
            List of recommendation strings
        """
        recommendations = []

        show_rules = visibility_rules.get('show_rules', [])
        hide_rules = visibility_rules.get('hide_rules', [])

        if complexity_level == "Complex":
            recommendations.append("Consider simplifying visibility conditions to improve workflow clarity")

        if show_rules and hide_rules:
            recommendations.append("Review show/hide rule interactions to prevent conflicts")

        if len(show_rules) > 2:
            recommendations.append("Multiple show conditions may create user confusion - consider consolidation")

        if not show_rules and not hide_rules:
            recommendations.append("Step has simple visibility - no optimization needed")

        return recommendations

    def _generate_dependency_recommendations(self, dependencies: Dict, complexity_level: str) -> List[str]:
        """
        Generates dependency-specific recommendations.

        Args:
            dependencies: Dictionary containing dependency information
            complexity_level: String indicating complexity level

        Returns:
            List of recommendation strings
        """
        recommendations = []

        incoming_count = len(dependencies.get('incoming', []))
        outgoing_count = len(dependencies.get('outgoing', []))

        if complexity_level == "High":
            recommendations.append("High dependency complexity - consider breaking step into smaller components")

        if incoming_count > 3:
            recommendations.append("Step has many dependencies - ensure all are necessary")

        if outgoing_count > 3:
            recommendations.append("Step affects many other steps - verify all impacts are intentional")

        if not incoming_count and not outgoing_count:
            recommendations.append("Step is independent - good for parallel execution")

        return recommendations
