from email.utils import formataddr
from plone import api as plone_api
from plone.app.dexterity.textindexer.directives import searchable
from plone.autoform import directives
from plone.autoform.interfaces import IFormFieldProvider
from plone.base import PloneMessageFactory as _pmf
from plone.dexterity.content import Container
from plone.namedfile.field import NamedBlobImage
from plone.supermodel import model
from ploneintranet import schema as pischema
from ploneintranet.core import _
from ploneintranet.layout.widgets.checkbox import PIMinimalCheckBoxFieldWidget
from z3c.form import validator
from zope import schema
from zope.deferredimport import deprecated
from zope.interface import implementer
from zope.interface import Invalid
from zope.interface import provider


def invalid_when_triggered(value):
    if not value:
        return True
    if value in {"error", "debug"} and plone_api.env.debug_mode():
        raise Invalid(f"Forced server-side error: {value!r}")
    return True


deprecated(
    "Email is now in ploneintranet.schema",
    Email="ploneintranet.schema.fields:PIEmail",
)

deprecated(
    "Phone is now in ploneintranet.schema",
    Phone="ploneintranet.schema.fields:Telephone",
)

deprecated(
    "GooglePlace is now in ploneintranet.schema",
    GooglePlace="ploneintranet.schema.fields:GooglePlace",
)


class SingleCheckbox(schema.Bool):
    """A checkbox field"""


def hide_email_default():
    return plone_api.portal.get_registry_record(
        "ploneintranet.userprofile.hide_email_by_default", default=False
    )


class IUserProfile(model.Schema):
    """The core user profile schema.

    Most of the plone intranet UI relies on these fields.
    """

    # username == context.getId() == userid
    # username should never be changed because it's used as an alias for userid
    # if you need different login names, switch on use_email_as_username
    # see changeset and docs in quaive/ploneintranet#1043
    searchable("username")
    username = schema.TextLine(title=_("Username"), required=True, default="")

    searchable("person_title")
    person_title = schema.TextLine(title=_("Person title"), required=False, default="")

    searchable("first_name")
    first_name = schema.TextLine(title=_("First name"), required=True, default="")

    searchable("last_name")
    last_name = schema.TextLine(title=_("Last name"), required=True, default="")

    searchable("email")
    email = pischema.PIEmail(title=_("Email"), required=True, default="")

    directives.widget("hide_email", PIMinimalCheckBoxFieldWidget)
    hide_email = pischema.PIBool(
        title="",
        label_content=_("Hide my email address from other users"),
        required=False,
        defaultFactory=hide_email_default,
    )

    portrait = NamedBlobImage(title=_("Photo"), required=False, default=None)

    directives.omitted("recent_contacts")
    recent_contacts = schema.List(
        title=_("Last Contacts"), required=False, defaultFactory=list
    )

    directives.write_permission(source="cmf.ManagePortal")
    directives.read_permission(source="cmf.ManagePortal")
    source = schema.TextLine(
        title=_("Source (plone, LDAP, XLS, ...)"),
        required=False,
        default="plone",
    )

    language = schema.TextLine(title=_pmf("Language"), required=False, default="")

    preferred_content_language = schema.TextLine(
        title=_(
            "label_preferred_content_language", default="Preferred content language"
        ),
        required=False,
        default="",
    )


@provider(IFormFieldProvider)
class IUserProfileAdditional(model.Schema):
    """Default additional fields for UserProfile."""

    searchable("job_title")
    job_title = schema.TextLine(title=_("Job title"), required=False, default="")

    searchable("department")
    department = schema.TextLine(title=_("Department"), required=False, default="")

    searchable("organization")
    organization = schema.TextLine(title=_("Organisation"), required=False, default="")

    searchable("industry")
    industry = schema.TextLine(
        title=_("Industry"),
        required=False,
        default="",
        constraint=invalid_when_triggered,
    )

    searchable("url")
    url = schema.URI(title=_("Website"), required=False, default=None)

    searchable("telephone")
    telephone = pischema.Telephone(
        title=_("Telephone Number"), required=False, default=""
    )

    searchable("mobile")
    mobile = pischema.Telephone(title=_("Mobile Number"), required=False, default="")

    searchable("address")
    address = schema.Text(title=_("Address"), required=False, default="")

    time_zone = schema.Choice(
        title=_("Time Zone"),
        source="plone.app.vocabularies.CommonTimezones",
        required=False,
    )

    primary_location = pischema.GooglePlace(
        title=_("Primary location"),
        source="ploneintranet.userprofile.locations_vocabulary",
        required=False,
    )

    searchable("biography")
    biography = schema.Text(title=_("Biography"), required=False, default="")


@implementer(IUserProfile)
class UserProfile(Container):
    """UserProfile content type."""

    def Title(self):
        return self.fullname

    def Description(self):
        return getattr(self, "job_title", None)

    @property
    def fullname(self):
        names = [
            getattr(self, "person_title", ""),
            getattr(self, "first_name", ""),
            getattr(self, "last_name", ""),
        ]
        return " ".join([name for name in names if name])

    @property
    def initials(self):
        first_name = self.first_name or ""
        last_name = self.last_name or ""
        initials = first_name[:1].upper() + last_name[:2].capitalize()
        return initials or self.getId()[:3].capitalize()

    @property
    def display_name_email(self):
        if not self.email:
            return ""
        name = self.fullname or self.email
        return formataddr((name, self.email))


class UsernameValidator(validator.SimpleFieldValidator):
    """Two users can't have the same username.

    Because of #1043 it's possible that .username != .getUserName()
    so instead we rely on .username == .getUserId() which holds true
    since we don't do use_uuid_as_userid.
    """

    def validate(self, value, force=False):
        membrane_tool = plone_api.portal.get_tool("membrane_tool")
        usernames = membrane_tool._catalog.uniqueValuesFor("exact_getUserId")
        if value in usernames:
            brains = membrane_tool.searchResults(exact_getUserId=value)

            if brains and self.context != brains[0].getObject():
                raise Invalid(_("A user with this username already exists"))

        return super().validate(value)


validator.WidgetValidatorDiscriminators(
    UsernameValidator, field=IUserProfile["username"]
)
