import { promises as fs } from 'fs';
import path from 'path';
import { randomUUID } from 'crypto';

const ENV_PATH = path.resolve(process.cwd(), '.env');
const ENV_EXAMPLE_PATH = path.resolve(process.cwd(), '.env.example');

async function exists(p: string): Promise<boolean> {
  try {
    await fs.access(p);
    return true;
  } catch {
    return false;
  }
}

async function readEnvText(): Promise<string> {
  if (await exists(ENV_PATH)) {
    return fs.readFile(ENV_PATH, 'utf8');
  }
  if (await exists(ENV_EXAMPLE_PATH)) {
    return fs.readFile(ENV_EXAMPLE_PATH, 'utf8');
  }
  return '';
}

function unquote(raw: string): string {
  const trimmed = raw.trim();
  if (
    (trimmed.startsWith('"') && trimmed.endsWith('"')) ||
    (trimmed.startsWith("'") && trimmed.endsWith("'"))
  ) {
    return trimmed.slice(1, -1);
  }
  return trimmed;
}

export async function readEnvFile(): Promise<Record<string, string>> {
  const text = await readEnvText();
  const out: Record<string, string> = {};
  for (const rawLine of text.split(/\r?\n/)) {
    const line = rawLine.trim();
    if (!line || line.startsWith('#')) continue;
    const eq = line.indexOf('=');
    if (eq === -1) continue;
    const key = line.slice(0, eq).trim();
    if (!key) continue;
    out[key] = unquote(line.slice(eq + 1));
  }
  return out;
}

function isEnvLineForKey(line: string, key: string): boolean {
  const trimmed = line.trimStart();
  if (!trimmed || trimmed.startsWith('#')) return false;
  const eq = trimmed.indexOf('=');
  if (eq === -1) return false;
  return trimmed.slice(0, eq).trim() === key;
}

export async function setEnvVar(key: string, value: string): Promise<void> {
  if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) {
    throw new Error(`Invalid env var key: ${key}`);
  }
  if (/[\r\n]/.test(value)) {
    throw new Error('Env var value cannot contain newlines');
  }

  let text = '';
  if (await exists(ENV_PATH)) {
    text = await fs.readFile(ENV_PATH, 'utf8');
  } else if (await exists(ENV_EXAMPLE_PATH)) {
    text = await fs.readFile(ENV_EXAMPLE_PATH, 'utf8');
  }

  const newLine = `${key}=${value}`;
  const eol = text.includes('\r\n') ? '\r\n' : '\n';
  const hadTrailingNewline = text.length === 0 || text.endsWith('\n') || text.endsWith('\r\n');

  const lines = text.split(/\r?\n/);
  const endsBlank = lines.length > 0 && lines[lines.length - 1] === '';
  const body = endsBlank ? lines.slice(0, -1) : lines;

  let replaced = false;
  const updated = body.map(line => {
    if (!replaced && isEnvLineForKey(line, key)) {
      replaced = true;
      return newLine;
    }
    return line;
  });

  if (!replaced) {
    updated.push(newLine);
  }

  let out = updated.join(eol);
  if (hadTrailingNewline) out += eol;

  const tmp = path.join(path.dirname(ENV_PATH), `.env.tmp-${process.pid}-${Date.now()}-${randomUUID()}`);
  await fs.writeFile(tmp, out, { encoding: 'utf8', mode: 0o600 });
  await fs.rename(tmp, ENV_PATH);
}

export function envFilePath(): string {
  return ENV_PATH;
}

const RESTART_TRIGGER_PATH = path.resolve(process.cwd(), 'restart-trigger');

export function isDevRestartSupported(): boolean {
  return process.env.TSX_WATCH === '1' && process.env.NODE_ENV !== 'production';
}

export async function triggerDevRestart(): Promise<boolean> {
  if (!isDevRestartSupported()) return false;
  try {
    await fs.writeFile(RESTART_TRIGGER_PATH, `${Date.now()}\n`, { encoding: 'utf8' });
    return true;
  } catch {
    return false;
  }
}
