import { createPublicKey, verify as cryptoVerify } from 'node:crypto';

import { LICENSE_ED25519_SPKI_DER_B64 } from '../../shared/license/bundledPublicKey';
import { canonicalJson } from '../../shared/license/canonicalJson';
import { isLicensePayloadV1, type LicensePayloadV1 } from '../../shared/license/payloadV1';
import { splitSignedLicenseToken } from '../../shared/license/tokenFormat';
import type { LicenseVerifyFailure } from '../../shared/license/verifyReasons';

export type LicenseVerifyResult =
  | { ok: true; payload: LicensePayloadV1 }
  | { ok: false; reason: LicenseVerifyFailure };

let cachedPublicKey: ReturnType<typeof createPublicKey> | null = null;

function getBundledPublicKey() {
  cachedPublicKey ??= createPublicKey({
    key: Buffer.from(LICENSE_ED25519_SPKI_DER_B64, 'base64'),
    format: 'der',
    type: 'spki',
  });
  return cachedPublicKey;
}

export function verifyLicenseToken(
  token: string,
  opts: {
    nowSec: number;
    deviceId: string;
    /** Старые deviceId (например UUID из userData) — принимаются до повторной активации. */
    alsoAcceptDeviceIds?: readonly string[];
    publicKeyOverrideSpkiDerB64?: string;
  },
): LicenseVerifyResult {
  const parts = splitSignedLicenseToken(token);
  if (!parts) return { ok: false, reason: 'malformed' };

  let payload: unknown;
  try {
    payload = JSON.parse(parts.bodyUtf8) as unknown;
  } catch {
    return { ok: false, reason: 'bad_payload' };
  }
  if (!isLicensePayloadV1(payload)) return { ok: false, reason: 'bad_payload' };

  const body = canonicalJson(payload);
  const msg = Buffer.from(body, 'utf8');
  const publicKey =
    opts.publicKeyOverrideSpkiDerB64 !== undefined
      ? createPublicKey({
          key: Buffer.from(opts.publicKeyOverrideSpkiDerB64, 'base64'),
          format: 'der',
          type: 'spki',
        })
      : getBundledPublicKey();
  const okSig = cryptoVerify(null, msg, publicKey, Buffer.from(parts.signature));
  if (!okSig) return { ok: false, reason: 'bad_signature' };

  if (payload.nbf !== undefined && opts.nowSec < payload.nbf) {
    return { ok: false, reason: 'not_yet_valid' };
  }
  if (opts.nowSec >= payload.exp) return { ok: false, reason: 'expired' };
  if (payload.did !== null) {
    const accepted = new Set<string>([opts.deviceId, ...(opts.alsoAcceptDeviceIds ?? [])]);
    if (!accepted.has(payload.did)) {
      return { ok: false, reason: 'wrong_device' };
    }
  }

  return { ok: true, payload };
}
