from datetime import datetime, timedelta

from django.db import models, IntegrityError
from django.contrib.auth.models import User
from django.conf import settings
from django.utils import timezone
import requests

from . import api


class AccessToken(models.Model):
    account = models.OneToOneField("ApiAccount", on_delete=models.CASCADE, primary_key=True)
    value = models.CharField(max_length=256, verbose_name="Withings API Access Token")
    expires = models.DateTimeField(verbose_name="Time of expiration")


class RefreshToken(models.Model):
    account = models.OneToOneField("ApiAccount", on_delete=models.CASCADE, primary_key=True)
    value = models.CharField(max_length=256, verbose_name="Withings API Refresh Token")
    expires = models.DateTimeField(verbose_name="Time of expiration")


class ApiAccount(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True)
    userid = models.PositiveIntegerField(verbose_name="Withings API User ID")
    last_update = models.DateTimeField(null=True, default=None, verbose_name="Time of last synchronization with Withings API")

    def refresh_tokens(self):
        data = {
            'action': 'requesttoken',
            'client_id': settings.WITHINGS_CONFIG['CLIENT_ID'],
            'client_secret': settings.WITHINGS_CONFIG['CLIENT_SECRET'],
            'grant_type': 'refresh_token',
            'refresh_token': self.refreshtoken.value
        }
        response = requests.post(
            url=settings.WITHINGS_CONFIG['ENDPOINT_URL_OAUTH2'],
            json=data
        )

        if response is not None:
            response.raise_for_status()
        response_data = response.json()

        now = timezone.now()
        self.accesstoken.value = response_data['body']['access_token']
        self.accesstoken.expires = now + timedelta(seconds=response_data['body']['expires_in'])
        self.refreshtoken.value = response_data['body']['refresh_token']
        self.refreshtoken.expires = now + timedelta(days=365)
        self.accesstoken.save()
        self.refreshtoken.save()


    def get_measurements(self, since: datetime | None = None) -> list:
        if self.accesstoken.expires < timezone.now():
            self.refresh_tokens()

        params={
            'action': 'getmeas',
            'meastypes': '9,10,11,54,71'
        }
        if since:
            params['lastupdate'] = str(int(since.timestamp()))

        response = requests.get(
            url="https://wbsapi.withings.net/measure",
            params=params,
            headers={
                'Authorization': f"Bearer {self.accesstoken.value}"
            }
        )
        if response is not None:
            response.raise_for_status()
        data = response.json()
        if data['status'] != 0:
            raise RuntimeError(f"Received status {data['status']} while retrieving measurements: {data['error']}")

        return api.parse_getmeas_response(data, self.user)

    
    def update_records(self):
        records = self.get_measurements(self.last_update)
        for record in records:
            try:
                record.save()
            except IntegrityError:
                pass
        self.last_update = timezone.now()
        self.save()
