/**
 * Non-side-effect class export for `<relative-time-ui>`.
 *
 * Importing this file gives you the class without auto-registering the
 * tag. Useful for test isolation, subclassing with tag-name override,
 * or selective composition.
 *
 * The auto-register path stays at `@adia-ai/web-components/components/relative-time`
 * (which imports this file + calls `defineIfFree()`).
 *
 * @see ../../USAGE.md#registration--auto-vs-explicit
 */

/**
 * <relative-time-ui datetime="2026-05-25T09:00:00Z"></relative-time-ui>
 *
 * Renders a timestamp as a human-readable relative phrase
 * ("3 hours ago", "in 2 days", "yesterday") using
 * `Intl.RelativeTimeFormat`. Re-renders on a configurable tick
 * so the displayed text stays current.
 */

import { UIElement } from '../../core/element.js';

// Threshold table — each row: { limit (seconds), unit, divisor }.
// First row whose absolute delta is ≤ limit wins.
const THRESHOLDS = [
  { limit: 60,            unit: 'second', divisor: 1 },
  { limit: 60 * 60,       unit: 'minute', divisor: 60 },
  { limit: 60 * 60 * 24,  unit: 'hour',   divisor: 60 * 60 },
  { limit: 60 * 60 * 24 * 7,  unit: 'day',  divisor: 60 * 60 * 24 },
  { limit: 60 * 60 * 24 * 30, unit: 'week', divisor: 60 * 60 * 24 * 7 },
  { limit: 60 * 60 * 24 * 365, unit: 'month', divisor: 60 * 60 * 24 * 30 },
  { limit: Infinity,           unit: 'year',  divisor: 60 * 60 * 24 * 365 },
];

export class UIRelativeTime extends UIElement {
  static properties = {
    datetime:       { type: String, default: '',     reflect: true },
    timeStyle:      { type: String, default: 'long', reflect: true, attribute: 'time-style' },
    numeric:        { type: String, default: 'auto', reflect: true },
    locale:         { type: String, default: '',     reflect: true },
    updateInterval: { type: Number, default: 60,     reflect: true, attribute: 'update-interval' },
  };

  static template = () => null;

  #tickHandle = null;

  #resolveLocale() {
    if (this.locale) return this.locale;
    return this.ownerDocument?.documentElement?.lang || undefined;
  }

  #format() {
    if (!this.datetime) return '';
    const target = new Date(this.datetime);
    if (Number.isNaN(target.getTime())) return '';
    const deltaSec = (target.getTime() - Date.now()) / 1000;
    const abs = Math.abs(deltaSec);
    const row = THRESHOLDS.find((t) => abs <= t.limit) || THRESHOLDS[THRESHOLDS.length - 1];
    const value = Math.round(deltaSec / row.divisor);
    try {
      const fmt = new Intl.RelativeTimeFormat(this.#resolveLocale(), {
        style:   this.timeStyle,
        numeric: this.numeric,
      });
      return fmt.format(value, row.unit);
    } catch {
      // Fallback if Intl.RelativeTimeFormat unsupported (very old browsers).
      const past = deltaSec < 0;
      const absVal = Math.abs(value);
      return past ? `${absVal} ${row.unit}${absVal === 1 ? '' : 's'} ago` : `in ${absVal} ${row.unit}${absVal === 1 ? '' : 's'}`;
    }
  }

  #fullTitle() {
    if (!this.datetime) return '';
    const target = new Date(this.datetime);
    if (Number.isNaN(target.getTime())) return '';
    try {
      return new Intl.DateTimeFormat(this.#resolveLocale(), {
        dateStyle: 'full',
        timeStyle: 'long',
      }).format(target);
    } catch {
      return target.toString();
    }
  }

  connected() {
    super.connected();
    if (!this.hasAttribute('role')) this.setAttribute('role', 'time');
    this.#startTick();
  }

  render() {
    const text = this.#format();
    this.textContent = text;
    // Set a hover-title with the full datetime for precision-on-hover
    // unless the consumer set their own title attribute.
    if (text && !this.hasAttribute('data-suppress-title')) {
      this.setAttribute('title', this.#fullTitle());
    }
    // Mirror the timestamp to a `datetime` attribute on a child <time>
    // would be ideal but the element IS the time; expose via aria.
    if (this.datetime) {
      this.setAttribute('datetime', this.datetime);
    }
    // Restart tick if updateInterval changed.
    this.#startTick();
  }

  #startTick() {
    this.#stopTick();
    const interval = Number(this.updateInterval);
    if (!Number.isFinite(interval) || interval <= 0) return;
    this.#tickHandle = setInterval(() => {
      // Re-render by re-running the formatter; signal-backed render() is
      // overkill since only Date.now() changed.
      this.textContent = this.#format();
    }, interval * 1000);
  }

  #stopTick() {
    if (this.#tickHandle != null) {
      clearInterval(this.#tickHandle);
      this.#tickHandle = null;
    }
  }

  disconnected() {
    super.disconnected();
    this.#stopTick();
  }
}
