const crypto = require('crypto');
const { get, run } = require('./postgres');

const JWT_SECRET = process.env.JWT_SECRET || 'greenlens-dev-secret-change-in-prod';
const TOKEN_EXPIRY_SECONDS = 365 * 24 * 3600; // 1 year

// ─── Minimal JWT (HS256, no external deps) ─────────────────────────────────

const b64url = (input) => {
  const str = typeof input === 'string' ? input : JSON.stringify(input);
  return Buffer.from(str).toString('base64url');
};

const b64urlDecode = (str) => Buffer.from(str, 'base64url').toString();

const signJwt = (payload) => {
  const header = b64url({ alg: 'HS256', typ: 'JWT' });
  const body = b64url(payload);
  const sig = crypto.createHmac('sha256', JWT_SECRET).update(`${header}.${body}`).digest('base64url');
  return `${header}.${body}.${sig}`;
};

const verifyJwt = (token) => {
  if (!token || typeof token !== 'string') return null;
  const parts = token.split('.');
  if (parts.length !== 3) return null;
  const [header, body, sig] = parts;
  const expected = crypto.createHmac('sha256', JWT_SECRET).update(`${header}.${body}`).digest('base64url');
  if (sig !== expected) return null;
  try {
    const payload = JSON.parse(b64urlDecode(body));
    if (payload.exp && Math.floor(Date.now() / 1000) > payload.exp) return null;
    return payload;
  } catch {
    return null;
  }
};

const issueToken = (userId, email, name) =>
  signJwt({
    sub: userId,
    email,
    name,
    iat: Math.floor(Date.now() / 1000),
    exp: Math.floor(Date.now() / 1000) + TOKEN_EXPIRY_SECONDS,
  });

// ─── Password hashing ──────────────────────────────────────────────────────

const hashPassword = (password) =>
  crypto.createHmac('sha256', JWT_SECRET).update(password).digest('hex');

// ─── Schema ────────────────────────────────────────────────────────────────

const ensureAuthSchema = async (db) => {
  await run(
    db,
    `CREATE TABLE IF NOT EXISTS auth_users (
      id           TEXT PRIMARY KEY,
      email        TEXT NOT NULL UNIQUE,
      name         TEXT NOT NULL DEFAULT '',
      password_hash TEXT NOT NULL,
      created_at   TIMESTAMPTZ NOT NULL DEFAULT NOW()
    )`,
  );
};

// ─── Operations ───────────────────────────────────────────────────────────

const signUp = async (db, email, name, password) => {
  const normalizedEmail = email.trim().toLowerCase();
  const existing = await get(db, 'SELECT id FROM auth_users WHERE LOWER(email) = LOWER($1)', [normalizedEmail]);
  if (existing) {
    const err = new Error('Email already in use.');
    err.code = 'EMAIL_TAKEN';
    err.status = 409;
    throw err;
  }
  const id = `usr_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;
  await run(db, 'INSERT INTO auth_users (id, email, name, password_hash) VALUES ($1, $2, $3, $4)', [
    id,
    normalizedEmail,
    name.trim(),
    hashPassword(password),
  ]);
  return { id, email: normalizedEmail, name: name.trim() };
};

const login = async (db, email, password) => {
  const normalizedEmail = email.trim().toLowerCase();
  const user = await get(
    db,
    'SELECT id, email, name, password_hash FROM auth_users WHERE LOWER(email) = LOWER($1)',
    [normalizedEmail],
  );
  if (!user) {
    const err = new Error('No account found for this email.');
    err.code = 'USER_NOT_FOUND';
    err.status = 401;
    throw err;
  }
  if (user.password_hash !== hashPassword(password)) {
    const err = new Error('Wrong password.');
    err.code = 'WRONG_PASSWORD';
    err.status = 401;
    throw err;
  }
  return { id: user.id, email: user.email, name: user.name };
};

module.exports = { ensureAuthSchema, signUp, login, issueToken, verifyJwt };
