// Profile-edit validation, mirroring the backend (account/profile.go,
// account/timezone.go) so the form can disable Save and flag fields before a round
// trip. Pure and unit-tested.

/** maxDisplayName caps the editable display name in runes. */
export const maxDisplayName = 32;

/** maxDisplayNameSpecials caps the total special characters (every rune that is neither a
 *  letter, a space, nor a digit — i.e. the "." / "_" separators) a display name may carry.
 *  A trailing digit run is bounded separately by displayNameRe. Mirrors the Go rule. */
export const maxDisplayNameSpecials = 5;

/** maxAwayMinutes bounds the daily away window's length (12 h). */
export const maxAwayMinutes = 12 * 60;

// Unicode letters joined by single space / "." / "_" separators, where a "." or "_"
// may be followed by a single space. No leading separator and no adjacent separators
// except "<dot|underscore> <space>". The name may end with EITHER a single trailing "."
// (an initial) OR a run of 1–5 digits (a number or year), but not both; digits never
// appear elsewhere. Same rule as the Go displayNameRe.
const displayNameRe = /^\p{L}+(?:(?:[._] ?| )\p{L}+)*(?:\.|[0-9]{1,5})?$/u;

/** displayNameError returns true when the trimmed name is a valid display name. */
export function validDisplayName(raw: string): boolean {
  const name = raw.trim();
  const chars = [...name];
  if (name.length === 0 || chars.length > maxDisplayName || !displayNameRe.test(name)) return false;
  const specials = chars.filter((c) => c !== ' ' && !/\p{L}/u.test(c) && !/[0-9]/.test(c)).length;
  return specials <= maxDisplayNameSpecials;
}

// A pragmatic email check (the backend re-validates with net/mail). Rejects spaces
// and requires a local part, an @, and a dotted domain.
const emailRe = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;

/** validEmail reports whether email is a plausible address. */
export function validEmail(email: string): boolean {
  return emailRe.test(email.trim());
}

/** toMinutes parses an "HH:MM" time-of-day into minutes since midnight, or null. */
export function toMinutes(hhmm: string): number | null {
  const m = /^(\d{2}):(\d{2})$/.exec(hhmm);
  if (!m) return null;
  const h = Number(m[1]);
  const min = Number(m[2]);
  if (h > 23 || min > 59) return null;
  return h * 60 + min;
}

/** awayDurationOk reports whether the away window (wrapping midnight) is <= 12 h. */
export function awayDurationOk(start: string, end: string): boolean {
  const s = toMinutes(start);
  const e = toMinutes(end);
  if (s === null || e === null) return false;
  let d = e - s;
  if (d < 0) d += 24 * 60;
  return d <= maxAwayMinutes;
}

/** The real-world set of unique UTC offsets, for the timezone dropdown. */
export const timezoneOffsets: string[] = [
  '-12:00', '-11:00', '-10:00', '-09:30', '-09:00', '-08:00', '-07:00', '-06:00',
  '-05:00', '-04:00', '-03:30', '-03:00', '-02:00', '-01:00', '+00:00', '+01:00',
  '+02:00', '+03:00', '+03:30', '+04:00', '+04:30', '+05:00', '+05:30', '+05:45',
  '+06:00', '+06:30', '+07:00', '+08:00', '+08:45', '+09:00', '+09:30', '+10:00',
  '+10:30', '+11:00', '+12:00', '+12:45', '+13:00', '+14:00',
];

/** isOffsetZone reports whether a stored timezone is a "±HH:MM" offset. */
export function isOffsetZone(tz: string): boolean {
  return /^[+-]\d{2}:\d{2}$/.test(tz);
}

/** browserOffset returns the client's current UTC offset as "±HH:MM". */
export function browserOffset(): string {
  const mins = -new Date().getTimezoneOffset(); // getTimezoneOffset is minutes behind UTC
  const sign = mins < 0 ? '-' : '+';
  const abs = Math.abs(mins);
  const hh = String(Math.floor(abs / 60)).padStart(2, '0');
  const mm = String(abs % 60).padStart(2, '0');
  return `${sign}${hh}:${mm}`;
}

/** Hour options "00".."23" for the away-window pickers. */
export const awayHours: string[] = Array.from({ length: 24 }, (_, i) => String(i).padStart(2, '0'));

/** Minute options on a 10-minute step. */
export const awayMinutes: string[] = ['00', '10', '20', '30', '40', '50'];

/**
 * signInProvidersVisible reports whether the profile may surface competing sign-in providers — the
 * Telegram and VK link / unlink entries. Inside a proprietary Mini App host (Telegram or VK) only
 * email linking is offered, a platform ToS requirement: VK forbids showing a Telegram login, and we
 * mirror it in Telegram for VK. The web and native builds are not proprietary hosts, so they show
 * both providers. Pure, so it is unit-tested.
 */
export function signInProvidersVisible(insideTelegramHost: boolean, insideVKHost: boolean): boolean {
  return !insideTelegramHost && !insideVKHost;
}
