"""
Basic CRUD operations for template management
"""

from typing import List, Optional, Dict, Any
from .base import TemplateManagerBase
from ..models import Template, Step, TallyfyError, TemplatesList
from email_validator import validate_email, EmailNotValidError

class TemplateBasicOperations(TemplateManagerBase):
    """Handles basic template CRUD operations"""

    def search_templates_by_name(self, org_id: str, template_name: str) -> str:
        """
        Search for template by name using the search endpoint.

        Args:
            org_id: Organization ID
            template_name: Name or partial name of the template to search for

        Returns:
            Template ID of the found template

        Raises:
            TallyfyError: If no template found, multiple matches, or search fails
        """
        self._validate_org_id(org_id)

        try:
            search_endpoint = f"organizations/{org_id}/search"
            search_params = {
                'on': 'blueprint',
                'per_page': '20',
                'q': template_name
            }

            search_response = self.sdk._make_request('GET', search_endpoint, params=search_params)

            if isinstance(search_response, dict) and 'blueprint' in search_response:
                template_data = search_response['blueprint']
                if 'data' in template_data and template_data['data']:
                    templates = template_data['data']

                    # First try exact match (case-insensitive)
                    exact_matches = [p for p in templates if p['title'].lower() == template_name.lower()]
                    if exact_matches:
                        return exact_matches[0]['id']
                    elif len(templates) == 1:
                        # Single search result, use it
                        return templates[0]['id']
                    else:
                        # Multiple matches found, provide helpful error with options
                        match_names = [f"'{p['title']}'" for p in templates[:10]]  # Show max 10
                        raise TallyfyError(
                            f"Multiple templates found matching '{template_name}': {', '.join(match_names)}. Please be more specific.")
                else:
                    raise TallyfyError(f"No template found matching name: {template_name}")
            else:
                raise TallyfyError(f"Search failed for template name: {template_name}")

        except TallyfyError:
            raise
        except Exception as e:
            self._handle_api_error(e, "search templates by name", org_id=org_id, template_name=template_name)

    def get_template(self, org_id: str, template_id: Optional[str] = None, template_name: Optional[str] = None) -> Optional[Template]:
        """
        Get a template (checklist) by its ID or name with full details including prerun fields,
        automated actions, linked tasks, and metadata.

        Args:
            org_id: Organization ID
            template_id: Template (checklist) ID
            template_name: Template (checklist) name

        Returns:
            Template object with complete template data

        Raises:
            TallyfyError: If the request fails
        """
        if not template_id and not template_name:
            raise ValueError("Either template_id or template_name must be provided")

        self._validate_org_id(org_id)

        try:
            # If template_name is provided but not template_id, search for the template first
            if template_name and not template_id:
                template_id = self.search_templates_by_name(org_id, template_name)

            endpoint = f"organizations/{org_id}/checklists/{template_id}"
            template_params = {'with': 'steps,automated_actions,prerun,tags'}
            response_data = self.sdk._make_request('GET', endpoint, params=template_params)

            template_data = self._extract_data(response_data)
            if template_data:
                return Template.from_dict(template_data)
            return None

        except TallyfyError:
            raise
        except Exception as e:
            self._handle_api_error(e, "get template", org_id=org_id, template_id=template_id)

    def get_all_templates(self, org_id: str, per_page: int = 100, page: int = 1) -> TemplatesList:
        """
        Get all templates (checklists) for an organization with pagination metadata.

        Args:
            org_id: Organization ID
            per_page: Number of templates to return per page (default: 100)
            page: Page number to fetch (default: 1)

        Returns:
            TemplatesList object containing list of templates and pagination metadata

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

        try:
            endpoint = f"organizations/{org_id}/checklists"
            params = {'per_page': per_page, 'page': page}
            response_data = self.sdk._make_request('GET', endpoint, params=params)

            if isinstance(response_data, dict):
                return TemplatesList.from_dict(response_data)
            else:
                self.sdk.logger.warning("Unexpected response format for templates list")
                return TemplatesList(data=[], meta=None)

        except TallyfyError:
            raise
        except Exception as e:
            self._handle_api_error(e, "get all templates", org_id=org_id)

    def update_template_metadata(self, org_id: str, template_id: str, **kwargs) -> Optional[Template]:
        """
        Update template metadata like title, summary, guidance, icons, etc.

        Args:
            org_id: Organization ID
            template_id: Template ID to update
            **kwargs: Template metadata fields to update (title, summary, guidance, icon, etc.)

        Returns:
            Updated Template object

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

        try:
            endpoint = f"organizations/{org_id}/checklists/{template_id}"

            # Build update data from kwargs
            update_data = {}
            allowed_fields = [
                'title', 'summary', 'guidance', 'icon', 'alias', 'webhook',
                'explanation_video', 'kickoff_title', 'kickoff_description',
                'is_public', 'is_featured', 'auto_naming', 'folderize_process',
                'tag_process', 'allow_launcher_change_name', 'is_pinned',
                'default_folder', 'folder_changeable_by_launcher',
                'type', 'starred', 'default_process_name_format',
                'can_add_oot', 'owner_id', 'users', 'groups', 'prerun'
            ]

            for field, value in kwargs.items():
                if field in allowed_fields:
                    update_data[field] = value
                else:
                    self.sdk.logger.warning(f"Ignoring unknown template field: {field}")

            if not update_data:
                raise ValueError("No valid template fields provided for update")

            response_data = self.sdk._make_request('PUT', endpoint, data=update_data)

            template_data = self._extract_data(response_data)
            if template_data:
                return Template.from_dict(template_data)
            return None

        except TallyfyError:
            raise
        except Exception as e:
            self._handle_api_error(e, "update template metadata", org_id=org_id, template_id=template_id)

    def get_template_with_steps(self, org_id: str, template_id: Optional[str] = None, template_name: Optional[str] = None) -> Optional[Dict[str, Any]]:
        """
        Get template with full step details and structure.

        Args:
            org_id: Organization ID
            template_id: Template ID to retrieve
            template_name: Template name to retrieve (alternative to template_id)

        Returns:
            Dictionary containing template data with full step details

        Raises:
            TallyfyError: If the request fails
        """
        if not template_id and not template_name:
            raise ValueError("Either template_id or template_name must be provided")

        self._validate_org_id(org_id)

        try:
            # If template_name is provided but not template_id, search for the template first
            if template_name and not template_id:
                template_id = self.search_templates_by_name(org_id, template_name)

            # Get template with steps included
            endpoint = f"organizations/{org_id}/checklists/{template_id}"
            params = {'with': 'steps,automated_actions,prerun'}

            response_data = self.sdk._make_request('GET', endpoint, params=params)

            template_data = self._extract_data(response_data)
            if template_data:
                return {
                    'template': Template.from_dict(template_data),
                    'raw_data': template_data,
                    'step_count': len(template_data.get('steps', [])),
                    'steps': template_data.get('steps', []),
                    'automation_count': len(template_data.get('automated_actions', [])),
                    'prerun_field_count': len(template_data.get('prerun', []))
                }
            return None

        except TallyfyError:
            raise
        except Exception as e:
            self._handle_api_error(e, "get template with steps", org_id=org_id, template_id=template_id)

    def duplicate_template(self, org_id: str, template_id: str, new_name: str, tenant: str) -> Optional[Template]:
        """
        Clone a template within the organization.

        Args:
            org_id: Organization ID (destination)
            template_id: Template ID to clone
            new_name: Title for the cloned template
            tenant: Organization ID to clone from (required)

        Returns:
            New Template object

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

        if not tenant:
            raise ValueError("tenant is required")

        try:
            endpoint = f"organizations/{org_id}/checklists/{template_id}/clone"

            form_data = {'tenant': tenant}
            if new_name:
                form_data['title'] = new_name

            response_data = self.sdk._make_request('POST', endpoint, form_data=form_data)

            template_data = self._extract_data(response_data)
            if template_data:
                return Template.from_dict(template_data)
            return None

        except TallyfyError:
            raise
        except Exception as e:
            self._handle_api_error(e, "duplicate template", org_id=org_id, template_id=template_id)

    def get_template_steps(self, org_id: str, template_id: str) -> List[Step]:
        """
        Get all steps of a template.

        Args:
            org_id: Organization ID
            template_id: Template ID

        Returns:
            List of Step objects

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

        try:
            endpoint = f"organizations/{org_id}/checklists/{template_id}/steps"
            response_data = self.sdk._make_request('GET', endpoint)

            if isinstance(response_data, dict) and 'data' in response_data:
                steps_data = response_data['data']
                return [Step.from_dict(step_data) for step_data in steps_data]
            elif isinstance(response_data, list):
                return [Step.from_dict(step_data) for step_data in response_data]
            else:
                self.sdk.logger.warning("Unexpected response format for template steps")
                return []

        except TallyfyError:
            raise
        except Exception as e:
            self._handle_api_error(e, "get template steps", org_id=org_id, template_id=template_id)

    def edit_description_on_step(self, org_id: str, template_id: str, step_id: str, description: str) -> Dict[str, Any]:
        """
        Edit the description/summary of a specific step in a template.

        Args:
            org_id: Organization ID
            template_id: Template ID
            step_id: Step ID to edit description for
            description: New description/summary text for the step

        Returns:
            Dictionary containing updated step information

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

        try:
            endpoint = f"organizations/{org_id}/checklists/{template_id}/steps/{step_id}"

            # Validate description
            if not isinstance(description, str):
                raise ValueError("Description must be a string")

            description = description.strip()
            if not description:
                raise ValueError("Description cannot be empty")

            # Fetch current step to get the required title field
            current_step_data = self.sdk._make_request('GET', endpoint)
            current_step = current_step_data.get('data', current_step_data) if isinstance(current_step_data, dict) else {}
            current_title = current_step.get('title', '')
            if not current_title:
                raise TallyfyError("Could not retrieve current step title required for update")

            # API requires title even when only updating summary
            update_data = {
                'title': current_title,
                'summary': description,
            }

            response_data = self.sdk._make_request('PUT', endpoint, data=update_data)

            if isinstance(response_data, dict):
                if 'data' in response_data:
                    return response_data['data']
                return response_data
            else:
                self.sdk.logger.warning("Unexpected response format for step description update")
                return {'success': True, 'updated_summary': description}

        except TallyfyError:
            raise
        except ValueError as e:
            raise TallyfyError(f"Invalid description data: {e}")
        except Exception as e:
            self._handle_api_error(e, "edit step description", org_id=org_id, template_id=template_id, step_id=step_id)

    def add_step_to_template(self, org_id: str, template_id: str, step_data: Dict[str, Any]) -> Dict[str, Any]:
        """
        Add a new step to a template.

        Args:
            org_id: Organization ID
            template_id: Template ID
            step_data: Dictionary containing step data including title, summary, position, etc.

        Returns:
            Dictionary containing created step information

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

        try:
            endpoint = f"organizations/{org_id}/checklists/{template_id}/steps"

            # Validate step data
            if not isinstance(step_data, dict):
                raise ValueError("Step data must be a dictionary")

            # Validate required fields
            if 'title' not in step_data or not step_data['title']:
                raise ValueError("Step title is required")

            title = step_data['title'].strip()
            if not title:
                raise ValueError("Step title cannot be empty")

            # Build step creation data with defaults
            create_data = {'title': title}

            # Add optional string fields
            optional_string_fields = ['summary', 'step_type', 'alias', 'webhook']
            for field in optional_string_fields:
                if field in step_data and step_data[field]:
                    create_data[field] = str(step_data[field]).strip()

            # Add optional integer fields
            if 'position' in step_data:
                position = step_data['position']
                if isinstance(position, int) and position > 0:
                    create_data['position'] = position
                else:
                    raise ValueError("Position must be a positive integer")

            if 'max_assignable' in step_data:
                max_assignable = step_data['max_assignable']
                if isinstance(max_assignable, int) and max_assignable > 0:
                    create_data['max_assignable'] = max_assignable
                elif max_assignable is not None:
                    raise ValueError("max_assignable must be a positive integer or None")

            # Add boolean fields
            boolean_fields = [
                'allow_guest_owners', 'skip_start_process', 'can_complete_only_assignees',
                'everyone_must_complete', 'prevent_guest_comment', 'is_soft_start_date',
                'role_changes_every_time'
            ]
            for field in boolean_fields:
                if field in step_data:
                    create_data[field] = bool(step_data[field])

            # Add assignees if provided
            if 'assignees' in step_data and step_data['assignees']:
                assignees_list = step_data['assignees']
                if isinstance(assignees_list, list):
                    for user_id in assignees_list:
                        if not isinstance(user_id, int):
                            raise ValueError(f"User ID {user_id} must be an integer")
                    create_data['assignees'] = assignees_list
                else:
                    raise ValueError("Assignees must be a list of user IDs")

            # Add guests if provided
            if 'guests' in step_data and step_data['guests']:
                guests_list = step_data['guests']
                if isinstance(guests_list, list):
                    for guest_email in guests_list:
                        try:
                            validation = validate_email(guest_email)
                            # The validated email address
                            email = validation.normalized
                        except EmailNotValidError as e:
                            raise ValueError(f"Invalid email address: {str(e)}")
                    create_data['guests'] = guests_list
                else:
                    raise ValueError("Guests must be a list of email addresses")
            
            # Add roles if provided
            if 'roles' in step_data and step_data['roles']:
                roles_list = step_data['roles']
                if isinstance(roles_list, list):
                    create_data['roles'] = [str(role) for role in roles_list]
                else:
                    raise ValueError("Roles must be a list of role names")

            create_data['checklist_id'] = template_id
            response_data = self.sdk._make_request('POST', endpoint, data=create_data)

            if isinstance(response_data, dict):
                if 'data' in response_data:
                    return response_data['data']
                return response_data
            else:
                self.sdk.logger.warning("Unexpected response format for step creation")
                return {'success': True, 'created_step': create_data}

        except TallyfyError:
            raise
        except ValueError as e:
            raise TallyfyError(f"Invalid step data: {e}")
        except Exception as e:
            self._handle_api_error(e, "add step to template", org_id=org_id, template_id=template_id)

    def create_template(self, org_id: str, title: str, type: str = 'procedure',
                        summary: Optional[str] = None, owner_id: Optional[int] = None,
                        guidance: Optional[str] = None, icon: Optional[str] = None,
                        starred: Optional[bool] = None,
                        is_public: Optional[bool] = None) -> Optional[Template]:
        """
        Create a new template (checklist).

        Args:
            org_id: Organization ID
            title: Template title (required)
            type: Template type - 'procedure', 'form', or 'document' (default: 'procedure')
            summary: Template summary/description
            owner_id: User ID to set as template owner
            guidance: Guidance text for the template
            icon: Icon identifier
            starred: Whether to star the template
            is_public: Whether the template is publicly accessible

        Returns:
            Created Template object

        Raises:
            TallyfyError: If the request fails
            ValueError: If required parameters are missing
        """
        self._validate_org_id(org_id)

        if not title or not isinstance(title, str):
            raise ValueError("title must be a non-empty string")

        valid_types = ['procedure', 'form', 'document']
        if type not in valid_types:
            raise ValueError(f"type must be one of: {', '.join(valid_types)}")

        try:
            endpoint = f"organizations/{org_id}/checklists"

            body: Dict[str, Any] = {'title': title, 'type': type}

            if summary is not None:
                body['summary'] = summary
            if owner_id is not None:
                body['owner_id'] = owner_id
            if guidance is not None:
                body['guidance'] = guidance
            if icon is not None:
                body['icon'] = icon
            if starred is not None:
                body['starred'] = starred
            if is_public is not None:
                body['is_public'] = is_public

            response_data = self.sdk._make_request('POST', endpoint, data=body)

            template_data = self._extract_data(response_data)
            if template_data:
                return Template.from_dict(template_data)
            return None

        except TallyfyError:
            raise
        except Exception as e:
            self._handle_api_error(e, "create template", org_id=org_id, title=title)

    def delete_template(self, org_id: str, template_id: str) -> bool:
        """
        Delete (soft-delete) a template.

        Args:
            org_id: Organization ID
            template_id: Template ID to delete

        Returns:
            True if deleted successfully

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

        try:
            endpoint = f"organizations/{org_id}/checklists/{template_id}"
            self.sdk._make_request('DELETE', endpoint)
            return True

        except TallyfyError:
            raise
        except Exception as e:
            self._handle_api_error(e, "delete template", org_id=org_id, template_id=template_id)

    def delete_step(self, org_id: str, template_id: str, step_id: str) -> bool:
        """
        Delete a step from a template.

        Args:
            org_id: Organization ID
            template_id: Template ID
            step_id: Step ID to delete

        Returns:
            True if deleted successfully

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

        try:
            endpoint = f"organizations/{org_id}/checklists/{template_id}/steps/{step_id}"
            self.sdk._make_request('DELETE', endpoint)
            return True

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

    def clone_step(self, org_id: str, template_id: str, step_id: str) -> Dict[str, Any]:
        """
        Clone (duplicate) a step within a template.

        Args:
            org_id: Organization ID
            template_id: Template ID
            step_id: Step ID to clone

        Returns:
            Dictionary containing the cloned step data

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

        try:
            endpoint = f"organizations/{org_id}/checklists/{template_id}/steps/{step_id}/clone"
            response_data = self.sdk._make_request('POST', endpoint)

            if isinstance(response_data, dict):
                if 'data' in response_data:
                    return response_data['data']
                return response_data
            return {'success': True}

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

    def reorder_step(self, org_id: str, template_id: str, step_id: str, position: int) -> Dict[str, Any]:
        """
        Reorder a step within a template.

        Args:
            org_id: Organization ID
            template_id: Template ID
            step_id: Step ID to reorder
            position: New position for the step (1-based)

        Returns:
            Dictionary containing the updated step data

        Raises:
            TallyfyError: If the request fails
            ValueError: If position is not a positive integer
        """
        self._validate_org_id(org_id)
        self._validate_template_id(template_id)

        if not isinstance(position, int) or position < 1:
            raise ValueError("position must be a positive integer")

        try:
            endpoint = f"organizations/{org_id}/checklists/{template_id}/steps/{step_id}/reorder"
            body = {'position': position}
            response_data = self.sdk._make_request('PUT', endpoint, data=body)

            if isinstance(response_data, dict):
                if 'data' in response_data:
                    return response_data['data']
                return response_data
            return {'success': True}

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

    def get_kickoff_form_fields(self, org_id: str, template_id: str) -> List[Dict[str, Any]]:
        """
        Get kick-off (prerun) form fields for a template.

        Args:
            org_id: Organization ID
            template_id: Template ID

        Returns:
            List of kick-off form field dictionaries

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

        try:
            endpoint = f"organizations/{org_id}/checklists/{template_id}/form-fields"
            response_data = self.sdk._make_request('GET', endpoint)

            if isinstance(response_data, dict) and 'data' in response_data:
                return response_data['data']
            elif isinstance(response_data, list):
                return response_data
            return []

        except TallyfyError:
            raise
        except Exception as e:
            self._handle_api_error(e, "get kickoff form fields", org_id=org_id, template_id=template_id)