"""
Unified Interface for Cross-Platform Operations
Common interface for all connectors with standardized methods
"""

from abc import ABC, abstractmethod
from typing import Dict, List, Any, Optional, Union, Type
from dataclasses import dataclass
from enum import Enum
from .exceptions import ConnectorError, NotImplementedError
from .logging_config import get_logger, LoggerMixin

logger = get_logger(__name__)


class ContentType(Enum):
    """Content types for unified operations"""
    PAGE = "page"
    ISSUE = "issue"
    ATTACHMENT = "attachment"
    COMMENT = "comment"
    SPACE = "space"
    PROJECT = "project"


class OperationType(Enum):
    """Operation types for unified interface"""
    READ = "read"
    WRITE = "write"
    UPDATE = "update"
    DELETE = "delete"
    SEARCH = "search"
    LIST = "list"


@dataclass
class UnifiedContent:
    """Unified content representation"""
    id: str
    title: str
    content_type: ContentType
    content: Optional[str] = None
    metadata: Optional[Dict[str, Any]] = None
    url: Optional[str] = None
    created_date: Optional[str] = None
    modified_date: Optional[str] = None
    author: Optional[str] = None
    
    def __post_init__(self):
        """Set default values"""
        if self.metadata is None:
            self.metadata = {}


@dataclass
class UnifiedSearchRequest:
    """Unified search request"""
    query: str
    content_types: List[ContentType]
    filters: Optional[Dict[str, Any]] = None
    limit: int = 25
    offset: int = 0
    
    def __post_init__(self):
        """Set default values"""
        if self.filters is None:
            self.filters = {}


@dataclass
class UnifiedSearchResult:
    """Unified search result"""
    items: List[UnifiedContent]
    total_count: int
    has_more: bool
    next_offset: Optional[int] = None


class UnifiedConnector(ABC, LoggerMixin):
    """Abstract base class for unified connector interface"""
    
    def __init__(self, name: str, config: Optional[Dict[str, Any]] = None):
        self.name = name
        self.config = config or {}
        self.logger = get_logger(f"connectors.{name}")
    
    @abstractmethod
    async def test_connection(self) -> bool:
        """Test connection to the service"""
        pass
    
    @abstractmethod
    async def get_content(self, content_id: str, content_type: ContentType) -> UnifiedContent:
        """Get content by ID and type"""
        pass
    
    @abstractmethod
    async def create_content(self, content: UnifiedContent) -> UnifiedContent:
        """Create new content"""
        pass
    
    @abstractmethod
    async def update_content(self, content_id: str, content: UnifiedContent) -> UnifiedContent:
        """Update existing content"""
        pass
    
    @abstractmethod
    async def delete_content(self, content_id: str, content_type: ContentType) -> bool:
        """Delete content by ID and type"""
        pass
    
    @abstractmethod
    async def search_content(self, request: UnifiedSearchRequest) -> UnifiedSearchResult:
        """Search content"""
        pass
    
    @abstractmethod
    async def list_content(
        self, 
        content_type: ContentType, 
        parent_id: Optional[str] = None,
        limit: int = 25,
        offset: int = 0
    ) -> List[UnifiedContent]:
        """List content of specific type"""
        pass
    
    async def get_supported_content_types(self) -> List[ContentType]:
        """Get list of supported content types"""
        return list(ContentType)
    
    async def get_capabilities(self) -> Dict[str, Any]:
        """Get connector capabilities"""
        return {
            'supported_types': await self.get_supported_content_types(),
            'supported_operations': [op.value for op in OperationType],
            'features': {}
        }
    
    async def batch_operation(
        self,
        operation: OperationType,
        contents: List[Union[UnifiedContent, str]],
        **kwargs
    ) -> List[Any]:
        """Perform batch operation on multiple contents"""
        results = []
        
        for content in contents:
            try:
                if operation == OperationType.READ:
                    if isinstance(content, str):
                        result = await self.get_content(content, ContentType.PAGE)
                    else:
                        result = await self.get_content(content.id, content.content_type)
                elif operation == OperationType.DELETE:
                    if isinstance(content, str):
                        result = await self.delete_content(content, ContentType.PAGE)
                    else:
                        result = await self.delete_content(content.id, content.content_type)
                else:
                    raise NotImplementedError(f"Batch {operation.value} not supported")
                
                results.append(result)
                
            except Exception as e:
                self.logger.error(f"Batch operation failed for item {content}: {e}")
                results.append({'error': str(e), 'item': content})
        
        return results
    
    async def close(self) -> None:
        """Close connector and cleanup resources"""
        pass


