import 'dotenv/config';
import Joi from 'joi';

export interface Env {
  NODE_ENV: 'development' | 'test' | 'production';
  PORT: number;
  DATABASE_URL: string;
  CORS_ORIGIN: string;
  TRUST_PROXY: boolean;
  RATE_LIMIT_WINDOW_MS: number;
  RATE_LIMIT_MAX: number;
  AUTH_RATE_LIMIT_WINDOW_MS: number;
  AUTH_RATE_LIMIT_MAX: number;
  FIREBASE_PROJECT_ID: string;
  FIREBASE_CLIENT_EMAIL: string;
  FIREBASE_PRIVATE_KEY: string;
  FIREBASE_WEB_API_KEY: string;
  DOCS_USERNAME: string;
  DOCS_PASSWORD: string;
  DOCS_SESSION_MINUTES: number;
  // Optional overrides for the super admin bootstrap — prisma/seed.ts falls
  // back to a fixed email and a freshly generated password when unset. Not
  // required, so the app boots fine without ever touching .env for this.
  SUPER_ADMIN_EMAIL?: string;
  SUPER_ADMIN_PASSWORD?: string;
  // Shown to users of a suspended organization (403 `tenant_suspended`).
  SUPPORT_EMAIL: string;
  // Links in emails (invites, password reset) point at the tenant web app.
  TENANT_APP_URL: string;
  // Mailgun (transactional email).
  MAILGUN_API_KEY: string;
  MAILGUN_DOMAIN: string;
  MAILGUN_REGION: 'us' | 'eu';
  MAIL_FROM: string;
  MAILGUN_TEMPLATE_INVITE: string;
  MAILGUN_TEMPLATE_PASSWORD_RESET: string;
  MAILGUN_TEMPLATE_ACCESS_REQUEST: string;
  MAILGUN_TEMPLATE_ORGANIZATION_ADMIN_INVITE: string;
  // S3 storage for images (logos, profile photos). Image uploads answer 503 until these are set.
  AWS_REGION: string;
  AWS_ACCESS_KEY_ID: string;
  AWS_SECRET_ACCESS_KEY: string;
  AWS_S3_BUCKET: string;
  /** Optional CloudFront / public-bucket base URL; without it files are served by signed URLs. */
  AWS_S3_PUBLIC_BASE_URL: string;
  /** How long signed file URLs stay valid, in seconds. */
  AWS_S3_SIGNED_URL_TTL: number;
  /** Public bucket for brand assets (the email logo); `npm run storage:upload-brand` uploads there. */
  AWS_S3_PUBLIC_BUCKET: string;
}

const schema = Joi.object<Env>({
  NODE_ENV: Joi.string().valid('development', 'test', 'production').default('development'),
  PORT: Joi.number().integer().min(1).max(65535).default(3000),
  DATABASE_URL: Joi.string().uri({ scheme: ['postgresql', 'postgres'] }).required(),
  CORS_ORIGIN: Joi.string().allow('').default(''),
  TRUST_PROXY: Joi.boolean().default(false),
  RATE_LIMIT_WINDOW_MS: Joi.number().integer().min(1000).default(15 * 60 * 1000),
  RATE_LIMIT_MAX: Joi.number().integer().min(1).default(100),
  // Tighter limit for /api/v1/auth/signup and /login (brute-force / credential-stuffing protection).
  AUTH_RATE_LIMIT_WINDOW_MS: Joi.number().integer().min(1000).default(15 * 60 * 1000),
  AUTH_RATE_LIMIT_MAX: Joi.number().integer().min(1).default(10),
  // Firebase Admin SDK service-account credentials, used to verify client ID tokens
  // and to create users server-side during signup.
  FIREBASE_PROJECT_ID: Joi.string().required(),
  FIREBASE_CLIENT_EMAIL: Joi.string().required(),
  FIREBASE_PRIVATE_KEY: Joi.string().required(),
  // Firebase's public Web API key (Console → Project Settings → General), used to
  // call the Identity Toolkit REST API for password sign-in — a different
  // credential from the service account above, and safe to be non-secret.
  FIREBASE_WEB_API_KEY: Joi.string().required(),
  // Gate for /docs (Swagger UI) — plain HTTP Basic Auth, unrelated to Firebase.
  DOCS_USERNAME: Joi.string().required(),
  DOCS_PASSWORD: Joi.string().min(8).required(),
  DOCS_SESSION_MINUTES: Joi.number().integer().min(1).default(10),
  // Optional — see seedSuperAdmin() in prisma/seed.ts.
  SUPER_ADMIN_EMAIL: Joi.string().email().optional(),
  SUPER_ADMIN_PASSWORD: Joi.string().min(8).optional(),
  // Shown to users of a suspended organization (403 `tenant_suspended`).
  SUPPORT_EMAIL: Joi.string().email().default('support@blenaxis.dev'),
  // Links in emails (invites, password reset) point at the tenant web app.
  TENANT_APP_URL: Joi.string().uri({ scheme: ['http', 'https'] }).default('http://localhost:5173'),
  // Mailgun (transactional email). Leave the key empty to skip sending: the
  // email is logged instead (outside production), which is fine for local work.
  MAILGUN_API_KEY: Joi.string().allow('').default(''),
  MAILGUN_DOMAIN: Joi.string().allow('').default(''),
  MAILGUN_REGION: Joi.string().valid('us', 'eu').default('us'),
  // Defaults to "BlenAxis <no-reply@MAILGUN_DOMAIN>".
  MAIL_FROM: Joi.string().allow('').default(''),
  // Names of the templates uploaded to Mailgun from email-templates/.
  MAILGUN_TEMPLATE_INVITE: Joi.string().default('blenaxis-invite'),
  MAILGUN_TEMPLATE_PASSWORD_RESET: Joi.string().default('blenaxis-password-reset'),
  MAILGUN_TEMPLATE_ACCESS_REQUEST: Joi.string().default('blenaxis-access-request'),
  MAILGUN_TEMPLATE_ORGANIZATION_ADMIN_INVITE: Joi.string().default('blenaxis-organization-admin-invite'),
  AWS_REGION: Joi.string().allow('').default(''),
  AWS_ACCESS_KEY_ID: Joi.string().allow('').default(''),
  AWS_SECRET_ACCESS_KEY: Joi.string().allow('').default(''),
  AWS_S3_BUCKET: Joi.string().allow('').default(''),
  AWS_S3_PUBLIC_BASE_URL: Joi.string().uri({ scheme: ['https', 'http'] }).allow('').default(''),
  AWS_S3_SIGNED_URL_TTL: Joi.number().integer().min(60).max(604800).default(3600),
  AWS_S3_PUBLIC_BUCKET: Joi.string().allow('').default('blenaxis-public-assets'),
});

const { value, error } = schema.validate(process.env, {
  allowUnknown: true,
  abortEarly: false,
});

if (error) {
  // Only print field names, never database credentials.
  const fields = error.details.map((detail) => detail.path.join('.')).join(', ');
  throw new Error(`Invalid or missing environment settings: ${fields}. Check .env.example.`);
}

export const env = value as Env;
