import { Injectable, computed, effect, inject, signal } from '@angular/core';
import { Result } from '@shared/kernel/fp';
import { Session } from '../domain/session';
import { DigidAdapter } from '../infrastructure/digid.adapter';

const STORAGE_KEY = 'session-v1';

/** Restore a persisted session (best-effort; corrupt entry → logged out).
    G2: validate the shape before trusting it. G1: the BSN is never persisted
    (see the effect below), so a restored session carries an empty one — it is
    unused after login; only `naam` is shown in the chrome. */
function restore(): Session | null {
  try {
    const raw = sessionStorage.getItem(STORAGE_KEY);
    if (!raw) return null;
    const parsed = JSON.parse(raw) as Partial<Session>;
    return typeof parsed?.naam === 'string' ? { bsn: '', naam: parsed.naam } : null;
  } catch {
    return null;
  }
}

/**
 * Holds the current session for the whole app. Because it is providedIn:'root'
 * there is exactly one instance — every component that injects it sees the same
 * session signal, so logging in is instantly visible everywhere (the guard, the
 * header, etc.). The session is mirrored to sessionStorage so a refresh or a
 * deep-link to a protected route keeps you logged in; it clears when the tab
 * closes. ponytail: sessionStorage, not localStorage — no cross-tab sync, which
 * matches a single-session portal.
 */
@Injectable({ providedIn: 'root' })
export class SessionStore {
  private digid = inject(DigidAdapter);
  private _session = signal<Session | null>(restore());

  readonly session = this._session.asReadonly();
  readonly isAuthenticated = computed(() => this._session() !== null);

  constructor() {
    effect(() => {
      const s = this._session();
      // G1: persist only `naam` — never write the BSN (national ID) to storage.
      if (s) sessionStorage.setItem(STORAGE_KEY, JSON.stringify({ naam: s.naam }));
      else sessionStorage.removeItem(STORAGE_KEY);
    });
  }

  /** Effectful command: authenticate, then store the session on success. */
  async login(bsn: string): Promise<Result<string, Session>> {
    const r = await this.digid.authenticate(bsn);
    if (r.ok) this._session.set(r.value);
    return r;
  }

  logout() {
    this._session.set(null);
  }
}
