"""
biblioteca destinada à integração com o SharePoint, utilizando a biblioteca automaweb.
"""

import automaweb

import json
import os
from office365.sharepoint.client_context import ClientContext
from office365.runtime.client_request_exception import ClientRequestException

from office365.graph_client import GraphClient
from office365.outlook.mail.item_body import ItemBody

from tkinter import messagebox
import tkinter as tk

import sys

class SharePoint():
    
    """
    Uma classe para gerenciar conexões e operações de arquivos
    com um site do SharePoint usando autenticação baseada em cookies.
    """
    
    def __init__(self, site_url : str, caminho_cookie: str = os.path.join(os.path.expanduser("~"), "Downloads", "cookies_SharePoint.json")):
        """
        inicializa o gerenciador, autentica e estabelece a conexão.
        """
        self.site_url = site_url
        self.caminho_cookie = caminho_cookie
        
        try:
            self.ctx = ClientContext(self.site_url).with_cookies(self._autenticar)
            self.web = self.ctx.web.get().execute_query()
            print(f"Conectado com sucesso ao site: {self.web.properties['Title']}")
        
        except:
            try:
                self._coletar_cookies()
                self.ctx = ClientContext(self.site_url).with_cookies(self._autenticar)
                self.web = self.ctx.web.get().execute_query()
                print(f"Conectado com sucesso ao site: {self.web.properties['Title']}")

            except Exception as e:
                messagebox.showerror("Erro", f"Não foi possível conectar ao site!\n{e}")
        
    def _coletar_cookies(self):
        """
        Coleta os cookies do site e salva em um arquivo JSON.
        """
        try:
            nav = automaweb.Navegador()
            nav.abrir_driver()
            nav.abrir_url(self.site_url)
            nav.salvar_cookies(nome_arquivo=self.caminho_cookie)

        except Exception as e:
            print(f"Erro ao coletar os cookies!\n{e}")

    def _autenticar(self):
        '''
        Abre o json carregado com os cookies e seleciona os cookies necessarios

        Returns:
            dict: cookies
        '''
        try:
            with open(self.caminho_cookie, "r", encoding="utf-8") as f:
                data = json.load(f)
            cookies = {}
            for c in data:
                name = c.get("name")
                if name in {"FedAuth", "rtFa", "SPOIDCRL"}:
                    cookies[name] = c.get("value", "")
            return cookies
        
        except Exception as e:
            print(f"Erro ao carregar os cookies!\n{e}")
            raise

    def download_arquivo(self, caminho_sharepoint, pasta_local: str = os.path.join(os.path.expanduser("~"), "Downloads")):
        '''
        Faz o download do arquivo no caminho_sharepoint e salva na pasta_local.

        Args:
            caminho_sharepoint (str): caminho do arquivo no SharePoint. Ex.: "/sites/Nome/Shared Documents/Pasta/arquivo.txt"
            pasta_local (str): caminho para salvar o arquivo. Padrão: Downloads
        '''

        try:
            #encontra o arquivo e faz o download
            nome_arquivo = caminho_sharepoint.split('/')[-1] #separa o nome do arquivo
            caminho_local = os.path.join(pasta_local, nome_arquivo) #cria caminho completo
            #cria a pasta local correspondente caso não exista
            if not os.path.exists(pasta_local):
                os.makedirs(pasta_local)
            with open(caminho_local, "wb") as caminho_local:
                arquivo = self.ctx.web.get_file_by_server_relative_url(caminho_sharepoint)
                arquivo.download(caminho_local)
                self.ctx.execute_query()
            
            print(f"Download de '{nome_arquivo}' realizado com sucesso!")
    
        except Exception as e:
            print(f"Erro ao fazer o download do arquivo!\n{e}") 

    def upload_arquivo(self, caminho_local, pasta_sharepoint):
        '''
        Faz o upload do arquivo no caminho_local para a pasta_sharepoint.

        Args:
            pasta_sharepoint (str): caminho da pasta no SharePoint
            caminho_local (str): caminho do arquivo local
        '''
        try:
            #encontra a pasta e faz o upload
            nome_arquivo = os.path.basename(caminho_local)
            pasta_alvo = self.ctx.web.get_folder_by_server_relative_url(pasta_sharepoint)
            with open(caminho_local, "rb") as f:
                pasta_alvo.files.upload(f, nome_arquivo).execute_query()
            print(f"Upload de '{nome_arquivo}' realizado com sucesso em '{pasta_sharepoint}'!")
    
        except Exception as e:
            print(f"Erro ao fazer o upload do arquivo!\n{e}")

    def download_pasta(self, pasta_sharepoint, pasta_local: str = os.path.join(os.path.expanduser("~"), "Downloads")):
        """
        Baixa recursivamente todo o conteúdo de uma pasta do SharePoint.

        Args:
            pasta_sharepoint (str): Caminho da pasta no SharePoint.
            pasta_local_base (str): Caminho da pasta local de destino. Padrão: Downloads
        """
        try:
            #obtém a pasta e carrega arquivos e subpastas
            pasta = self.ctx.web.get_folder_by_server_relative_url(pasta_sharepoint)
            self.ctx.load(pasta.files)
            arquivos = pasta.files.get().execute_query()
            subpastas = pasta.folders.get().execute_query()

            #cria a pasta local correspondente caso não exista
            if not os.path.exists(pasta_local):
                os.makedirs(pasta_local)

            #download dos arquivos da pasta atual
            for arquivo in arquivos:
                self.download_arquivo(arquivo.serverRelativeUrl, pasta_local)

            #recursividade para subpastas
            for sub_folder in subpastas:
                #ignora pastas de sistema
                if sub_folder.name not in ["Forms"]: 
                    nova_pasta_local = os.path.join(pasta_local, sub_folder.name)
                    self.download_pasta(sub_folder.serverRelativeUrl, nova_pasta_local)

        except Exception as e:
            print(f"Erro no download da pasta: {e}")

    def upload_pasta(self, pasta_local, pasta_sharepoint):
        """
        Faz o upload de uma pasta local inteira para o SharePoint.

        Args:
            pasta_local (str): Caminho da pasta local de origem.
            pasta_sharepoint_destino (str): Caminho da pasta no SharePoint de destino.
        """
        try:
            
            #encontra todos os arquivos e faz upload
            arquivos = automaweb.listar_arquivos(pasta_local)
            for arquivo in arquivos:
                #cria pasta se necessário
                if not self.existe_pasta(pasta_sharepoint):
                    self.criar_pasta(pasta_sharepoint)
                self.upload_arquivo(arquivo, pasta_sharepoint)
            
            #encontra todas as pastas e faz upload recursivamente
            sub_pastas = automaweb.listar_pastas(pasta_local)
            for sub_pasta in sub_pastas:
                basename_pasta = os.path.basename(sub_pasta)
                sub_pasta_sharepoint = pasta_sharepoint + "/" + basename_pasta #cria caminho completo pro SharePoint
                self.upload_pasta(os.path.join(pasta_local, sub_pasta), sub_pasta_sharepoint)

        except Exception as e:
            print(f"Erro no upload da pasta: {e}")

    def criar_pasta(self, caminho_pasta_sharepoint):
        '''
        Cria uma pasta no caminho_sharepoint.

        Args:
            caminho_pasta_sharepoint (str): caminho da pasta no SharePoint
        '''
        #garante que a pasta existe (se não existir, cria)
        folder = (
            self.ctx.web.ensure_folder_path(caminho_pasta_sharepoint)
            .get()
            .select(["ServerRelativePath"])
            .execute_query()
        )
        print(f"Pasta {folder} criada com sucesso!")
    
    def existe_arquivo(self, caminho_arquivo_sharepoint):
        '''
        Verifica se o arquivo existe no caminho_sharepoint.

        Args:
            caminho_arquivo_sharepoint (str): caminho do arquivo no SharePoint
        '''
        try:
            #encontra o arquivo, salva e retorna True
            self.web.get_file_by_server_relative_url(caminho_arquivo_sharepoint).get().execute_query()
            return True
        
        except ClientRequestException as e:
            if e.response.status_code == 404:
                return False
            else:
                raise ValueError(e.response.text)
    
    def existe_pasta(self, caminho_pasta_sharepoint):
        '''
        Verifica se uma pasta existe no SharePoint.

        Args:
            caminho_pasta_sharepoint (str): Caminho relativo da pasta no SharePoint.
            
        Returns:
            bool: True se a pasta existir, False se não existir.
        '''
        try:
            #encontra a pasta, salva e retorna True
            self.ctx.web.get_folder_by_server_relative_url(caminho_pasta_sharepoint).get().execute_query()
            return True
        
        #caso a pasta não seja encontrada, retorna False
        except ClientRequestException as e:
            if e.response.status_code == 404:
                return False
            else:
                raise ValueError(e.response.text)
    
    def excluir_arquivo(self, caminho_arquivo_sharepoint):
        '''
        Exclui um arquivo no caminho_sharepoint.

        Args:
            caminho_arquivo_sharepoint (str): caminho do arquivo no SharePoint
        '''
        try:
            #encontra o arquivo, deleta e salva
            self.ctx.web.get_file_by_server_relative_url(caminho_arquivo_sharepoint).delete_object().execute_query()
            print(f"Arquivo '{caminho_arquivo_sharepoint}' excluido com sucesso!")
        except Exception as e:
            print(f"Erro ao excluir o arquivo '{caminho_arquivo_sharepoint}':\n{e}")
    
    def excluir_pasta(self, caminho_pasta_sharepoint):
        '''
        Exclui uma pasta no caminho_sharepoint.

        Args:
            caminho_pasta_sharepoint (str): caminho da pasta no SharePoint
        '''
        try:
            #encontra a pasta, deleta e salva
            self.ctx.web.get_folder_by_server_relative_url(caminho_pasta_sharepoint).delete_object().execute_query()
            print(f"Pasta '{caminho_pasta_sharepoint}' excluida com sucesso!")
        except Exception as e:
            print(f"Erro ao excluir a pasta '{caminho_pasta_sharepoint}':\n{e}")

class Outlook():
    pass #https://github.com/vgrem/office365-rest-python-client?tab=readme-ov-file#Working-with-Outlook-API

class Teams():

    def __init__(self):
        pass

    def enviar_mensagem():

        test_tenant = "xxx"
        test_client_id = "xxx"
        test_username = "xxx"
        test_password = "xxx"

        cliente = GraphClient(tenant=test_tenant).with_username_and_password(
            test_client_id, test_username, test_password
        ) #pesquisar sobre autenticação ao GraphClient
        my_teams = cliente.me.joined_teams.get().top(1).execute_query()
        if len(my_teams) == 0:
            sys.exit("No teams found")

        first_team = my_teams[0]
        message = first_team.primary_channel.messages.add(
            itemBody=ItemBody("Hello world!")
        ).execute_query()
        print(message.web_url)