import { promises as fs } from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
import { randomUUID, randomBytes } from 'crypto';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const backendDir = path.resolve(__dirname, '..', '..');
const dataDir = path.join(backendDir, 'data');
const dataFile = path.join(dataDir, 'totp.json');

const DEFAULT_TOTP = {
  enabled: false,
  secret: '',
  otpauthUrl: '',
  issuer: 'Vault',
  label: 'vault',
  pendingSecret: '',
  pendingOtpauthUrl: '',
  pendingCreatedAt: null,
  sessions: [],
  createdAt: null,
  updatedAt: null
};

async function ensureStore() {
  await fs.mkdir(dataDir, { recursive: true });
  try {
    await fs.access(dataFile);
  } catch {
    await fs.writeFile(dataFile, JSON.stringify(DEFAULT_TOTP, null, 2), 'utf-8');
  }
}

export async function readTotpConfig() {
  await ensureStore();
  try {
    const raw = await fs.readFile(dataFile, 'utf-8');
    const parsed = JSON.parse(raw);
    return {
      ...DEFAULT_TOTP,
      ...parsed
    };
  } catch {
    return { ...DEFAULT_TOTP };
  }
}

export async function writeTotpConfig(patch) {
  const current = await readTotpConfig();
  const timestamp = new Date().toISOString();
  const next = {
    ...current,
    ...patch,
    updatedAt: timestamp
  };
  if (!current.createdAt) {
    next.createdAt = timestamp;
  }
  await fs.writeFile(dataFile, JSON.stringify(next, null, 2), 'utf-8');
  return next;
}

const normalizeSessions = (sessions) => {
  if (!Array.isArray(sessions)) {
    return [];
  }
  const now = Date.now();
  return sessions.filter((session) => {
    const expiresAt = new Date(session.expiresAt || 0).getTime();
    return Number.isFinite(expiresAt) && expiresAt > now;
  });
};

const withPrunedSessions = async () => {
  const config = await readTotpConfig();
  const pruned = normalizeSessions(config.sessions);
  if (pruned.length === (config.sessions || []).length) {
    return { config, sessions: pruned };
  }
  const updated = await writeTotpConfig({ sessions: pruned });
  return { config: updated, sessions: pruned };
};

const createToken = () => {
  const raw = randomBytes(24).toString('base64url');
  return `vault_${raw}`;
};

export async function createSession({ ttlDays = 30, label = 'device' } = {}) {
  const { sessions } = await withPrunedSessions();
  const now = Date.now();
  const ttlMs = Math.max(1, Number(ttlDays) || 30) * 24 * 60 * 60 * 1000;
  const expiresAt = new Date(now + ttlMs).toISOString();
  const session = {
    id: randomUUID(),
    token: createToken(),
    label,
    createdAt: new Date(now).toISOString(),
    expiresAt
  };
  const nextSessions = [...sessions, session];
  await writeTotpConfig({ sessions: nextSessions });
  return session;
}

export async function validateSession(token) {
  if (!token) {
    return { valid: false, session: null };
  }
  const { sessions } = await withPrunedSessions();
  const match = sessions.find((session) => session.token === token) || null;
  return { valid: Boolean(match), session: match };
}

export async function clearSessions() {
  const updated = await writeTotpConfig({ sessions: [] });
  return updated.sessions || [];
}

export async function setPendingTotp({ secret, otpauthUrl }) {
  const updated = await writeTotpConfig({
    pendingSecret: secret,
    pendingOtpauthUrl: otpauthUrl,
    pendingCreatedAt: new Date().toISOString()
  });
  return updated;
}

export async function clearPendingTotp() {
  const updated = await writeTotpConfig({
    pendingSecret: '',
    pendingOtpauthUrl: '',
    pendingCreatedAt: null
  });
  return updated;
}

export { DEFAULT_TOTP };
