import json
from typing import Iterable, Optional, Dict
import datetime
from sqlalchemy.orm import Mapped, mapped_column
import logging
from flask import current_app
import random, string

logger = logging.getLogger(__name__)

db = current_app.db


class User(db.Model):
    uuid: Mapped[str] = mapped_column(primary_key=True)
    username: Mapped[str] = mapped_column(unique=True)
    display_name: Mapped[str] = mapped_column(server_default="")
    first_name: Mapped[str] = mapped_column(server_default="")
    last_name: Mapped[str] = mapped_column(server_default="")
    email: Mapped[str] = mapped_column(server_default="")
    phone: Mapped[str] = mapped_column(server_default="")
    street: Mapped[str] = mapped_column(server_default="")
    number: Mapped[str] = mapped_column(server_default="")
    postal: Mapped[str] = mapped_column(server_default="")
    city: Mapped[str] = mapped_column(server_default="")
    country: Mapped[str] = mapped_column(server_default="")
    lat: Mapped[str] = mapped_column(server_default="")
    lon: Mapped[str] = mapped_column(server_default="")
    birthdate: Mapped[Optional[datetime.date]]
    last_updated: Mapped[datetime.datetime]
    include_in_views: Mapped[Optional[bool]]
    carddav_key: Mapped[str]

    def reset_carddav_password(self):
        self.carddav_key = "".join(
            random.choices(string.ascii_lowercase + string.digits, k=20)
        )
        current_app.radicale.create_or_update_authfile()

    @staticmethod
    def create_or_update(oidc_user: Dict) -> "User":
        uuid = oidc_user["sub"]
        user = db.session.execute(
            db.select(User).filter_by(uuid=uuid)
        ).scalar_one_or_none()
        if not user:
            logger.debug(f"User with UUID {uuid} not yet found; creating..")
            user = User(uuid=uuid, last_updated=datetime.datetime.now())
            user.reset_carddav_password()  # init carddav password
            db.session.add(user)
        user.display_name = oidc_user["name"]
        if user.username != oidc_user["preferred_username"]:
            # possibly changed username
            user.username = oidc_user["preferred_username"]
            current_app.radicale.ensure_user_exists(user)
            current_app.radicale.create_or_update_authfile()
        return user

    def __repr__(self):
        return self.username

    def to_dict(self):
        return {k: self.__getattribute__(k) for k in User.__dict__.keys()}

    @staticmethod
    def all_users() -> Iterable["User"]:
        return db.session.execute(db.select(User)).scalars()

    @staticmethod
    def all_viewable_users() -> Iterable["User"]:
        return db.session.execute(
            db.select(User).filter_by(include_in_views=True)
        ).scalars()
