import requests

class TomClient:
    """A thin class that supports sending requests via "requests" to the DESC tom.

    Usage: initialize one of these, giving it the url, your TOM
    username, and either your TOM password, or a file that has your TOM
    password in it:

      tc = TomClient( username='rknop', passwordfile='/home/raknop/secrets/tom_rknop_passwd' )

    (You can give it a url with url=; it defaults to https://desc-tom.lbl.gov.)

    Thereafter, just do something like

      res = tc.request( "POST", "elasticc2/ppdbdiaobject/55772173" )

    and res will come back with a string that you can load into JSON
    that will have all the fun data about PPDBDiaObject number 55772173.

    tc.request is just a thin front-end to python requests.request.  The
    only reason to use this client rather than the python requests
    module directly is that this class takes care of the stupid fiddly
    bits of getting some headers that django demands set up right in the
    request object when you log in.

    """

    def __init__( self, url="https://desc-tom.lbl.gov", username=None, password=None, passwordfile=None, connect=True ):
        self._url = url
        self._username = username
        self._password = password
        self._rqs = None
        if self._password is None:
            if passwordfile is None:
                raise RuntimeError( "Must give either password or passwordfile. " )
            with open( passwordfile ) as ifp:
                self._password = ifp.readline().strip()

        if connect:
            self.connect()

    def connect( self ):
        self._rqs = requests.session()
        res = self._rqs.get( f'{self._url}/accounts/login/' )
        if res.status_code != 200:
            raise RuntimeError( f"Got status {res.status_code} from first attempt to connect to {self._url}" )
        res = self._rqs.post( f'{self._url}/accounts/login/',
                              data={ 'username': self._username,
                                     'password': self._password,
                                     'csrfmiddlewaretoken': self._rqs.cookies['csrftoken'] } )
        if res.status_code != 200:
            raise RuntimeError( f"Failed to log in; http status: {res.status_code}" )
        if 'Please enter a correct' in res.text:
            # This is a very cheesy attempt at checking if the login failed.
            # I haven't found clean documentation on how to log into a django site
            # from an app like this using standard authentication stuff.  So, for
            # now, I'm counting on the HTML that happened to come back when
            # I ran it with a failed login one time.  One of these days I'll actually
            # figure out how Django auth works and make a version of /accounts/login/
            # designed for use in API scripts like this one, rather than desgined
            # for interactive users.
            raise RuntimeError( "Failed to log in.  I think.  Put in a debug break and look at res.text" )
        self._rqs.headers.update( { 'X-CSRFToken': self._rqs.cookies['csrftoken'] } )

    def request( self, method="GET", page=None, **kwargs ):
        """Send a request to the TOM

        method : a string with the HTTP method ("GET", "POST", etc.)

        page : the page to get; this is the URL but with the url you
          passed to the constructor removed.  So, if you wanted to get
          https://desc-tom.lbl.gov/elasticc, you'd pass just "elasticc"
          here.

        **kwargs : additional keyword arguments are passed on to
          requests.request

        """
        return self._rqs.request( method=method, url=f"{self._url}/{page}", **kwargs )
        
    def post( self, page=None, **kwargs ):
        """Shortand for TomClient.request( "POST", ... )"""
        return self.request( "POST", page, **kwargs )

    def get( self, page=None, **kwargs ):
        """Shortand for TomClient.request( "GET", ... )"""
        return self.request( "GET", page, **kwargs )

    def put( self, page=None, **kwargs ):
        """Shortand for TomClient.request( "PUT", ... )"""
        return self.request( "PUT", page, **kwargs )