import { Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { createHash, randomBytes } from 'crypto';
import { loadEnv } from '@/config/env';

export interface AccessTokenPayload {
  sub: string; // user id (or platform user id)
  typ: 'user' | 'platform';
  tid: string | null; // tenant id
  tcode?: string; // tenant code
  sid: string; // session id
  name: string;
  email: string;
  storer: string | null;
  admin: boolean; // tenant admin
  prole?: string; // platform role
  perm: string[];
  wh: string[] | 'ALL';
  feat: string[];
}

@Injectable()
export class TokenService {
  private readonly env = loadEnv();
  constructor(private readonly jwt: JwtService) {}

  signAccessToken(payload: AccessTokenPayload): { token: string; expiresIn: number } {
    const token = this.jwt.sign(payload, { secret: this.env.JWT_ACCESS_SECRET, expiresIn: this.env.JWT_ACCESS_TTL });
    return { token, expiresIn: parseTtlSeconds(this.env.JWT_ACCESS_TTL) };
  }

  verifyAccessToken(token: string): AccessTokenPayload {
    return this.jwt.verify<AccessTokenPayload>(token, { secret: this.env.JWT_ACCESS_SECRET });
  }

  /** Opaque refresh token: random, only its hash is stored. */
  generateRefreshToken(): { raw: string; hash: string } {
    const raw = randomBytes(48).toString('base64url');
    return { raw, hash: TokenService.hash(raw) };
  }

  /** API keys look like wms_live_<32 bytes>. The prefix is stored for display. */
  generateApiKey(): { raw: string; hash: string; prefix: string } {
    const raw = `wms_${randomBytes(32).toString('base64url')}`;
    return { raw, hash: TokenService.hash(raw), prefix: raw.slice(0, 12) };
  }

  static hash(value: string): string {
    return createHash('sha256').update(value).digest('hex');
  }
}

export function parseTtlSeconds(ttl: string): number {
  const m = /^(\d+)([smhd])$/.exec(ttl.trim());
  if (!m) return 900;
  const n = Number(m[1]);
  switch (m[2]) {
    case 's':
      return n;
    case 'm':
      return n * 60;
    case 'h':
      return n * 3600;
    case 'd':
      return n * 86400;
    default:
      return 900;
  }
}
