"""
Confluence Core - Client
ConfluenceClient using curl_cffi with Chrome impersonation to bypass Cloudflare 403.
Similar to jira_core/client.py
"""

from typing import Optional, List, Dict, Any
from curl_cffi import requests as cffi_requests


class ConfluenceClient:
    """
    Confluence REST API client using curl_cffi with Chrome impersonation.
    Bypass Cloudflare protection (same approach as JiraClient).
    """
    
    def __init__(self, server: str, token: str = None, basic_auth: tuple = None):
        """
        Initialize Confluence client.
        
        Args:
            server: Confluence server URL (e.g. https://insight.fsoft.com.vn/conf)
            token: PAT token for authentication
            basic_auth: Tuple of (email, api_token) for basic auth
        """
        self.server = server.rstrip('/')
        self.session = cffi_requests.Session()
        
        # Build auth header
        if token:
            self.auth_header = f"Bearer {token}"
        elif basic_auth:
            import base64
            email, api_token = basic_auth
            credentials = base64.b64encode(f"{email}:{api_token}".encode()).decode()
            self.auth_header = f"Basic {credentials}"
        else:
            raise ValueError("Either token or basic_auth must be provided")
    
    def _request(self, method: str, endpoint: str, **kwargs) -> cffi_requests.Response:
        """Make a request to Confluence API."""
        url = f"{self.server}/rest/api/{endpoint.lstrip('/')}"
        
        headers = kwargs.pop('headers', {})
        headers['Authorization'] = self.auth_header
        headers['Content-Type'] = 'application/json'
        headers['Accept'] = 'application/json'
        # Additional headers to bypass firewall
        headers['X-Atlassian-Token'] = 'no-check'
        headers['Origin'] = self.server
        headers['Referer'] = f"{self.server}/"
        headers['User-Agent'] = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
        
        response = self.session.request(
            method, 
            url, 
            headers=headers,
            impersonate='chrome120',
            **kwargs
        )
        
        return response
    
    def _get(self, endpoint: str, **kwargs) -> dict:
        """GET request."""
        response = self._request('GET', endpoint, **kwargs)
        response.raise_for_status()
        return response.json()
    
    def _post(self, endpoint: str, json_data: dict = None, **kwargs) -> dict:
        """POST request."""
        response = self._request('POST', endpoint, json=json_data, **kwargs)
        if response.status_code >= 400:
            # Include response body for better error debugging
            error_body = response.text[:500] if response.text else "No response body"
            raise Exception(f"HTTP Error {response.status_code}: {error_body}")
        return response.json()
    
    def _put(self, endpoint: str, json_data: dict = None, **kwargs) -> dict:
        """PUT request."""
        response = self._request('PUT', endpoint, json=json_data, **kwargs)
        if response.status_code >= 400:
            error_body = response.text[:500] if response.text else "No response body"
            raise Exception(f"HTTP Error {response.status_code}: {error_body}")
        if response.text:
            return response.json()
        return {}
    
    def _delete(self, endpoint: str, **kwargs) -> None:
        """DELETE request."""
        response = self._request('DELETE', endpoint, **kwargs)
        response.raise_for_status()
    
    # ============================================================
    # USER & SERVER INFO
    # ============================================================
    
    def myself(self) -> dict:
        """Get current user info."""
        return self._get('user/current')
    
    def server_info(self) -> dict:
        """Get server info (space list as proxy)."""
        return self._get('space?limit=1')
    
    # ============================================================
    # SPACES
    # ============================================================
    
    def get_spaces(self, limit: int = 100) -> List['Space']:
        """Get all accessible spaces."""
        data = self._get(f'space?limit={limit}')
        return [Space(s) for s in data.get('results', [])]
    
    def get_space(self, space_key: str) -> 'Space':
        """Get space by key."""
        data = self._get(f'space/{space_key}')
        return Space(data)
    
    # ============================================================
    # PAGES
    # ============================================================
    
    def get_page(self, page_id: str, expand: str = 'body.storage,version,ancestors') -> 'Page':
        """Get page by ID with content and version info."""
        data = self._get(f'content/{page_id}?expand={expand}')
        return Page(self, data)
    
    def get_page_by_title(self, space_key: str, title: str) -> Optional['Page']:
        """Get page by title in a space."""
        # URL encode title
        import urllib.parse
        encoded_title = urllib.parse.quote(title)
        data = self._get(f'content?spaceKey={space_key}&title={encoded_title}&expand=body.storage,version,ancestors')
        results = data.get('results', [])
        if results:
            return Page(self, results[0])
        return None
    
    def create_page(self, space_key: str, title: str, body: str, 
                    parent_id: str = None) -> 'Page':
        """
        Create a new page.
        
        Args:
            space_key: Space key (e.g., 'AGENTVISTA')
            title: Page title
            body: Page content in Confluence storage format (XHTML)
            parent_id: Optional parent page ID to create under
        """
        payload = {
            'type': 'page',
            'title': title,
            'space': {'key': space_key},
            'body': {
                'storage': {
                    'value': body,
                    'representation': 'storage'
                }
            }
        }
        
        if parent_id:
            payload['ancestors'] = [{'id': parent_id}]
        
        data = self._post('content', json_data=payload)
        return Page(self, data)
    
    def update_page(self, page_id: str, title: str, body: str, 
                    version: int, message: str = None) -> 'Page':
        """
        Update an existing page.
        
        Args:
            page_id: Page ID
            title: New title
            body: New content in storage format
            version: Current version number (will be incremented)
            message: Optional version message
        """
        payload = {
            'type': 'page',
            'title': title,
            'body': {
                'storage': {
                    'value': body,
                    'representation': 'storage'
                }
            },
            'version': {
                'number': version + 1
            }
        }
        
        if message:
            payload['version']['message'] = message
        
        data = self._put(f'content/{page_id}', json_data=payload)
        return Page(self, data)
    
    def delete_page(self, page_id: str) -> None:
        """Delete a page."""
        self._delete(f'content/{page_id}')
    
    # ============================================================
    # PAGE TREE
    # ============================================================
    
    def get_space_root_pages(self, space_key: str, limit: int = 100) -> List['Page']:
        """Get root-level pages in a space (pages with no parent)."""
        # Get pages in space with ancestors expanded
        data = self._get(
            f'content?spaceKey={space_key}&type=page&expand=ancestors&limit={limit}'
        )
        
        # Filter to only root pages (no ancestors or empty ancestors)
        root_pages = []
        for page_data in data.get('results', []):
            ancestors = page_data.get('ancestors', [])
            if not ancestors:
                root_pages.append(Page(self, page_data))
        
        return root_pages
    
    def get_page_children(self, page_id: str, limit: int = 100) -> List['Page']:
        """Get child pages of a page."""
        data = self._get(f'content/{page_id}/child/page?limit={limit}&expand=ancestors')
        return [Page(self, p) for p in data.get('results', [])]
    
    def get_page_tree(self, space_key: str = None, parent_id: str = None, 
                      depth: int = 2) -> List[Dict]:
        """
        Get page tree structure for display.
        OPTIMIZED: Fetches all pages in ONE API call, then builds tree in memory.
        
        Args:
            space_key: Space key to get pages from
            parent_id: Parent page ID to get children from
            depth: How many levels deep to show (for display, not API calls)
            
        Returns:
            List of dicts with tree structure
        """
        # Fetch ALL pages in space with ancestors in ONE call
        all_pages_data = self._get(
            f'content?spaceKey={space_key}&type=page&limit=500&expand=ancestors'
        )
        all_pages = all_pages_data.get('results', [])
        
        # Build lookup maps - preserve order from API using ordered lists
        pages_by_id = {}
        children_map = {}  # parent_id -> [child_ids] - preserves order
        root_pages = []  # preserves order from API
        page_order = {}  # track original order for each page
        
        for idx, page_data in enumerate(all_pages):
            page_id = page_data.get('id')
            page_order[page_id] = idx  # Track original order
            pages_by_id[page_id] = {
                'id': page_id,
                'title': page_data.get('title'),
                'url': f"{self.server}{page_data.get('_links', {}).get('webui', '')}",
                'children': []
            }
            
            ancestors = page_data.get('ancestors', [])
            if ancestors:
                # Has parent - add to children_map
                parent_page_id = ancestors[-1].get('id')
                if parent_page_id not in children_map:
                    children_map[parent_page_id] = []
                children_map[parent_page_id].append(page_id)
            else:
                # Root page
                root_pages.append(page_id)
        
        # Build tree recursively from maps (no API calls!)
        def build_node(page_id: str, current_depth: int) -> dict:
            node = pages_by_id.get(page_id)
            if not node:
                return None
            
            result = {
                'id': node['id'],
                'title': node['title'],
                'url': node['url'],
                'children': []
            }
            
            if current_depth < depth:
                child_ids = children_map.get(page_id, [])
                for child_id in child_ids:
                    child_node = build_node(child_id, current_depth + 1)
                    if child_node:
                        result['children'].append(child_node)
                # Keep original order from API (no sorting)
            
            return result
        
        # Build tree from specified starting point
        if parent_id:
            # Return children of specific page
            child_ids = children_map.get(parent_id, [])
            tree = []
            for child_id in child_ids:
                node = build_node(child_id, 1)
                if node:
                    tree.append(node)
            # Keep original order (no sorting)
            return tree
        else:
            # Return from root pages
            tree = []
            for page_id in root_pages:
                node = build_node(page_id, 1)
                if node:
                    tree.append(node)
            # Keep original order (no sorting)
            return tree
    
    # ============================================================
    # SEARCH
    # ============================================================
    
    def search_pages(self, cql: str, limit: int = 50) -> List['Page']:
        """
        Search pages using CQL (Confluence Query Language).
        
        Args:
            cql: CQL query string (e.g., 'space=TEAM AND title~"test"')
            limit: Maximum results
        """
        import urllib.parse
        encoded_cql = urllib.parse.quote(cql)
        data = self._get(f'content/search?cql={encoded_cql}&limit={limit}&expand=ancestors')
        return [Page(self, r) for r in data.get('results', [])]
    
    def search_by_text(self, text: str, space_key: str = None, limit: int = 50) -> List['Page']:
        """Search pages by text content."""
        cql = f'type=page AND text~"{text}"'
        if space_key:
            cql = f'space={space_key} AND {cql}'
        return self.search_pages(cql, limit)
    
    # ============================================================
    # UTILITY
    # ============================================================
    
    def find_parent_page(self, space_key: str, parent_title: str) -> Optional['Page']:
        """Find a page by title to use as parent."""
        return self.get_page_by_title(space_key, parent_title)
    
    def close(self):
        """Close the session."""
        self.session.close()


