"""
Nextcloud integration service
Handles OAuth authentication and file operations with Nextcloud server
"""

import requests
import json
import base64
from typing import List, Dict, Any, Optional
from datetime import datetime, timedelta
from urllib.parse import urljoin
import hashlib

from config.settings import settings

class NextcloudService:
    """Nextcloud integration service"""
    
    def __init__(self):
        self.base_url = settings.nextcloud_base_url.rstrip('/')
        self.oauth_configured = bool(settings.nextcloud_oauth_client_id and settings.nextcloud_oauth_client_secret)
        self.access_token: Optional[str] = None
        self.refresh_token: Optional[str] = None
        self.token_expires: Optional[datetime] = None
    
    def get_oauth_url(self) -> str:
        """Generate OAuth authorization URL for Nextcloud"""
        if not self.oauth_configured:
            raise ValueError("Nextcloud OAuth not configured")
        
        params = {
            'client_id': settings.nextcloud_oauth_client_id,
            'redirect_uri': settings.nextcloud_redirect_uri,
            'response_type': 'code',
            'scope': 'files',
            'state': 'kpi_analysis_state'
        }
        
        auth_url = f"{self.base_url}/index.php/apps/oauth2/authorize"
        query_string = "&".join([f"{k}={v}" for k, v in params.items()])
        return f"{auth_url}?{query_string}"
    
    async def exchange_code_for_token(self, authorization_code: str) -> Dict[str, Any]:
        """Exchange authorization code for access token"""
        if not self.oauth_configured:
            raise ValueError("Nextcloud OAuth not configured")
        
        token_url = f"{self.base_url}/index.php/apps/oauth2/api/v1/token"
        
        data = {
            'grant_type': 'authorization_code',
            'client_id': settings.nextcloud_oauth_client_id,
            'client_secret': settings.nextcloud_oauth_client_secret,
            'redirect_uri': settings.nextcloud_redirect_uri,
            'code': authorization_code
        }
        
        response = requests.post(token_url, data=data)
        response.raise_for_status()
        
        token_data = response.json()
        
        # Store tokens
        self.access_token = token_data['access_token']
        self.refresh_token = token_data['refresh_token']
        self.token_expires = datetime.now() + timedelta(seconds=token_data['expires_in'])
        
        return token_data
    
    async def refresh_access_token(self) -> Dict[str, Any]:
        """Refresh access token using refresh token"""
        if not self.refresh_token:
            raise ValueError("No refresh token available")
        
        token_url = f"{self.base_url}/index.php/apps/oauth2/api/v1/token"
        
        data = {
            'grant_type': 'refresh_token',
            'client_id': settings.nextcloud_oauth_client_id,
            'client_secret': settings.nextcloud_oauth_client_secret,
            'refresh_token': self.refresh_token
        }
        
        response = requests.post(token_url, data=data)
        response.raise_for_status()
        
        token_data = response.json()
        
        # Update tokens
        self.access_token = token_data['access_token']
        self.refresh_token = token_data['refresh_token']
        self.token_expires = datetime.now() + timedelta(seconds=token_data['expires_in'])
        
        return token_data
    
    async def ensure_valid_token(self):
        """Ensure we have a valid access token"""
        if not self.access_token or not self.token_expires:
            raise ValueError("No access token available")
        
        if datetime.now() >= self.token_expires - timedelta(minutes=5):
            await self.refresh_access_token()
    
    def _get_headers(self) -> Dict[str, str]:
        """Get request headers with authentication"""
        if not self.access_token:
            raise ValueError("Not authenticated with Nextcloud")
        
        return {
            'Authorization': f'Bearer {self.access_token}',
            'Content-Type': 'application/json'
        }
    
    async def list_files(self, folder_path: str = None) -> List[Dict[str, Any]]:
        """List files in Nextcloud"""
        await self.ensure_valid_token()
        
        if not folder_path:
            folder_path = settings.nextcloud_kpi_folder
        
        # Use WebDAV API to list files
        dav_url = f"{self.base_url}/remote.php/dav/files/{settings.nextcloud_username}/"
        folder_url = urljoin(dav_url, folder_path.lstrip('/'))
        
        headers = {
            'Authorization': f'Bearer {self.access_token}',
            'Depth': '1'
        }
        
        response = requests.request('PROPFIND', folder_url, headers=headers)
        
        if response.status_code == 404:
            return []
        
        response.raise_for_status()
        
        # Parse WebDAV response (simplified)
        # In production, you might want to use a proper WebDAV client
        files = []
        content = response.text
        
        # Simple XML parsing (for demonstration)
        import xml.etree.ElementTree as ET
        try:
            root = ET.fromstring(content)
            for response_elem in root.findall('.//{DAV:}response'):
                href = response_elem.find('{DAV:}href')
                if href is not None and href.text:
                    filename = href.text.split('/')[-1]
                    if filename and not filename.startswith('.'):
                        # Get file properties
                        propstat = response_elem.find('.//{DAV:}propstat')
                        if propstat is not None:
                            props = propstat.find('.//{DAV:}prop')
                            if props is not None:
                                getcontentlength = props.find('{DAV:}getcontentlength')
                                getlastmodified = props.find('{DAV:}getlastmodified')
                                
                                files.append({
                                    'id': hashlib.md5(href.text.encode()).hexdigest(),
                                    'name': filename,
                                    'size': int(getcontentlength.text) if getcontentlength is not None else 0,
                                    'modified': getlastmodified.text if getlastmodified is not None else '',
                                    'path': href.text,
                                    'download_url': f"{self.base_url}/remote.php/dav{href.text}"
                                })
        except ET.ParseError:
            # Fallback - return empty list if XML parsing fails
            pass
        
        return files
    
    async def download_file(self, file_id: str) -> bytes:
        """Download file from Nextcloud by file ID"""
        await self.ensure_valid_token()
        
        # First, get file info to find the path
        files = await self.list_files()
        target_file = None
        
        for file_info in files:
            if file_info['id'] == file_id:
                target_file = file_info
                break
        
        if not target_file:
            raise FileNotFoundError(f"File with ID {file_id} not found")
        
        # Download the file
        download_url = target_file['download_url']
        headers = {'Authorization': f'Bearer {self.access_token}'}
        
        response = requests.get(download_url, headers=headers)
        response.raise_for_status()
        
        return response.content
    
    async def upload_file(self, file_path: str, upload_path: str) -> Dict[str, Any]:
        """Upload file to Nextcloud"""
        await self.ensure_valid_token()
        
        dav_url = f"{self.base_url}/remote.php/dav/files/{settings.nextcloud_username}/"
        full_upload_path = urljoin(dav_url, upload_path.lstrip('/'))
        
        # Read file content
        with open(file_path, 'rb') as f:
            content = f.read()
        
        headers = {
            'Authorization': f'Bearer {self.access_token}',
            'Content-Type': 'application/octet-stream'
        }
        
        response = requests.put(full_upload_path, data=content, headers=headers)
        response.raise_for_status()
        
        return {
            'success': True,
            'path': upload_path,
            'size': len(content)
        }
    
    async def delete_file(self, file_path: str) -> bool:
        """Delete file from Nextcloud"""
        await self.ensure_valid_token()
        
        dav_url = f"{self.base_url}/remote.php/dav/files/{settings.nextcloud_username}/"
        full_file_path = urljoin(dav_url, file_path.lstrip('/'))
        
        headers = {'Authorization': f'Bearer {self.access_token}'}
        
        response = requests.delete(full_file_path, headers=headers)
        response.raise_for_status()
        
        return True
    
    async def create_folder(self, folder_path: str) -> bool:
        """Create folder in Nextcloud"""
        await self.ensure_valid_token()
        
        dav_url = f"{self.base_url}/remote.php/dav/files/{settings.nextcloud_username}/"
        full_folder_path = urljoin(dav_url, folder_path.lstrip('/'))
        
        headers = {
            'Authorization': f'Bearer {self.access_token}',
            'Content-Type': 'application/xml'
        }
        
        # MKCOL request to create folder
        xml_body = f'<d:mkcol xmlns:d="DAV:"><d:set><d:prop><d:resourcetype><d:collection/></d:resourcetype></d:prop></d:set></d:mkcol>'
        
        response = requests.request('MKCOL', full_folder_path, data=xml_body, headers=headers)
        
        # 201 Created or 405 Method Not Allowed (if folder exists)
        return response.status_code in [201, 405]

# Global Nextcloud service instance
nextcloud_service = NextcloudService()