import { config as loadEnv } from 'dotenv';
import { resolve, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
import { existsSync, mkdirSync } from 'node:fs';

const __dirname = dirname(fileURLToPath(import.meta.url));
const rootDir = resolve(__dirname, '../..');
const nodeEnv = process.env.NODE_ENV ?? 'development';
const isProduction = nodeEnv === 'production';
const isTest = nodeEnv === 'test';

const rootEnv = resolve(rootDir, '.env');
// Production secrets come from systemd EnvironmentFile — never merge dev .env
if (!isProduction && existsSync(rootEnv)) loadEnv({ path: rootEnv });
else if (!isProduction) loadEnv();

function env(key: string, fallback?: string): string {
  const v = process.env[key];
  if (v !== undefined && v !== '') return v;
  if (fallback !== undefined) return fallback;
  throw new Error(`Missing required env: ${key}`);
}

function envOptional(key: string): string | undefined {
  const v = process.env[key];
  return v && v !== '' ? v : undefined;
}

function requireProduction(key: string, value: string | undefined, minLength = 16): string {
  if (!value || value.length < minLength) {
    throw new Error(
      `[production] ${key} is required (min ${minLength} chars). Set it in the environment file.`,
    );
  }
  return value;
}

// Persistent data directory — survives redeploys
const dataDirRaw = envOptional('DATA_DIR') ?? (isProduction ? undefined : './data');
const dataDir = dataDirRaw
  ? dataDirRaw.startsWith('/') ? dataDirRaw : resolve(rootDir, dataDirRaw)
  : requireProduction('DATA_DIR', undefined);

if (!existsSync(dataDir)) mkdirSync(dataDir, { recursive: true });

const dbPathRaw = envOptional('DATABASE_PATH');
const dbPath = dbPathRaw
  ? dbPathRaw.startsWith('/') ? dbPathRaw : resolve(rootDir, dbPathRaw)
  : resolve(dataDir, 'ticket-gate.sqlite');

const dbDir = dirname(dbPath);
if (!existsSync(dbDir)) mkdirSync(dbDir, { recursive: true });

const publicBaseUrl = isProduction
  ? requireProduction('PUBLIC_BASE_URL', envOptional('PUBLIC_BASE_URL'))
  : (envOptional('PUBLIC_BASE_URL') ?? `http://localhost:${env('PORT', '3847')}`);

const sessionSecret = isProduction
  ? requireProduction('SESSION_SECRET', envOptional('SESSION_SECRET'), 32)
  : env('SESSION_SECRET', 'dev-session-secret-change-me');

const majWpApiKey = isProduction
  ? requireProduction('MAJ_WP_API_KEY', envOptional('MAJ_WP_API_KEY'))
  : env('MAJ_WP_API_KEY', 'dev-maj-wp-key');

const extensionSecret = isProduction
  ? requireProduction('EXTENSION_SECRET', envOptional('EXTENSION_SECRET'))
  : env('EXTENSION_SECRET', 'dev-extension-secret');

const adminUsername = env('ADMIN_USERNAME', isProduction ? undefined : 'admin') ?? 'admin';

const adminPasswordHash = envOptional('ADMIN_PASSWORD_HASH');
const adminPasswordPlain = envOptional('ADMIN_PASSWORD');

if (isProduction) {
  if (!adminPasswordHash) {
    throw new Error('[production] ADMIN_PASSWORD_HASH is required. Use: npm run hash-password');
  }
  if (adminPasswordPlain) {
    console.warn('[production] ADMIN_PASSWORD is ignored — use ADMIN_PASSWORD_HASH only.');
  }
} else if (!isTest && !adminPasswordHash && !adminPasswordPlain) {
  // dev fallback
  process.env.ADMIN_PASSWORD = 'admin';
}

const defaultCors = isProduction
  ? `${publicBaseUrl},chrome-extension://`
  : `http://localhost:3847,http://localhost:5173,chrome-extension://`;

export const config = {
  rootDir,
  port: parseInt(env('PORT', isProduction ? '3847' : '3847'), 10),
  host: env('HOST', isProduction ? '127.0.0.1' : '0.0.0.0'),
  nodeEnv,
  isProduction,
  isTest,
  isDev: !isProduction && !isTest,
  dataDir,
  databasePath: dbPath,
  publicBaseUrl: publicBaseUrl.replace(/\/$/, ''),
  sessionSecret,
  sessionMaxAgeMs: parseInt(env('SESSION_MAX_AGE_MS', String(7 * 24 * 60 * 60 * 1000)), 10),
  majWpApiKey,
  extensionSecret,
  adminUsername,
  adminPasswordHash: adminPasswordHash ?? null,
  adminPassword: isProduction ? null : (envOptional('ADMIN_PASSWORD') ?? (isTest ? undefined : 'admin')),
  corsOrigins: (envOptional('CORS_ORIGINS') ?? defaultCors).split(',').map((s) => s.trim()).filter(Boolean),
  trustProxy: envOptional('TRUST_PROXY') === 'true' || isProduction,
  instanceCount: parseInt(env('INSTANCE_COUNT', '1'), 10),
  claimLeaseSeconds: parseInt(env('CLAIM_LEASE_SECONDS', '300'), 10),
  maxPayloadBytes: parseInt(env('MAX_PAYLOAD_BYTES', '1048576'), 10),
  gmail: {
    clientId: envOptional('GMAIL_CLIENT_ID'),
    clientSecret: envOptional('GMAIL_CLIENT_SECRET'),
    redirectUri:
      envOptional('GMAIL_REDIRECT_URI') ??
      `${publicBaseUrl.replace(/\/$/, '')}/api/gmail/callback`,
    pollIntervalMs: parseInt(env('GMAIL_POLL_INTERVAL_MS', '300000'), 10),
    searchQuery: env('GMAIL_SEARCH_QUERY', 'is:unread -label:TicketGate/Processed'),
    enabled(): boolean {
      return Boolean(this.clientId && this.clientSecret);
    },
  },
  monday: {
    apiToken: envOptional('MONDAY_API_TOKEN'),
    boardIds: (envOptional('MONDAY_BOARD_IDS') ?? '')
      .split(',')
      .map((s) => s.trim())
      .filter(Boolean),
    pollIntervalMs: parseInt(env('MONDAY_POLL_INTERVAL_MS', '600000'), 10),
    enabled(): boolean {
      return Boolean(this.apiToken && this.boardIds.length > 0);
    },
  },
};

/** Validate config at startup — fail closed in production. */
export function validateStartupConfig(): void {
  if (config.instanceCount !== 1) {
    console.warn(
      `[warn] INSTANCE_COUNT=${config.instanceCount} — V1 supports only 1 instance (SQLite + pollers).`,
    );
  }
  if (isProduction) {
    console.log(`[production] PUBLIC_BASE_URL=${config.publicBaseUrl}`);
    console.log(`[production] DATA_DIR=${config.dataDir}`);
    console.log(`[production] DATABASE=${config.databasePath}`);
    console.log(`[production] Gmail=${config.gmail.enabled() ? 'enabled' : 'disabled'}`);
    console.log(`[production] Monday=${config.monday.enabled() ? 'enabled' : 'disabled'}`);
  }
}
