import requests
import msal
import pandas as pd

class Source:
  def __init__(self,pagination):
    self.pagination = pagination


class Sink:
  def __init__(self,file_extension):
    self.file_extension = file_extension


class Authenticator:
  def __init__(self,client_id,client_secret):
    self.client_id = client_id
    self.client_secret = client_secret



class Authenticator_MicrosoftDataverse(Authenticator):
  def __init__(self,client_id,client_secret,tenant_id,resource_url):
    super().__init__(client_id,client_secret)
    self.tenant_id = tenant_id
    self.resource_url = resource_url
    self.token = self.__get_access_token()

  def __get_access_token(self):
    app = msal.ConfidentialClientApplication(self.client_id,authority=f"https://login.microsoftonline.com/{self.tenant_id}",client_credential=self.client_secret)
    result = app.acquire_token_for_client(scopes=[f"{self.resource_url}/.default"])
    return result["access_token"]
  
class Source_MicrosoftDataverse(Authenticator_MicrosoftDataverse,Source):
  def __init__(self,table_name,client_id,client_secret,tenant_id,resource_url,pagination=-1):
    Source.__init__(self,pagination=5000  if pagination  ==  -1 else  pagination)
    Authenticator_MicrosoftDataverse.__init__(self,client_id,client_secret,tenant_id,resource_url)

    self.table_name = table_name

    self.headers = {
      "Authorization":f"Bearer {self.token}",
      "OData-MaxVersion":"4.0",
      "OData-Version":"4.0",
      "Accept":"application/json",
      "Prefer": "odata.maxpagesize=" + str(self.pagination),
      "Content-Type": "application/json; charset=utf-8",
      "Cache-Control": "no-cache"
    }

    self.url = f"{self.resource_url}/api/data/v9.2/{self.table_name}"


  def __datanormalize__(self, df):
    for col in df.columns:
      if df[col].dtype == "object":
        df[col] = pd.to_datetime(df[col], errors="ignore")
    return df

  def data_sample(self,select=None, filter=None, top=100):
    if select is None:
      select = "*"
    if filter is None:
      filter = ""
    else:
      filter = f"?$filter={filter}"

    api_url =  self.url+f"{filter}?$select={select}&$top={top}" if  "?"  not in  filter  else self.url  +  f"{filter}&$select={select}&$top={top}"

    records = []

    response = requests.get(api_url,headers=self.headers)
    response.raise_for_status()
    data = response.json()
    records.extend(data.get("value", []))

    df = pd.DataFrame(records)

    return self.__datanormalize__(df)

  def data_extract(self,select=None, filter=None, top=5000):
    if select is None:
      select = "*"
    if filter is None:
      filter = ""
    else:
      filter = f"?$filter={filter}"

    api_url =  self.url+f"{filter}?$select={select}&$top={top}" if  "?"  not in  filter  else self.url  +  f"{filter}&$select={select}&$top={top}"

    records = []

    while api_url:
      response = requests.get(api_url,headers=self.headers)
      response.raise_for_status()
      data = response.json()
      records.extend(data.get("value", []))
      api_url = data.get("@odata.nextLink")

    df = pd.DataFrame(records)

    return self.__datanormalize__(df)
