import {
  HUB_SESSION_MANAGER,
  type IHubSessionManager,
} from '@aurora/modules/authentication/domain/ports';
import { Inject, Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import type { Response } from 'express';

const COOKIE_NAME = 'hub_session';
const DEFAULT_TTL_SECONDS = 28800; // 8 hours

/**
 * Hub session cookie helper (Design Decision D2).
 *
 * Responsible for signing/verifying the hub_session JWT and setting/clearing
 * the httpOnly cookie. All cookie attributes are env-driven.
 *
 * Uses the HUB_SESSION_MANAGER port (RS256 key pair) so the hub_session token uses the same
 * key pair as access tokens — single key-management concern.
 */
@Injectable()
export class OAuthHubSessionService {
  private readonly ttlSeconds: number;
  private readonly secure: boolean;
  private readonly domain: string | undefined;
  private readonly sameSite: 'lax' | 'strict' | 'none';

  constructor(
    @Inject(HUB_SESSION_MANAGER)
    private readonly hubSessionManager: IHubSessionManager,
    private readonly configService: ConfigService,
  ) {
    const rawTtl = Number(
      this.configService.get<string>('OAUTH_SESSION_COOKIE_TTL', String(DEFAULT_TTL_SECONDS)),
    );
    // Guard against a non-numeric env value (e.g. OAUTH_SESSION_COOKIE_TTL='') which would
    // make Number() return NaN → maxAge: NaN → browser treats the cookie as session-only.
    this.ttlSeconds = Number.isFinite(rawTtl) ? rawTtl : DEFAULT_TTL_SECONDS;
    const configuredSecure =
      this.configService.get<string>('OAUTH_SESSION_COOKIE_SECURE', 'false') === 'true';
    this.domain = this.configService.get<string | undefined>('OAUTH_SESSION_COOKIE_DOMAIN');
    const rawSameSite = this.configService.get<string>('OAUTH_SESSION_COOKIE_SAME_SITE', 'lax');
    this.sameSite = rawSameSite === 'strict' || rawSameSite === 'none' ? rawSameSite : 'lax';

    // Browsers silently drop SameSite=None cookies that lack the Secure attribute.
    // When sameSite resolves to 'none', force secure=true regardless of the env var.
    // This is a hard requirement, not a best-effort: an insecure SameSite=None cookie
    // is invisible to the browser and the /authorize round-trip would silently break.
    this.secure = this.sameSite === 'none' ? true : configuredSecure;
  }

  signHubSession(accountId: string): string {
    return this.hubSessionManager.signHubSession(accountId);
  }

  verifyHubSession(token: string): string | null {
    return this.hubSessionManager.verifyHubSession(token);
  }

  createSessionCookie(res: Response, accountId: string): void {
    const token = this.signHubSession(accountId);
    res.cookie(COOKIE_NAME, token, {
      httpOnly: true,
      secure: this.secure,
      sameSite: this.sameSite,
      domain: this.domain,
      maxAge: this.ttlSeconds * 1000,
      path: '/',
    });
  }

  clearSessionCookie(res: Response): void {
    res.clearCookie(COOKIE_NAME, {
      httpOnly: true,
      secure: this.secure,
      sameSite: this.sameSite,
      domain: this.domain,
      path: '/',
    });
  }

  verifyAccessTokenAndGetAccountId(accessToken: string): string | null {
    return this.hubSessionManager.verifyAccessToken(accessToken);
  }
}
