import { computed, inject, Injectable, signal, type Signal } from '@angular/core';
import { type CoreLang } from '@src/src/app/aurora.types';
import { Apollo } from 'apollo-angular';
import { firstValueFrom } from 'rxjs';
import { SessionService } from '../session';
import { coreGetLangsQuery } from './lang.graphql';

/** Cache TTL for langs in localStorage. Langs change rarely; one day is generous. */
const LANGS_TTL_MS = 24 * 60 * 60 * 1000;

/**
 * Single source of truth for system languages across the admin. Wraps the
 * upstream `coreGetLangs` query (served by the JSON adapter at
 * `backend/src/@aurora/modules/lang/core-langs.ts`) and exposes signals so
 * consumers (site-header lang switcher, account form selector, future
 * features) share a single fetch.
 *
 * Persistence is delegated to `SessionService` so the cache survives F5 and
 * lives under the same single localStorage key as the rest of SPA state.
 * On first load after a fresh tab, the signal is hydrated from the session
 * if non-expired — render is instant without waiting for the network.
 */
@Injectable({ providedIn: 'root' })
export class LangService {
  private readonly apollo = inject(Apollo);
  private readonly session = inject(SessionService);
  private readonly _langs = signal<CoreLang[]>(this.session.get('langs') ?? []);
  private loadPromise: Promise<void> | null = null;

  /** All languages as returned by the backend, including inactive entries. */
  readonly all: Signal<CoreLang[]> = this._langs.asReadonly();

  /** Active languages sorted by `sort` — the canonical list for UI dropdowns. */
  readonly active = computed<CoreLang[]>(() =>
    this._langs()
      .filter((lang) => lang.isActive)
      .sort((a, b) => (a.sort ?? 0) - (b.sort ?? 0)),
  );

  /**
   * Fetches languages on first call (or when cache is stale). Concurrent
   * callers share the in-flight promise. Fresh callers (cache hit within
   * TTL) resolve immediately without hitting the network.
   */
  async load(): Promise<void> {
    if (this.isFresh()) return;
    if (this.loadPromise) return this.loadPromise;

    this.loadPromise = firstValueFrom(
      this.apollo.query<{ coreGetLangs: CoreLang[] }>({
        query: coreGetLangsQuery,
        fetchPolicy: 'cache-first',
      }),
    )
      .then((result) => {
        const langs = result.data?.coreGetLangs ?? [];
        this._langs.set(langs);
        this.session.set('langs', langs);
        this.session.set('langsCachedAt', Date.now());
      })
      .catch((error) => {
        // Reset so a future call can retry instead of permanently failing.
        this.loadPromise = null;
        throw error;
      });

    return this.loadPromise;
  }

  /** True when we have at least one lang in memory and the cache is within TTL. */
  private isFresh(): boolean {
    if (this._langs().length === 0) return false;
    const cachedAt = this.session.get('langsCachedAt') ?? 0;
    return Date.now() - cachedAt < LANGS_TTL_MS;
  }
}