class Page:
    """Represents a Confluence page."""
    
    def __init__(self, client: ConfluenceClient, data: dict):
        self._client = client
        self._data = data
        self.id = data.get('id')
        self.title = data.get('title')
        self.type = data.get('type')
        self.status = data.get('status')
        
        # Version info
        version_data = data.get('version', {})
        self.version = version_data.get('number', 1) if version_data else 1
        
        # Body content
        body_data = data.get('body', {})
        storage_data = body_data.get('storage', {})
        self.content = storage_data.get('value', '')
        
        # Ancestors (parent pages)
        self.ancestors = data.get('ancestors', [])
        
        # Links
        links = data.get('_links', {})
        self.web_url = links.get('webui', '')
    
    def get_url(self) -> str:
        """Get full URL to the page."""
        if self.web_url:
            return f"{self._client.server}{self.web_url}"
        # Construct URL from space and page ID
        return f"{self._client.server}/pages/viewpage.action?pageId={self.id}"
    
    def get_parent_id(self) -> Optional[str]:
        """Get parent page ID if exists."""
        if self.ancestors:
            return self.ancestors[-1].get('id')
        return None
    
    def get_space_key(self) -> Optional[str]:
        """Get space key from data."""
        space_data = self._data.get('space', {})
        return space_data.get('key')
    
    def update(self, title: str = None, body: str = None, message: str = None) -> 'Page':
        """Update this page."""
        new_title = title or self.title
        new_body = body if body is not None else self.content
        return self._client.update_page(self.id, new_title, new_body, self.version, message)
    
    def __repr__(self):
        return f"<Page id='{self.id}' title='{self.title}'>"


class Space:
    """Represents a Confluence space."""
    
    def __init__(self, data: dict):
        self.id = data.get('id')
        self.key = data.get('key')
        self.name = data.get('name')
        self.type = data.get('type')
        self.status = data.get('status')
        
        # Links
        links = data.get('_links', {})
        self.web_url = links.get('webui', '')
    
    def __repr__(self):
        return f"<Space key='{self.key}' name='{self.name}'>"


# Compatibility alias
Confluence = ConfluenceClient
