const { Client, GatewayIntentBits, AttachmentBuilder } = require('discord.js');
const { pipeline } = require('stream/promises');
const { createWriteStream, existsSync, mkdirSync, unlinkSync, readFileSync, appendFileSync } = require('fs');
const path = require('path');
const https = require('https');
const http = require('http');
const parseCrzy = require('./parseCrzy');
const parseEmbed = require('./parseEmbed');
const nameGen = require('./nameGen');
const runProcess = require('./processor');
const blacklist = require('./blacklist');

const config = parseCrzy(path.join(__dirname, 'config.crzy'));
const embedPath = p => path.join(__dirname, 'embeds', p);
const logPath = path.join(__dirname, 'debug.log');
const ADMINS = ['1459684115245830340', '1379043538955927685'];

const client = new Client({
  intents: [
    GatewayIntentBits.Guilds,
    GatewayIntentBits.GuildMessages,
    GatewayIntentBits.MessageContent
  ]
});

function download(url, dest) {
  return new Promise((resolve, reject) => {
    const mod = url.startsWith('https') ? https : http;
    mod.get(url, res => {
      if (res.statusCode !== 200) return reject(new Error(`HTTP ${res.statusCode}`));
      pipeline(res, createWriteStream(dest)).then(resolve).catch(reject);
    }).on('error', reject);
  });
}

function buildEmbed(file, name, vars) {
  const { embed, attachment: attachPath } = parseEmbed(embedPath(file), name, vars);
  const payload = { embeds: [embed] };
  if (attachPath && existsSync(attachPath)) payload.files = [new AttachmentBuilder(attachPath)];
  return payload;
}

function cleanup(...paths) {
  for (const p of paths) {
    try { if (existsSync(p)) unlinkSync(p); } catch {}
  }
}

function containsBlacklisted(filepath) {
  try { return /crzyinfo/i.test(readFileSync(filepath, 'utf-8')); } catch { return false; }
}

function logError(username, filename, content, error) {
  const entry = [
    `\n[${new Date().toISOString()}]`,
    `Username: ${username}`,
    `File: ${filename}`,
    `Content:\n${content}`,
    `Error:\n${error}`,
    '-'.repeat(60)
  ].join('\n');
  appendFileSync(logPath, entry + '\n');
}

function baseVars(msg, attachment) {
  return {
    input_filename: attachment.name,
    timeout_seconds: Math.floor(config.limits.timeout / 1000),
    version: config.meta.version
  };
}

client.on('messageCreate', async msg => {
  if (msg.author.bot) return;

  if (msg.content.startsWith('.unbl')) {
    if (!ADMINS.includes(msg.author.id)) return;
    const targetId = msg.content.split(/\s+/)[1];
    if (!targetId) return msg.reply('Provide a user ID.');
    const result = blacklist.unblacklist(targetId);
    if (!result) return msg.reply('User not found in blacklist.');
    return msg.reply(buildEmbed('unblacklist.embed', 'unblacklist', { target_username: result.username, version: config.meta.version }));
  }

  if (!msg.content.startsWith(config.bot.prefix)) return;

  if (blacklist.isBlacklisted(msg.author.id)) {
    return msg.reply(buildEmbed('blacklisted.embed', 'blacklisted', { version: config.meta.version }));
  }

  const attachment = msg.attachments.first();
if (!attachment || !(/\.(luau|lua|txt)$/i).test(attachment.name)) {
  return msg.reply('Attach a `.luau`, `.lua`, or `.txt` file.');
 }

  if (attachment.size > config.limits.max_file_size) {
    return msg.reply('File too large.');
  }

  const loadingMsg = await msg.reply(buildEmbed('loading.embed', 'loading', { version: config.meta.version }));

  const respond = async (file, name, vars) => {
    try { await loadingMsg.delete(); } catch {}
    return msg.reply(buildEmbed(file, name, vars));
  };

  const name = nameGen();
  const outputDir = config.paths.output_dir;

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

  const inputPath = path.join(config.paths.input_dir, `${name}.luau`);
  const outputPath = path.join(outputDir, `${name}.lua`);

  try {
    await download(attachment.url, inputPath);
  } catch (e) {
    logError(msg.author.username, attachment.name, 'Failed to download', e.message);
    return respond('error.embed', 'error', baseVars(msg, attachment));
  }

  const fileContent = readFileSync(inputPath, 'utf-8');
  const vars = baseVars(msg, attachment);

  const start = Date.now();
  const result = await runProcess(
    config.paths.lune,
    ['run', config.paths.main_script, inputPath, '-o', outputPath],
    config.limits.timeout
  );
  vars.elapsed_time = Date.now() - start;
  vars.output_path = outputPath;

  if (result.status === 'timeout') {
    logError(msg.author.username, attachment.name, fileContent, 'Process timed out');
    cleanup(inputPath, outputPath);
    return respond('timeout.embed', 'timeout', vars);
  }

  if (result.status === 'error') {
    logError(msg.author.username, attachment.name, fileContent, result.error);
    cleanup(inputPath, outputPath);
    return respond('error.embed', 'error', vars);
  }

  if (!existsSync(outputPath)) {
    logError(msg.author.username, attachment.name, fileContent, 'Output file was not generated');
    cleanup(inputPath);
    return respond('error.embed', 'error', vars);
  }

  if (containsBlacklisted(outputPath)) {
    const userData = blacklist.warn(msg.author.id, msg.author.username);
    logError(msg.author.username, attachment.name, fileContent, `Blacklisted content detected (warning ${userData.warnings}/2)`);
    cleanup(inputPath, outputPath);
    vars.warnings_left = 2 - userData.warnings;
    if (userData.blacklisted) {
      return respond('blacklisted.embed', 'blacklisted', vars);
    }
    return respond('warning.embed', 'warning', vars);
  }

  await respond('success.embed', 'success', vars);
  cleanup(inputPath, outputPath);
});

client.once('ready', () => console.log(`${client.user.tag} online`));
client.login(config.bot.token);