class UnifiedConfluenceConnector(UnifiedConnector):
    """Unified connector for Confluence"""
    
    def __init__(self, confluence_connector, confluence_operations):
        super().__init__("confluence")
        self.confluence = confluence_connector
        self.operations = confluence_operations
    
    async def test_connection(self) -> bool:
        """Test connection to Confluence"""
        try:
            return self.confluence.test_connection()
        except Exception as e:
            self.logger.error(f"Confluence connection test failed: {e}")
            return False
    
    async def get_content(self, content_id: str, content_type: ContentType) -> UnifiedContent:
        """Get content from Confluence"""
        if content_type == ContentType.PAGE:
            page_data = self.operations.read_page(content_id)
            if not page_data["success"]:
                raise ConnectorError(f"Failed to get page {content_id}: {page_data.get('error')}")
            
            return UnifiedContent(
                id=content_id,
                title=page_data["metadata"]["title"],
                content_type=content_type,
                content=page_data["content"],
                metadata=page_data["metadata"],
                url=page_data["metadata"]["url"],
                created_date=page_data["metadata"]["created_date"],
                modified_date=page_data["metadata"]["modified_date"],
                author=page_data["metadata"]["author"].get("displayName")
            )
        else:
            raise NotImplementedError(f"Content type {content_type} not supported")
    
    async def create_content(self, content: UnifiedContent) -> UnifiedContent:
        """Create content in Confluence"""
        if content.content_type == ContentType.PAGE:
            # Extract space key from metadata or use default
            space_key = content.metadata.get("space_key", "DEFAULT")
            
            result = self.operations.write_page(
                space_key=space_key,
                title=content.title,
                content=content.content or "",
                labels=content.metadata.get("labels")
            )
            
            if not result["success"]:
                raise ConnectorError(f"Failed to create page: {result.get('error')}")
            
            # Return created content
            return await self.get_content(result["page_id"], content.content_type)
        else:
            raise NotImplementedError(f"Content type {content.content_type} not supported")
    
    async def update_content(self, content_id: str, content: UnifiedContent) -> UnifiedContent:
        """Update content in Confluence"""
        if content.content_type == ContentType.PAGE:
            # Update the page
            updated_page = self.confluence.update_page(
                page_id=content_id,
                title=content.title,
                content=content.content
            )
            
            # Return updated content
            return await self.get_content(content_id, content.content_type)
        else:
            raise NotImplementedError(f"Content type {content.content_type} not supported")
    
    async def delete_content(self, content_id: str, content_type: ContentType) -> bool:
        """Delete content from Confluence"""
        # Note: Confluence connector doesn't currently implement delete
        raise NotImplementedError("Delete operation not implemented for Confluence")
    
    async def search_content(self, request: UnifiedSearchRequest) -> UnifiedSearchResult:
        """Search content in Confluence"""
        if ContentType.PAGE in request.content_types:
            results = self.operations.search_pages_by_text(
                request.query,
                limit=request.limit
            )
            
            unified_items = []
            for result in results:
                unified_items.append(UnifiedContent(
                    id=result.get("id"),
                    title=result.get("title"),
                    content_type=ContentType.PAGE,
                    metadata=result,
                    url=f"{self.confluence.confluence_url}/pages/viewpage.action?pageId={result.get('id')}"
                ))
            
            return UnifiedSearchResult(
                items=unified_items,
                total_count=len(unified_items),
                has_more=len(unified_items) >= request.limit,
                next_offset=request.offset + len(unified_items) if len(unified_items) >= request.limit else None
            )
        else:
            return UnifiedSearchResult(items=[], total_count=0, has_more=False)
    
    async def list_content(
        self, 
        content_type: ContentType, 
        parent_id: Optional[str] = None,
        limit: int = 25,
        offset: int = 0
    ) -> List[UnifiedContent]:
        """List content from Confluence"""
        if content_type == ContentType.PAGE:
            if parent_id:
                # List child pages
                children = self.confluence.get_page_children(parent_id)
                unified_items = []
                
                for child in children[:limit]:
                    unified_items.append(UnifiedContent(
                        id=child.get("id"),
                        title=child.get("title"),
                        content_type=content_type,
                        metadata=child,
                        url=f"{self.confluence.confluence_url}/pages/viewpage.action?pageId={child.get('id')}"
                    ))
                
                return unified_items
            else:
                # List all pages (requires space key)
                raise NotImplementedError("Listing all pages requires space specification")
        else:
            raise NotImplementedError(f"Content type {content_type} not supported")
    
    async def get_supported_content_types(self) -> List[ContentType]:
        """Get supported content types for Confluence"""
        return [ContentType.PAGE, ContentType.ATTACHMENT, ContentType.COMMENT]


