import crypto from 'node:crypto';
import fs from 'node:fs/promises';
import path from 'node:path';
import { HttpError } from './errors.js';

const HOUR_LIMIT = 5;
const DAY_LIMIT = 10;
const GLOBAL_DAY_LIMIT = 100;

export async function createUploadLimiter({ dataDir }) {
  const filePath = path.join(path.resolve(dataDir), 'index', 'upload-limits.json');
  await fs.mkdir(path.dirname(filePath), { recursive: true });
  let state = await loadState(filePath);
  let writeChain = Promise.resolve();

  return {
    async checkAndConsume(ip) {
      const now = new Date();
      const day = now.toISOString().slice(0, 10);
      const hour = now.toISOString().slice(0, 13);
      const key = crypto.createHash('sha256').update(ip || 'unknown').digest('hex');

      if (state.day !== day) state = { day, global: 0, clients: {} };
      const client = state.clients[key] || { day, dayCount: 0, hour, hourCount: 0 };
      if (client.day !== day) {
        client.day = day;
        client.dayCount = 0;
      }
      if (client.hour !== hour) {
        client.hour = hour;
        client.hourCount = 0;
      }

      if (state.global >= GLOBAL_DAY_LIMIT) {
        throw new HttpError(429, 'Daily site upload budget reached. Try again tomorrow.');
      }
      if (client.hourCount >= HOUR_LIMIT) {
        throw new HttpError(429, 'Upload limit reached: 5 images per hour.');
      }
      if (client.dayCount >= DAY_LIMIT) {
        throw new HttpError(429, 'Upload limit reached: 10 images per day.');
      }

      state.global += 1;
      client.hourCount += 1;
      client.dayCount += 1;
      state.clients[key] = client;
      writeChain = writeChain.then(() => fs.writeFile(filePath, `${JSON.stringify(state, null, 2)}\n`));
      await writeChain;

      return {
        remainingHour: Math.max(0, HOUR_LIMIT - client.hourCount),
        remainingDay: Math.max(0, DAY_LIMIT - client.dayCount),
        remainingGlobalDay: Math.max(0, GLOBAL_DAY_LIMIT - state.global)
      };
    }
  };
}

async function loadState(filePath) {
  try {
    return JSON.parse(await fs.readFile(filePath, 'utf8'));
  } catch {
    return { day: new Date().toISOString().slice(0, 10), global: 0, clients: {} };
  }
}
