import requests
import os

class SharePointClient:
    def __init__(self, access_token=None):
        self.tenant_id = os.environ.get('SHAREPOINT_TENANT_ID')
        self.client_id = os.environ.get('SHAREPOINT_CLIENT_ID')
        self.client_secret = os.environ.get('SHAREPOINT_CLIENT_SECRET')
        self.site_id = os.environ.get('SHAREPOINT_SITE_ID')
        if access_token:
            self.access_token = access_token
        else:
            self.access_token = self.authenticate()
        self.base_url = f'https://graph.microsoft.com/v1.0/sites/{self.site_id}'
        self._drive_id_cache = {}

    def authenticate(self):
        url = f'https://login.microsoftonline.com/{self.tenant_id}/oauth2/v2.0/token'
        data = {
            'grant_type': 'client_credentials',
            'client_id': self.client_id,
            'client_secret': self.client_secret,
            'scope': 'https://graph.microsoft.com/.default'
        }
        response = requests.post(url, data=data)
        try:
            response.raise_for_status()
        except Exception as e:
            from src.discord.discord_webhook import send_discord_audit_log
            send_discord_audit_log(
                action="Falha de integração externa",
                user_id=None,
                object_type="SharePoint",
                details=f"Erro ao autenticar no SharePoint: {str(e)}",
                ip_address=None
            )
            raise
        return response.json().get('access_token')

    def get_drive_id_by_name(self, drive_name='Arquivos'):
        if drive_name.lower() == 'arquivos':
            return 'b!PvwXRPMJ2Uq5bxWxWWiZaS97wielQ81DqAQaRqwAs0ORcYqmkhe7Q6bSFIrkDPZY'
        possible_names = [drive_name]
        if drive_name.lower() == 'documentos':
            possible_names.append('Documentos Compartilhados')
        elif drive_name.lower() == 'arquivos':
            possible_names.append('Arquivos')
        if drive_name in self._drive_id_cache:
            return self._drive_id_cache[drive_name]
        drives = self.get_drives()
        for drive in drives:
            if drive['name'] in possible_names:
                self._drive_id_cache[drive_name] = drive['id']
                return drive['id']
        raise Exception(f"Drive '{drive_name}' não encontrado no site do SharePoint.")

    def list_files(self, path=None, drive_name='Arquivos'):
        """Lista arquivos/pastas a partir da biblioteca 'Arquivos' (ou outra, se especificado)."""
        drive_id = self.get_drive_id_by_name(drive_name)
        headers = {'Authorization': f'Bearer {self.access_token}'}
        if path:
            url = f'https://graph.microsoft.com/v1.0/drives/{drive_id}/root:/{path}:/children'
        else:
            url = f'https://graph.microsoft.com/v1.0/drives/{drive_id}/root/children'
        resp = requests.get(url, headers=headers)
        resp.raise_for_status()
        return resp.json()

    def list_sites(self, search=None):
        headers = {'Authorization': f'Bearer {self.access_token}'}
        url = 'https://graph.microsoft.com/v1.0/sites'
        if search:
            url += f'?search={search}'
        response = requests.get(url, headers=headers)
        response.raise_for_status()
        return response.json()

    def find_folder_recursive(self, folder_name, current_path=None, depth=0, max_depth=15, drive_name='Arquivos', _visited=None, _max_visited=500):
        if _visited is None:
            _visited = set()
        if depth > max_depth or len(_visited) > _max_visited:
            return None
        try:
            items = self.list_files(current_path, drive_name=drive_name).get('value', [])
        except Exception:
            return None
        for item in items:
            if item.get('folder'):
                folder_id = (current_path, item['name'])
                if folder_id in _visited:
                    continue
                _visited.add(folder_id)
                caminho_atual = f"{current_path}/{item['name']}" if current_path else item['name']
                print(f'[DEBUG][FOLDER-RECURSIVE] Visitando pasta: {caminho_atual}')
                if item['name'].lower() == folder_name.lower():
                    return caminho_atual
        for item in items:
            if item.get('folder'):
                folder_id = (current_path, item['name'])
                if folder_id in _visited:
                    continue
                sub_path = f"{current_path}/{item['name']}" if current_path else item['name']
                found = self.find_folder_recursive(folder_name, sub_path, depth+1, max_depth, drive_name=drive_name, _visited=_visited, _max_visited=_max_visited)
                if found:
                    return found
        return None

    def find_folder_fuzzy(self, folder_name, current_path=None, depth=0, max_depth=15, drive_name='Arquivos', min_ratio=0.7, _visited=None, _max_visited=500):
        import difflib
        if _visited is None:
            _visited = set()
        if depth > max_depth or len(_visited) > _max_visited:
            return None, []
        try:
            items = self.list_files(current_path, drive_name=drive_name).get('value', [])
        except Exception:
            return None, []
        folder_names = [item['name'] for item in items if item.get('folder')]
        matches = difflib.get_close_matches(folder_name, folder_names, n=3, cutoff=min_ratio)
        for item in items:
            if item.get('folder'):
                folder_id = (current_path, item['name'])
                if folder_id in _visited:
                    continue
                _visited.add(folder_id)
                caminho_atual = f"{current_path}/{item['name']}" if current_path else item['name']
                print(f'[DEBUG][FOLDER-FUZZY] Visitando pasta: {caminho_atual}')
                if matches and item['name'] == matches[0]:
                    return caminho_atual, matches
        for item in items:
            if item.get('folder'):
                folder_id = (current_path, item['name'])
                if folder_id in _visited:
                    continue
                sub_path = f"{current_path}/{item['name']}" if current_path else item['name']
                found, suggestions = self.find_folder_fuzzy(folder_name, sub_path, depth+1, max_depth, drive_name=drive_name, min_ratio=min_ratio, _visited=_visited, _max_visited=_max_visited)
                if found:
                    return found, suggestions
        return None, folder_names

    def get_drives(self):
        headers = {'Authorization': f'Bearer {self.access_token}'}
        url = f'{self.base_url}/drives'
        resp = requests.get(url, headers=headers)
        resp.raise_for_status()
        drives = resp.json().get('value', [])
        print('[DEBUG] Drives disponíveis no site do SharePoint:')
        for drive in drives:
            print(f"  - id: {drive.get('id')}, name: {drive.get('name')}, displayName: {drive.get('displayName')}")
        return drives

    def list_tree(self, path=None, drive_name='Arquivos', level=0, max_depth=15):
        """
        Lista recursivamente todos os arquivos e pastas a partir de um caminho, formatando como árvore com indentação.
        """
        drive_id = self.get_drive_id_by_name(drive_name)
        headers = {'Authorization': f'Bearer {self.access_token}'}
        if path:
            url = f'https://graph.microsoft.com/v1.0/drives/{drive_id}/root:/{path}:/children'
        else:
            url = f'https://graph.microsoft.com/v1.0/drives/{drive_id}/root/children'
        resp = requests.get(url, headers=headers)
        resp.raise_for_status()
        items = resp.json().get('value', [])
        lines = []
        indent = '  ' * level
        for item in items:
            if item.get('folder'):
                lines.append(f"{indent}📁 {item['name']}")
                if level < max_depth:
                    sub_path = f"{path}/{item['name']}" if path else item['name']
                    lines.extend(self.list_tree(sub_path, drive_name=drive_name, level=level+1, max_depth=max_depth))
            else:
                lines.append(f"{indent}📄 {item['name']}")
        return lines

    def get_file_content(self, file_path, drive_name='Arquivos'):
        """
        Baixa o conteúdo de um arquivo do SharePoint pelo caminho relativo (ex: 'Sao Paulo/RH/arquivo.txt').
        Suporta arquivos de texto, PDF e DOCX (retorna texto extraído).
        """
        import io
        drive_id = self.get_drive_id_by_name(drive_name)
        headers = {'Authorization': f'Bearer {self.access_token}'}
        url = f'https://graph.microsoft.com/v1.0/drives/{drive_id}/root:/{file_path}:/content'
        resp = requests.get(url, headers=headers)
        resp.raise_for_status()
        content_type = resp.headers.get('Content-Type', '')
        if 'text' in content_type or file_path.lower().endswith('.txt'):
            return resp.text
        elif file_path.lower().endswith('.pdf'):
            from PyPDF2 import PdfReader
            pdf_bytes = io.BytesIO(resp.content)
            reader = PdfReader(pdf_bytes)
            text = ''
            for page in reader.pages:
                text += page.extract_text() or ''
            return text
        elif file_path.lower().endswith('.docx'):
            try:
                import docx
            except ImportError:
                return '[docx2txt não instalado]'
            from docx import Document
            doc_bytes = io.BytesIO(resp.content)
            doc = Document(doc_bytes)
            return '\n'.join([p.text for p in doc.paragraphs])
        else:
            return '[Tipo de arquivo não suportado para leitura de conteúdo]'