class UnifiedJiraConnector(UnifiedConnector):
    """Unified connector for Jira"""
    
    def __init__(self, jira_connector, jira_operations):
        super().__init__("jira")
        self.jira = jira_connector
        self.operations = jira_operations
    
    async def test_connection(self) -> bool:
        """Test connection to Jira"""
        try:
            return self.jira.test_connection()
        except Exception as e:
            self.logger.error(f"Jira connection test failed: {e}")
            return False
    
    async def get_content(self, content_id: str, content_type: ContentType) -> UnifiedContent:
        """Get content from Jira"""
        if content_type == ContentType.ISSUE:
            issue_data = self.operations.read_issue(content_id)
            if not issue_data["success"]:
                raise ConnectorError(f"Failed to get issue {content_id}: {issue_data.get('error')}")
            
            metadata = issue_data["metadata"]
            fields = metadata.get("fields", {})
            
            return UnifiedContent(
                id=content_id,
                title=fields.get("summary", ""),
                content_type=content_type,
                content=fields.get("description", ""),
                metadata=metadata,
                url=f"{self.jira.jira_url}/browse/{content_id}",
                created_date=metadata.get("created"),
                modified_date=metadata.get("updated"),
                author=fields.get("reporter", {}).get("displayName")
            )
        else:
            raise NotImplementedError(f"Content type {content_type} not supported")
    
    async def create_content(self, content: UnifiedContent) -> UnifiedContent:
        """Create content in Jira"""
        if content.content_type == ContentType.ISSUE:
            # Extract project key from metadata
            project_key = content.metadata.get("project_key", "DEFAULT")
            issue_type = content.metadata.get("issue_type", "Task")
            
            result = self.operations.write_issue(
                project_key=project_key,
                issue_type=issue_type,
                summary=content.title,
                description=content.content
            )
            
            if not result["success"]:
                raise ConnectorError(f"Failed to create issue: {result.get('error')}")
            
            # Return created content
            return await self.get_content(result["issue_key"], content.content_type)
        else:
            raise NotImplementedError(f"Content type {content.content_type} not supported")
    
    async def update_content(self, content_id: str, content: UnifiedContent) -> UnifiedContent:
        """Update content in Jira"""
        if content.content_type == ContentType.ISSUE:
            # Update the issue
            result = self.operations.write_issue_update(
                content_id,
                summary=content.title,
                description=content.content
            )
            
            if not result["success"]:
                raise ConnectorError(f"Failed to update issue: {result.get('error')}")
            
            # Return updated content
            return await self.get_content(content_id, content.content_type)
        else:
            raise NotImplementedError(f"Content type {content.content_type} not supported")
    
    async def delete_content(self, content_id: str, content_type: ContentType) -> bool:
        """Delete content from Jira"""
        # Note: Jira connector doesn't currently implement delete
        raise NotImplementedError("Delete operation not implemented for Jira")
    
    async def search_content(self, request: UnifiedSearchRequest) -> UnifiedSearchResult:
        """Search content in Jira"""
        if ContentType.ISSUE in request.content_types:
            results = self.operations.search_issues_by_text(
                request.query,
                limit=request.limit
            )
            
            unified_items = []
            for result in results:
                metadata = result.get("metadata", {})
                fields = metadata.get("fields", {})
                
                unified_items.append(UnifiedContent(
                    id=metadata.get("key"),
                    title=fields.get("summary", ""),
                    content_type=ContentType.ISSUE,
                    metadata=metadata,
                    url=f"{self.jira.jira_url}/browse/{metadata.get('key')}"
                ))
            
            return UnifiedSearchResult(
                items=unified_items,
                total_count=len(unified_items),
                has_more=len(unified_items) >= request.limit,
                next_offset=request.offset + len(unified_items) if len(unified_items) >= request.limit else None
            )
        else:
            return UnifiedSearchResult(items=[], total_count=0, has_more=False)
    
    async def list_content(
        self, 
        content_type: ContentType, 
        parent_id: Optional[str] = None,
        limit: int = 25,
        offset: int = 0
    ) -> List[UnifiedContent]:
        """List content from Jira"""
        if content_type == ContentType.ISSUE:
            if parent_id:
                # List issues for project
                issues = self.operations.read_project_issues(parent_id, limit=limit)
                unified_items = []
                
                for issue_data in issues:
                    if issue_data["success"]:
                        metadata = issue_data["metadata"]
                        fields = metadata.get("fields", {})
                        
                        unified_items.append(UnifiedContent(
                            id=metadata.get("key"),
                            title=fields.get("summary", ""),
                            content_type=content_type,
                            metadata=metadata,
                            url=f"{self.jira.jira_url}/browse/{metadata.get('key')}"
                        ))
                
                return unified_items
            else:
                raise NotImplementedError("Listing all issues requires project specification")
        else:
            raise NotImplementedError(f"Content type {content_type} not supported")
    
    async def get_supported_content_types(self) -> List[ContentType]:
        """Get supported content types for Jira"""
        return [ContentType.ISSUE, ContentType.COMMENT]


