import { eq } from 'drizzle-orm';
import { authUsers, customAuthSchema, issueServiceToken } from '@platform-modules/auth/engine-custom';
import { createNeonHttpClient } from '@platform-modules/db/neon-http';

// Mints ONE opaque service token (PAT) bound to an existing admin user and prints
// it exactly once. The raw token is unrecoverable afterwards (only its SHA-256
// hash is stored) — capture it now and set it as MOD_CMS_ADMIN_TOKEN for the MCP
// server (and as a `wrangler secret` if the agent path runs in the Worker).
//
// COMPROMISE RESPONSE (PAT semantics — read before an incident): service tokens are
// intentionally NOT coupled to sessionVersion, so bumping a user's session version
// ("log out everywhere" / password reset) does NOT revoke them. To kill agent/CI
// access on account compromise you MUST call revokeServiceTokensForUser(db, serviceTokens, userId)
// (bulk) or revokeServiceToken(db, serviceTokens, id) (single). See
// docs/specs/2026-06-12-auth-util-module-boundaries.md service-token security contract.

type IssueEnv = {
  DATABASE_URL: string;
  SERVICE_TOKEN_EMAIL: string;
  SERVICE_TOKEN_LABEL: string;
  SERVICE_TOKEN_TTL_DAYS?: string;
};

const REQUIRED_ENV_KEYS = ['DATABASE_URL', 'SERVICE_TOKEN_EMAIL', 'SERVICE_TOKEN_LABEL'] as const;

function parseEnv(): IssueEnv {
  const missing = REQUIRED_ENV_KEYS.filter((key) => !process.env[key]?.trim());
  if (missing.length > 0) {
    throw new Error(`issue-service-token: missing required environment variable(s): ${missing.join(', ')}`);
  }
  return {
    DATABASE_URL: process.env.DATABASE_URL!,
    SERVICE_TOKEN_EMAIL: process.env.SERVICE_TOKEN_EMAIL!,
    SERVICE_TOKEN_LABEL: process.env.SERVICE_TOKEN_LABEL!,
    SERVICE_TOKEN_TTL_DAYS: process.env.SERVICE_TOKEN_TTL_DAYS,
  };
}

async function main(): Promise<void> {
  const env = parseEnv();
  const db = createNeonHttpClient({ connectionString: env.DATABASE_URL, schema: customAuthSchema });

  const [user] = await db
    .select({ id: authUsers.id })
    .from(authUsers)
    .where(eq(authUsers.email, env.SERVICE_TOKEN_EMAIL.toLowerCase()))
    .limit(1);

  if (!user) {
    throw new Error(`no user found for SERVICE_TOKEN_EMAIL="${env.SERVICE_TOKEN_EMAIL}" — seed the admin first`);
  }

  const ttlDays = env.SERVICE_TOKEN_TTL_DAYS ? Number(env.SERVICE_TOKEN_TTL_DAYS) : undefined;
  if (ttlDays !== undefined && (!Number.isFinite(ttlDays) || ttlDays <= 0)) {
    throw new Error(`SERVICE_TOKEN_TTL_DAYS must be a positive number, got "${env.SERVICE_TOKEN_TTL_DAYS}"`);
  }

  const { id, token } = await issueServiceToken(
    db,
    { authUsers: customAuthSchema.authUsers, serviceTokens: customAuthSchema.serviceTokens },
    {
      userId: user.id,
      label: env.SERVICE_TOKEN_LABEL,
      ttlMs: ttlDays !== undefined ? ttlDays * 24 * 60 * 60 * 1000 : undefined,
    },
  );

  console.log(`service token issued (id=${id}, label="${env.SERVICE_TOKEN_LABEL}"${ttlDays ? `, ttl=${ttlDays}d` : ', non-expiring'})`);
  console.log('SET THIS NOW — it will not be shown again:');
  console.log(`MOD_CMS_ADMIN_TOKEN=${token}`);
}

main().catch((err: unknown) => {
  const message = err instanceof Error ? err.message : String(err);
  console.error(`issue-service-token failed: ${message}`);
  process.exitCode = 1;
});
