"""
Configuration service for managing GeViSoft configuration
"""
from typing import Dict, Any
import structlog

from clients.sdk_bridge_client import sdk_bridge_client

logger = structlog.get_logger()


class ConfigurationService:
    """Service for configuration operations"""

    def __init__(self):
        """Initialize configuration service"""
        pass

    async def read_configuration(self) -> Dict[str, Any]:
        """
        Read and parse complete configuration from GeViServer

        Returns:
            Dictionary with configuration data and statistics
        """
        try:
            logger.info("configuration_service_reading_config")
            result = await sdk_bridge_client.read_configuration()

            if not result["success"]:
                logger.error("configuration_read_failed", error=result.get("error_message"))
                raise ValueError(f"Configuration read failed: {result.get('error_message')}")

            logger.info("configuration_read_success",
                       total_nodes=result["statistics"]["total_nodes"],
                       file_size=result["file_size"])

            return result

        except Exception as e:
            logger.error("configuration_service_read_failed", error=str(e), exc_info=True)
            raise

    async def export_configuration_json(self) -> Dict[str, Any]:
        """
        Export complete configuration as JSON

        Returns:
            Dictionary with JSON data and size
        """
        try:
            logger.info("configuration_service_exporting_json")
            result = await sdk_bridge_client.export_configuration_json()

            if not result["success"]:
                logger.error("configuration_export_failed", error=result.get("error_message"))
                raise ValueError(f"Configuration export failed: {result.get('error_message')}")

            logger.info("configuration_export_success", json_size=result["json_size"])

            return result

        except Exception as e:
            logger.error("configuration_service_export_failed", error=str(e), exc_info=True)
            raise

    async def modify_configuration(self, modifications: list) -> Dict[str, Any]:
        """
        Modify configuration values and write back to server

        Args:
            modifications: List of modifications to apply

        Returns:
            Dictionary with success status and count of modifications applied
        """
        try:
            logger.info("configuration_service_modifying",
                       modification_count=len(modifications))

            result = await sdk_bridge_client.modify_configuration(modifications)

            if not result["success"]:
                logger.error("configuration_modify_failed", error=result.get("error_message"))
                raise ValueError(f"Configuration modification failed: {result.get('error_message')}")

            logger.info("configuration_modify_success",
                       modifications_applied=result["modifications_applied"])

            return result

        except Exception as e:
            logger.error("configuration_service_modify_failed", error=str(e), exc_info=True)
            raise

    async def import_configuration(self, json_data: str) -> Dict[str, Any]:
        """
        Import complete configuration from JSON and write to GeViServer

        Args:
            json_data: Complete configuration as JSON string

        Returns:
            Dictionary with success status, bytes written, and nodes imported
        """
        try:
            logger.info("configuration_service_importing",
                       json_size=len(json_data))

            result = await sdk_bridge_client.import_configuration(json_data)

            if not result["success"]:
                logger.error("configuration_import_failed", error=result.get("error_message"))
                raise ValueError(f"Configuration import failed: {result.get('error_message')}")

            logger.info("configuration_import_success",
                       bytes_written=result["bytes_written"],
                       nodes_imported=result["nodes_imported"])

            return result

        except Exception as e:
            logger.error("configuration_service_import_failed", error=str(e), exc_info=True)
            raise

    async def read_action_mappings(self) -> Dict[str, Any]:
        """
        Read ONLY action mappings (Rules markers) from GeViServer
        Much faster than full configuration export

        Returns:
            Dictionary with action mappings list and count
        """
        try:
            logger.info("configuration_service_reading_action_mappings")
            result = await sdk_bridge_client.read_action_mappings()

            if not result["success"]:
                logger.error("action_mappings_read_failed", error=result.get("error_message"))
                raise ValueError(f"Action mappings read failed: {result.get('error_message')}")

            logger.info("action_mappings_read_success",
                       total_count=result["total_count"],
                       total_actions=sum(len(m["actions"]) for m in result["mappings"]))

            return result

        except Exception as e:
            logger.error("configuration_service_read_action_mappings_failed", error=str(e), exc_info=True)
            raise

    async def read_specific_markers(self, marker_names: list) -> Dict[str, Any]:
        """
        Read specific configuration markers by name

        Args:
            marker_names: List of marker names to extract (e.g., ["Rules", "Camera"])

        Returns:
            Dictionary with extracted nodes and statistics
        """
        try:
            logger.info("configuration_service_reading_specific_markers",
                       markers=marker_names)
            result = await sdk_bridge_client.read_specific_markers(marker_names)

            if not result["success"]:
                logger.error("specific_markers_read_failed", error=result.get("error_message"))
                raise ValueError(f"Specific markers read failed: {result.get('error_message')}")

            logger.info("specific_markers_read_success",
                       markers_found=result["markers_found"])

            return result

        except Exception as e:
            logger.error("configuration_service_read_specific_markers_failed", error=str(e), exc_info=True)
            raise

    async def create_action_mapping(self, mapping_data: dict) -> Dict[str, Any]:
        """
        Create a new action mapping

        Args:
            mapping_data: Dictionary with name, input_actions, output_actions

        Returns:
            Dictionary with success status and created mapping
        """
        try:
            logger.info("configuration_service_creating_action_mapping",
                       name=mapping_data.get("name"))

            result = await sdk_bridge_client.create_action_mapping(mapping_data)

            if not result["success"]:
                logger.error("action_mapping_create_failed", error=result.get("error_message"))
                raise ValueError(f"Action mapping creation failed: {result.get('error_message')}")

            logger.info("action_mapping_create_success")

            return result

        except Exception as e:
            logger.error("configuration_service_create_action_mapping_failed", error=str(e), exc_info=True)
            raise

    async def update_action_mapping(self, mapping_id: int, mapping_data: dict) -> Dict[str, Any]:
        """
        Update an existing action mapping

        Args:
            mapping_id: 1-based ID of mapping to update
            mapping_data: Dictionary with updated fields

        Returns:
            Dictionary with success status and updated mapping
        """
        try:
            logger.info("configuration_service_updating_action_mapping",
                       mapping_id=mapping_id)

            result = await sdk_bridge_client.update_action_mapping(mapping_id, mapping_data)

            if not result["success"]:
                logger.error("action_mapping_update_failed", error=result.get("error_message"))
                raise ValueError(f"Action mapping update failed: {result.get('error_message')}")

            logger.info("action_mapping_update_success", mapping_id=mapping_id)

            return result

        except Exception as e:
            logger.error("configuration_service_update_action_mapping_failed", error=str(e), exc_info=True)
            raise

    async def delete_action_mapping(self, mapping_id: int) -> Dict[str, Any]:
        """
        Delete an action mapping by ID

        Args:
            mapping_id: 1-based ID of mapping to delete

        Returns:
            Dictionary with success status and message
        """
        try:
            logger.info("configuration_service_deleting_action_mapping",
                       mapping_id=mapping_id)

            result = await sdk_bridge_client.delete_action_mapping(mapping_id)

            if not result["success"]:
                logger.error("action_mapping_delete_failed", error=result.get("error_message"))
                raise ValueError(f"Action mapping deletion failed: {result.get('error_message')}")

            logger.info("action_mapping_delete_success", mapping_id=mapping_id)

            return result

        except Exception as e:
            logger.error("configuration_service_delete_action_mapping_failed", error=str(e), exc_info=True)
            raise

    async def read_configuration_as_tree(self, max_depth: int = None) -> Dict[str, Any]:
        """
        Read configuration as hierarchical folder tree

        Args:
            max_depth: Maximum depth to traverse (None = unlimited, 1 = root level only)

        Returns:
            Dictionary with tree structure
        """
        try:
            logger.info("configuration_service_reading_tree", max_depth=max_depth)
            result = await sdk_bridge_client.read_configuration_tree()

            if not result["success"]:
                logger.error("configuration_tree_read_failed", error=result.get("error_message"))
                raise ValueError(f"Configuration tree read failed: {result.get('error_message')}")

            tree = result["tree"]

            # Apply depth limit if specified
            if max_depth is not None:
                tree = self._limit_tree_depth(tree, max_depth)

            logger.info("configuration_tree_read_success",
                       total_nodes=result["total_nodes"],
                       max_depth=max_depth)

            return tree

        except Exception as e:
            logger.error("configuration_service_read_tree_failed", error=str(e), exc_info=True)
            raise

    async def read_configuration_path(self, path: str) -> Dict[str, Any]:
        """
        Read a specific folder from configuration tree

        Args:
            path: Path to folder (e.g., "MappingRules" or "MappingRules/1")

        Returns:
            Dictionary with subtree
        """
        try:
            logger.info("configuration_service_reading_path", path=path)
            result = await sdk_bridge_client.read_configuration_tree()

            if not result["success"]:
                logger.error("configuration_tree_read_failed", error=result.get("error_message"))
                raise ValueError(f"Configuration tree read failed: {result.get('error_message')}")

            tree = result["tree"]

            # Navigate to requested path
            path_parts = path.split("/")
            current = tree

            for part in path_parts:
                if not part:  # Skip empty parts
                    continue

                # Find child with matching name
                if current.get("type") != "folder" or "children" not in current:
                    raise ValueError(f"Path '{path}' not found: '{part}' is not a folder")

                found = None
                for child in current["children"]:
                    if child.get("name") == part:
                        found = child
                        break

                if found is None:
                    raise ValueError(f"Path '{path}' not found: folder '{part}' does not exist")

                current = found

            logger.info("configuration_path_read_success", path=path)
            return current

        except ValueError:
            raise
        except Exception as e:
            logger.error("configuration_service_read_path_failed", path=path, error=str(e), exc_info=True)
            raise

    def _limit_tree_depth(self, node: Dict[str, Any], max_depth: int, current_depth: int = 0) -> Dict[str, Any]:
        """
        Limit tree depth by removing children beyond max_depth

        Args:
            node: Tree node
            max_depth: Maximum depth
            current_depth: Current depth (internal)

        Returns:
            Tree node with limited depth
        """
        if current_depth >= max_depth:
            # At max depth - remove children
            limited = {k: v for k, v in node.items() if k != "children"}
            return limited

        # Not at max depth yet - recurse into children
        result = node.copy()
        if "children" in node and node.get("type") == "folder":
            result["children"] = [
                self._limit_tree_depth(child, max_depth, current_depth + 1)
                for child in node["children"]
            ]

        return result

    async def create_server(self, server_data: dict) -> dict:
        """
        Create a new G-core server and persist to GeViServer

        Args:
            server_data: Dictionary with server configuration (id, alias, host, user, password, enabled, etc.)

        Returns:
            Dictionary with success status and created server
        """
        try:
            server_id = server_data.get("id")
            if not server_id:
                raise ValueError("Server ID is required")

            logger.info("configuration_service_creating_server", server_id=server_id)

            # Read current tree
            tree_result = await sdk_bridge_client.read_configuration_tree()
            if not tree_result["success"]:
                raise ValueError(f"Failed to read configuration tree: {tree_result.get('error_message')}")

            tree = tree_result["tree"]

            # Find GeViGCoreServer folder
            gcore_folder = self._find_child(tree, "GeViGCoreServer")
            if not gcore_folder:
                raise ValueError("GeViGCoreServer folder not found in configuration")

            # Check if server already exists
            if self._find_child(gcore_folder, server_id):
                raise ValueError(f"Server '{server_id}' already exists")

            # Create new server folder structure
            new_server = {
                "type": "folder",
                "name": server_id,
                "children": [
                    {"type": "string", "name": "Alias", "value": server_data.get("alias", "")},
                    {"type": "string", "name": "Host", "value": server_data.get("host", "")},
                    {"type": "string", "name": "User", "value": server_data.get("user", "")},
                    {"type": "string", "name": "Password", "value": server_data.get("password", "")},
                    {"type": "int32", "name": "Enabled", "value": 1 if server_data.get("enabled", True) else 0},
                    {"type": "int32", "name": "DeactivateEcho", "value": 1 if server_data.get("deactivateEcho", False) else 0},
                    {"type": "int32", "name": "DeactivateLiveCheck", "value": 1 if server_data.get("deactivateLiveCheck", False) else 0}
                ]
            }

            # Add server to GeViGCoreServer folder
            if "children" not in gcore_folder:
                gcore_folder["children"] = []
            gcore_folder["children"].append(new_server)

            # Write modified tree back to GeViServer
            write_result = await sdk_bridge_client.write_configuration_tree(tree)

            if not write_result["success"]:
                raise ValueError(f"Failed to write configuration: {write_result.get('error_message')}")

            logger.info("configuration_service_server_created", server_id=server_id,
                       bytes_written=write_result.get("bytes_written"))

            return {
                "success": True,
                "message": f"Server '{server_id}' created successfully",
                "server": server_data,
                "bytes_written": write_result.get("bytes_written")
            }

        except ValueError:
            raise
        except Exception as e:
            logger.error("configuration_service_create_server_failed", error=str(e), exc_info=True)
            raise

    async def update_server(self, server_id: str, server_data: dict) -> dict:
        """
        Update an existing G-core server and persist to GeViServer

        Args:
            server_id: ID of the server to update
            server_data: Dictionary with updated server configuration

        Returns:
            Dictionary with success status
        """
        try:
            logger.info("configuration_service_updating_server", server_id=server_id)

            # Read current tree
            tree_result = await sdk_bridge_client.read_configuration_tree()
            if not tree_result["success"]:
                raise ValueError(f"Failed to read configuration tree: {tree_result.get('error_message')}")

            tree = tree_result["tree"]

            # Find GeViGCoreServer folder
            gcore_folder = self._find_child(tree, "GeViGCoreServer")
            if not gcore_folder:
                raise ValueError("GeViGCoreServer folder not found in configuration")

            # Find the server to update
            server_folder = self._find_child(gcore_folder, server_id)
            if not server_folder:
                raise ValueError(f"Server '{server_id}' not found")

            # Update server properties
            children_dict = {c.get("name"): c for c in server_folder.get("children", [])}

            if "alias" in server_data:
                if "Alias" in children_dict:
                    children_dict["Alias"]["value"] = server_data["alias"]
                else:
                    server_folder.setdefault("children", []).append(
                        {"type": "string", "name": "Alias", "value": server_data["alias"]}
                    )

            if "host" in server_data:
                if "Host" in children_dict:
                    children_dict["Host"]["value"] = server_data["host"]
                else:
                    server_folder.setdefault("children", []).append(
                        {"type": "string", "name": "Host", "value": server_data["host"]}
                    )

            if "user" in server_data:
                if "User" in children_dict:
                    children_dict["User"]["value"] = server_data["user"]
                else:
                    server_folder.setdefault("children", []).append(
                        {"type": "string", "name": "User", "value": server_data["user"]}
                    )

            if "password" in server_data:
                if "Password" in children_dict:
                    children_dict["Password"]["value"] = server_data["password"]
                else:
                    server_folder.setdefault("children", []).append(
                        {"type": "string", "name": "Password", "value": server_data["password"]}
                    )

            if "enabled" in server_data:
                enabled_value = 1 if server_data["enabled"] else 0
                if "Enabled" in children_dict:
                    children_dict["Enabled"]["value"] = enabled_value
                else:
                    server_folder.setdefault("children", []).append(
                        {"type": "int32", "name": "Enabled", "value": enabled_value}
                    )

            if "deactivateEcho" in server_data:
                echo_value = 1 if server_data["deactivateEcho"] else 0
                if "DeactivateEcho" in children_dict:
                    children_dict["DeactivateEcho"]["value"] = echo_value
                else:
                    server_folder.setdefault("children", []).append(
                        {"type": "int32", "name": "DeactivateEcho", "value": echo_value}
                    )

            if "deactivateLiveCheck" in server_data:
                livecheck_value = 1 if server_data["deactivateLiveCheck"] else 0
                if "DeactivateLiveCheck" in children_dict:
                    children_dict["DeactivateLiveCheck"]["value"] = livecheck_value
                else:
                    server_folder.setdefault("children", []).append(
                        {"type": "int32", "name": "DeactivateLiveCheck", "value": livecheck_value}
                    )

            # Write modified tree back to GeViServer
            write_result = await sdk_bridge_client.write_configuration_tree(tree)

            if not write_result["success"]:
                raise ValueError(f"Failed to write configuration: {write_result.get('error_message')}")

            logger.info("configuration_service_server_updated", server_id=server_id,
                       bytes_written=write_result.get("bytes_written"))

            return {
                "success": True,
                "message": f"Server '{server_id}' updated successfully",
                "bytes_written": write_result.get("bytes_written")
            }

        except ValueError:
            raise
        except Exception as e:
            logger.error("configuration_service_update_server_failed", server_id=server_id, error=str(e), exc_info=True)
            raise

    async def delete_server(self, server_id: str) -> dict:
        """
        Delete a G-core server and persist to GeViServer

        Args:
            server_id: ID of the server to delete

        Returns:
            Dictionary with success status
        """
        try:
            logger.info("configuration_service_deleting_server", server_id=server_id)

            # Read current tree
            tree_result = await sdk_bridge_client.read_configuration_tree()
            if not tree_result["success"]:
                raise ValueError(f"Failed to read configuration tree: {tree_result.get('error_message')}")

            tree = tree_result["tree"]

            # Find GeViGCoreServer folder
            gcore_folder = self._find_child(tree, "GeViGCoreServer")
            if not gcore_folder:
                raise ValueError("GeViGCoreServer folder not found in configuration")

            # Find and remove the server
            if "children" not in gcore_folder:
                raise ValueError(f"Server '{server_id}' not found")

            server_index = None
            for i, child in enumerate(gcore_folder["children"]):
                if child.get("name") == server_id and child.get("type") == "folder":
                    server_index = i
                    break

            if server_index is None:
                raise ValueError(f"Server '{server_id}' not found")

            # Remove server from children list
            gcore_folder["children"].pop(server_index)

            # Write modified tree back to GeViServer
            write_result = await sdk_bridge_client.write_configuration_tree(tree)

            if not write_result["success"]:
                raise ValueError(f"Failed to write configuration: {write_result.get('error_message')}")

            logger.info("configuration_service_server_deleted", server_id=server_id,
                       bytes_written=write_result.get("bytes_written"))

            return {
                "success": True,
                "message": f"Server '{server_id}' deleted successfully",
                "bytes_written": write_result.get("bytes_written")
            }

        except ValueError:
            raise
        except Exception as e:
            logger.error("configuration_service_delete_server_failed", server_id=server_id, error=str(e), exc_info=True)
            raise

    def _find_child(self, parent: dict, child_name: str) -> dict:
        """
        Helper method to find a child node by name

        Args:
            parent: Parent node (folder)
            child_name: Name of child to find

        Returns:
            Child node or None if not found
        """
        if parent.get("type") != "folder" or "children" not in parent:
            return None

        for child in parent["children"]:
            if child.get("name") == child_name:
                return child

        return None