class UnifiedConnectorFactory:
    """Factory for creating unified connectors"""
    
    @staticmethod
    def create_confluence_connector(confluence_connector, confluence_operations) -> UnifiedConfluenceConnector:
        """Create unified Confluence connector"""
        return UnifiedConfluenceConnector(confluence_connector, confluence_operations)
    
    @staticmethod
    def create_jira_connector(jira_connector, jira_operations) -> UnifiedJiraConnector:
        """Create unified Jira connector"""
        return UnifiedJiraConnector(jira_connector, jira_operations)
    
    @staticmethod
    def get_unified_connector(connector_type: str, *args, **kwargs) -> UnifiedConnector:
        """Get unified connector by type"""
        if connector_type.lower() == "confluence":
            return UnifiedConnectorFactory.create_confluence_connector(*args, **kwargs)
        elif connector_type.lower() == "jira":
            return UnifiedConnectorFactory.create_jira_connector(*args, **kwargs)
        else:
            raise ValueError(f"Unknown connector type: {connector_type}")


# Utility functions for working with unified interface
def convert_to_unified_content(data: Dict[str, Any], content_type: ContentType) -> UnifiedContent:
    """Convert raw data to unified content format"""
    return UnifiedContent(
        id=data.get("id", ""),
        title=data.get("title", ""),
        content_type=content_type,
        content=data.get("content", ""),
        metadata=data.get("metadata", {}),
        url=data.get("url", ""),
        created_date=data.get("created_date"),
        modified_date=data.get("modified_date"),
        author=data.get("author")
    )


def create_search_request(
    query: str,
    content_types: List[str],
    filters: Optional[Dict[str, Any]] = None,
    limit: int = 25,
    offset: int = 0
) -> UnifiedSearchRequest:
    """Create unified search request"""
    type_mapping = {
        "page": ContentType.PAGE,
        "issue": ContentType.ISSUE,
        "attachment": ContentType.ATTACHMENT,
        "comment": ContentType.COMMENT,
        "space": ContentType.SPACE,
        "project": ContentType.PROJECT
    }
    
    mapped_types = []
    for ct in content_types:
        if ct.lower() in type_mapping:
            mapped_types.append(type_mapping[ct.lower()])
        else:
            try:
                mapped_types.append(ContentType(ct.lower()))
            except ValueError:
                raise ValueError(f"Unknown content type: {ct}")
    
    return UnifiedSearchRequest(
        query=query,
        content_types=mapped_types,
        filters=filters,
        limit=limit,
        offset=offset
    )
