# This file is part of Gajim.
#
# SPDX-License-Identifier: GPL-3.0-only

import datetime as dt
from string import Template
from zoneinfo import ZoneInfo

from gi.repository import Gio
from nbxmpp.const import Affiliation
from nbxmpp.const import Chatstate
from nbxmpp.const import Role
from nbxmpp.modules.vcard4 import TzProperty
from nbxmpp.protocol import JID

from gajim.common import app
from gajim.common.const import GIO_TLS_ERRORS
from gajim.common.i18n import _
from gajim.common.i18n import ngettext
from gajim.common.i18n import p_
from gajim.common.iana import get_zone_data


def format_idle_time(idle_time: dt.datetime) -> str:
    now = dt.datetime.now(dt.UTC)

    now_date = now.date()
    idle_date = idle_time.date()

    if idle_date == now_date:
        return idle_time.strftime(app.settings.get("time_format"))
    if idle_date == now_date - dt.timedelta(days=1):
        return _("Yesterday (%s)") % idle_time.strftime(app.settings.get("time_format"))
    if idle_date >= now_date - dt.timedelta(days=6):
        return idle_time.strftime(f"%a {app.settings.get('time_format')}")

    return idle_date.strftime(app.settings.get("date_format"))


def get_subscription_request_msg(account: str | None = None) -> str:
    if account is None:
        return _("I would like to add you to my contact list.")

    message = app.settings.get_account_setting(account, "subscription_request_msg")
    if message:
        return message

    message = _("Hello, I am $name. %s") % message
    return Template(message).safe_substitute({"name": app.nicks[account]})


def get_moderation_text(by: str | JID | None, reason: str | None) -> str:
    text = _("This message has been moderated")

    if by is not None:
        text = _("This message has been moderated by %s") % by

    if reason is None:
        return text

    reason_text = _("(reason: %s)") % reason
    return f"{text} {reason_text}"


def get_retraction_text(timestamp: dt.datetime) -> str:
    dt_format = app.settings.get("date_time_format")
    timestamp = timestamp.astimezone()
    timestamp_formatted = timestamp.strftime(dt_format)
    return _("This message has been retracted on {date}").format(
        date=timestamp_formatted
    )


def get_uf_sub(sub: str) -> str:
    if sub == "none":
        return p_("Contact subscription", "None")

    if sub == "to":
        return p_("Contact subscription", "To")

    if sub == "from":
        return p_("Contact subscription", "From")

    if sub == "both":
        return p_("Contact subscription", "Both")

    return p_("Contact subscription", "Unknown")


def get_uf_ask(ask: str | None) -> str:
    if ask is None:
        return p_("Contact subscription", "None")

    if ask == "subscribe":
        return p_("Contact subscription", "Subscribe")

    return ask


def get_uf_role(role: Role | str, plural: bool = False) -> str:
    """plural determines if you get Moderators or Moderator"""
    if not isinstance(role, str):
        role = role.value

    if role == "none":
        return p_("Group chat contact role", "None")
    if role == "moderator":
        if plural:
            return p_("Group chat contact role", "Moderators")
        return p_("Group chat contact role", "Moderator")
    if role == "participant":
        if plural:
            return p_("Group chat contact role", "Participants")
        return p_("Group chat contact role", "Participant")
    if role == "visitor":
        if plural:
            return p_("Group chat contact role", "Visitors")
        return p_("Group chat contact role", "Visitor")
    return ""


def get_uf_affiliation(affiliation: Affiliation | str, plural: bool = False) -> str:
    """Get a nice and translated affilition for muc"""
    if not isinstance(affiliation, str):
        affiliation = affiliation.value

    if affiliation == "none":
        return p_("Group chat contact affiliation", "None")
    if affiliation == "owner":
        if plural:
            return p_("Group chat contact affiliation", "Owners")
        return p_("Group chat contact affiliation", "Owner")
    if affiliation == "admin":
        if plural:
            return p_("Group chat contact affiliation", "Administrators")
        return p_("Group chat contact affiliation", "Administrator")
    if affiliation == "member":
        if plural:
            return p_("Group chat contact affiliation", "Members")
        return p_("Group chat contact affiliation", "Member")
    return ""


def get_uf_relative_time(date_time: dt.datetime, now: dt.datetime | None = None) -> str:
    # `date_time` must be an aware datetime object with timezone UTC
    # The parameter `now` is only used by unittests

    if date_time.tzinfo != dt.UTC:
        raise ValueError("param 'date_time' with timezone =! UTC")

    if now is None:  # used by unittest
        now = dt.datetime.now(dt.UTC).astimezone()
    else:
        if now.tzinfo != dt.UTC:
            raise ValueError("param 'now' with timezone =! UTC")

    date_time = date_time.astimezone()
    timespan = now - date_time

    if timespan < dt.timedelta(minutes=1):
        return _("Just now")
    if timespan < dt.timedelta(minutes=15):
        minutes = int(timespan.seconds / 60)
        return ngettext("%(number)s min ago", "%(number)s mins ago", minutes) % {
            "number": minutes
        }

    today = now.date()
    if date_time.date() == today:
        format_string = app.settings.get("time_format")
        return date_time.strftime(format_string)
    yesterday = now.date() - dt.timedelta(days=1)
    if date_time.date() == yesterday:
        return _("Yesterday")
    if timespan < dt.timedelta(days=7):  # this week
        return date_time.strftime("%a")  # weekday
    if timespan < dt.timedelta(days=365):  # this year
        return date_time.strftime("%b %d")
    return str(date_time.year)


def get_time_zone_string(prop: TzProperty) -> str:
    try:
        tzinfo = ZoneInfo(prop.value)
    except Exception:
        return ""

    t_format = app.settings.get("time_format")
    remote_dt_str = dt.datetime.now(tz=tzinfo).strftime(t_format)
    data = get_zone_data(prop.value)

    return f"{remote_dt_str} ({data.full_name})"


def chatstate_to_string(chatstate: Chatstate | None) -> str:
    if chatstate is None:
        return ""

    if chatstate == Chatstate.ACTIVE:
        return ""

    if chatstate == Chatstate.COMPOSING:
        return _("is composing a message…")

    if chatstate in (Chatstate.INACTIVE, Chatstate.GONE):
        return _("is doing something else")

    if chatstate == Chatstate.PAUSED:
        return _("paused composing a message")

    raise ValueError(f"unknown value: {chatstate}")


def get_tls_error_phrases(tls_errors: set[Gio.TlsCertificateFlags]) -> list[str]:
    return [GIO_TLS_ERRORS[err] for err in tls_errors]
