from plone import api
from plone.app.z3cform.widgets.text import TextAreaWidget
from plone.dexterity.browser import edit
from plone.dexterity.browser import view
from plone.dexterity.browser.edit import DefaultEditForm
from z3c.form.browser.text import TextWidget


def icon_for_field(fieldname):
    """Helper method for translating from field names to
    prototype classes"""
    if "." in fieldname:
        # Remove behaviour prefixes from fieldnames
        fieldname = fieldname.split(".")[-1]
    default = "icon-right-open"
    mapping = {
        "address": "icon-home",
        "department": "icon-building",
        "email": "icon-mail",
        "industry": "icon-asterisk",
        "mobile": "icon-phone",
        "organization": "icon-asterisk",
        "primary_location": "icon-globe",
        "telephone": "icon-phone",
        "time_zone": "icon-clock",
        "url": "icon-globe",
    }
    return mapping.get(fieldname, default)


def get_fields_for_template(form):
    """Helper method to get widget information for view/edit templates"""
    fields = []
    for field_name in form.widgets.keys():
        widget = form.widgets[field_name]
        if widget.error:
            error_html = widget.error.render()
        else:
            error_html = None

        fields.append(
            {
                "name": field_name,
                "label": widget.label,
                "description": widget.field.description,
                "read_only": widget.mode == "display",
                "html": widget.render(),
                "error_html": error_html,
                "raw": widget.value,
                "icon_class": icon_for_field(field_name),
            }
        )
    return fields


class UserProfileBaseForm:
    """Custom user profile form base allowing field visibility
    to be controlled via registry settings
    """

    def _hidden_fields(self):
        """Look up hidden fields from registry.

        Note that if you're hiding fields from additional schemata, those should
        be prefixed in the registry value, for example: IUserProfileAdditional.job_title
        """

        hidden = (
            api.portal.get_registry_record("ploneintranet.userprofile.hidden_fields")
            or ()
        )
        # Portrait is edited elsewhere; contact history is auto-managed
        hidden = hidden + ("portrait", "recent_contacts")
        return hidden

    def _read_only_fields(self):
        """Look up read-only fields from registry"""
        read_only = api.portal.get_registry_record(
            "ploneintranet.userprofile.read_only_fields"
        )
        return read_only

    def updateFieldsFromSchemata(self):
        """Remove hidden fields from the form"""
        super().updateFieldsFromSchemata()
        hidden_fields = self._hidden_fields()
        for hidden_field in hidden_fields:
            self.fields = self.fields.omit(hidden_field)

    def updateWidgets(self):
        """Mark read only fields as 'display'"""
        super().updateWidgets()
        read_only_fields = self._read_only_fields()
        for fieldname, widget in self.widgets.items():
            if fieldname in read_only_fields:
                widget.mode = "display"
            # Set up sizes correctly for prototype
            if isinstance(widget, TextWidget):
                if not widget.size:
                    widget.size = 50
            if isinstance(widget, TextAreaWidget):
                if not widget.rows:
                    widget.rows = 14


class UserProfileEditForm(UserProfileBaseForm, DefaultEditForm):
    """Editable user profile form"""

    # The id is used by the injection.
    id = "user-data-view"

    def nextURL(self):
        return f"{self.context.absolute_url()}/@@userprofile-info"


class UserProfileViewForm(UserProfileBaseForm, view.DefaultView):
    """Non-editable user profile form"""

    def _hidden_fields(self):
        hidden = super()._hidden_fields()
        # Hide email if the users wishes so
        if getattr(self.context, "hide_email", False):
            hidden = hidden + ("email",)
        # Username, name and bio are dealt with differently
        # on the profile view page
        return hidden + (
            "username",
            "person_title",
            "first_name",
            "last_name",
            "IUserProfileAdditional.biography",
            "hide_email",
        )


class UserProfileEditView(edit.DefaultEditView):
    """Custom profile edit page that renders the edit form

    data_pat_inject typically points to the form id.

    NB in proto the injection
    source is "user-data-edit::element", but we require the source and target of
    the injection to be identical.
    This is needed because the form must be able to inject itself
    if server-side validation fails.

    To test server-side validation errors,
    input "error" or "debug" into the "industry"
    field when developing locally in debug mode.
    """

    form = UserProfileEditForm

    # PROTO DIVERGENCE: proto uses
    #
    # source: #user-data-view::element;
    # target: #user-data-edit::element
    #  &&
    # source: #portlet-contact;
    # target: #portlet-contact
    #  &&
    # source: #user-info-header figcaption;
    # target: #user-info-header figcaption
    data_pat_inject = "#user-data-view::element"
