const axios = require('axios');
const crypto = require('crypto');
const fs = require('fs');
const path = require('path');
const { createCanvas, loadImage } = require('canvas');
const { HfInference } = require("@huggingface/inference");

require("dotenv").config()

// Cache directory to store generated images
const CACHE_DIR = `/home/root/code/public/image_out`


if (!fs.existsSync(CACHE_DIR)) {
  fs.mkdirSync(CACHE_DIR);
}
const available_models = ["alimama-creative/FLUX.1-Turbo-Alpha", "black-forest-labs/FLUX.1-dev", "black-forest-labs/FLUX.1-schnell", "CompVis/stable-diffusion-v1-4", "Corcelio/mobius", "digiplay/AnalogMadness-realistic-model-v7", "digiplay/AsianBrmBeautyrealmix_v2.0", "digiplay/DonutHoleMix_Beta", "digiplay/KawaiiRealisticAsian_v0.7", "digiplay/m0nst3rfy3-testfix", "digiplay/m3u", "digiplay/MilkyWonderland_v1", "digiplay/Realisian_v6", "digiplay/realmixUnrealjourney_v1", "digiplay/STRANGER", "digiplay/STRANGER-ANIME", "dreamlike-art/dreamlike-diffusion-1.0", "dreamlike-art/dreamlike-photoreal-2.0", "fluently/Fluently-XL-v2", "GraydientPlatformAPI/yamers-nsfw4-xl", "John6666/3x3x3mixxl-v2-sdxl", "John6666/4th-tail-merges-050tponynai-sdxl", "John6666/4th-tail-merges-050wai70-sdxl", "John6666/big-lust-v1-sdxl", "John6666/carnival-unchained-v10-fp8-flux", "John6666/convtest", "John6666/convtest2", "John6666/dgs-4th-darkness-03ad-sdxl", "John6666/digital-af-xlp-v1-sdxl", "John6666/josei-realistic-v11-sdxl", "John6666/lyh-anime-flux-v2a1-fp8-flux", "John6666/mala-anime-mix-nsfw-pony-xl-v5-sdxl-spo", "John6666/mala-anime-mix-nsfw-pony-xl-v5new-sdxl", "John6666/mala-anime-mix-nsfw-pony-xl-v5new-sdxl-spo", "John6666/mala-smooth-v1-sdxl", "John6666/mikoshi-pony-v1-sdxl", "John6666/mklan-aio-nsfw-aio-nextgen-xlv2-sdxl", "John6666/nova-reality-v50-sdxl", "John6666/optimal-criminal-pony-v10-sdxl", "John6666/photo-realistic-pony-v5-sdxl", "John6666/pornworks-real-porn-v03-sdxl", "John6666/pornworks-real-porn-v04-sdxl", "John6666/real-mix-pony-v01-sdxl", "John6666/real-mix-pony-v2-sdxl", "John6666/reasianpony-merge-v10-sdxl", "John6666/relh-checkpoint-v30-sdxl", "John6666/sakuramoon-v10-sdxl", "John6666/sapianf-nude-men-women-for-flux-v20fp16-fp8-flux", "John6666/sorkh-pony-v1-sdxl", "John6666/speciosa-anime-v14-sdxl", "John6666/st2-bedamnrealpony-v1-sdxl", "John6666/suimix-xl-v10-sdxl", "John6666/sumeshi-flux1s-v002e-fp8-flux", "John6666/titania-juggernaut-mix-v1-sdxl", "John6666/titania-mix-realistic-pony-gbv10-sdxl", "John6666/uber-realistic-porn-merge-xl-urpmxl-v6final-sdxl", "John6666/ultimate-realistic-mix-v1-sdxl", "John6666/unlimited-porn-x-sdxl", "John6666/unlimited-porn-xreal-sdxl", "John6666/wai-ani-hentai-pony-v5-sdxl", "John6666/wai-c-v3-sdxl", "John6666/wai-doll-cn-v2-sdxl", "John6666/wai-simpleuse-for-real-pony-v1-sdxl", "Niggendar/duchaitenPonyXLNo_v35", "sd-community/sdxl-flash", "stabilityai/stable-diffusion-2-1", "stabilityai/stable-diffusion-2-base", "stabilityai/stable-diffusion-3-medium-diffusers", "stabilityai/stable-diffusion-xl-base-1.0", "stable-diffusion-v1-5/stable-diffusion-v1-5", "stablediffusionapi/disney-pixar-cartoon", "stablediffusionapi/dreamshaper-v6", "stablediffusionapi/mklan-xxx-nsfw-pony", "stablediffusionapi/newrealityxl-global-nsfw", "UnfilteredAI/NSFW-gen-v2", "Yntec/3DKX", "Yntec/BetterPonyDiffusion", "Yntec/Chip_n_DallE", "Yntec/DramaLlama", "Yntec/DreamlikeShaper", "Yntec/DreamPhotoGASM", "Yntec/DreamWorld", "Yntec/DucHaitenGODofSIMP", "Yntec/ElldrethSDaydreamMix", "Yntec/epiCPhotoGasm", "Yntec/MeinaAlter", "Yntec/MGM", "Yntec/MostClassical", "Yntec/nuipenimix2", "Yntec/Ponygraphy", "Yntec/RevAnimatedV2Rebirth", "Yntec/SCMIX_NightSkyMeina", "Yntec/TrueSight", "Yntec/WoopWoopAnime", "Yntec/YiffyMix", "Yntec/ZootVisionEpsilon"]

// Hugging Face Inference API configuration
const HF_API_URL = 'https://api-inference.huggingface.co/models/';
const HF_API_TOKEN = process.env.HF_API_TOKEN || ''; // Replace with your actual API token
const inferenceClient = new HfInference(HF_API_TOKEN);


const DEFAULT_USER_API_KEY = 'PUBLIC'; // Default API key for demonstration purposes
//const DEFAULT_GUIDANCE_SCALE = 5.0; // Default guidance scale for the diffusion model
const DEFAULT_WIDTH = 1024; // Default width of the generated image
const DEFAULT_HEIGHT = 1024; // Default height of the generated image
// In-memory store for user credits (for demonstration purposes)
// In production, consider using a database
const userCredits = {
  'PUBLIC': 20000,
  'user_api_key_1': 100, // Example API key with 10 credits
  'user_api_key_2': 5   // Example API key with 5 credits
};

const generateImage = async(requestPayload, res, responseFormat) => {
  const prompt = requestPayload.prompt;
  const seed = requestPayload.seed ? parseInt(requestPayload.seed) : parseInt(Math.random() * 100000);
  const modelId = requestPayload.model || 'black-forest-labs/FLUX.1-schnell';
  const apiKey = requestPayload.api_key || DEFAULT_USER_API_KEY;
  const cfg_scale = requestPayload.guidance_scale ? parseFloat(requestPayload.guidance_scale) : 7.0;
  const steps = requestPayload.steps ? parseInt(requestPayload.steps) : 4;
  const width = requestPayload.width ? parseInt(requestPayload.width) : DEFAULT_WIDTH;
  const height = requestPayload.height ? parseInt(requestPayload.height) : DEFAULT_HEIGHT;

  if (!prompt) {
    return res.status(400).send('Error: No prompt provided');
  }

  if (!apiKey) {
    return res.status(400).send('Error: No API key provided');
  }

  // Check if the API key is valid and if the user has enough credits
  if (!userCredits.hasOwnProperty(apiKey)) {
    return res.status(403).send('Error: Invalid API key');
  }

  if (userCredits[apiKey] <= 0) {
    return res.status(403).send('Error: Insufficient credits');
  }

  // Set the seed if provided (for caching purposes, does affect HF API)
  const seedStr =  `_${seed}`

  // Create a hash of the prompt, seed, and modelId to use as the cache key
  const cacheKey = crypto.createHash('sha256').update(`${prompt}${seedStr}_${modelId}`).digest('hex');
  const cachePath = path.join(CACHE_DIR, `${cacheKey}.png`);
  console.log(cachePath)
  // Check if the image already exists in the cache
  if (fs.existsSync(cachePath)) {
    console.log("Found in cache! "+cacheKey)
    return res.sendFile(cachePath);
  }

  try {
    // Prepare payload for Hugging Face Inference API
    const payload = ({
        "inputs": prompt,
        "parameters": {"width": width, "height": height},
        "seed": seed
        //seed,
        //cfg_scale,
        //steps
    });
    console.log("Payload: ", JSON.stringify(payload, null, 2));
    console.log("Endpoint: ", `${HF_API_URL}${modelId}`);
    // Send request to Hugging Face Inference API to generate the image
    const response = await axios.post(
      `${HF_API_URL}${modelId}`,
      payload,
      {
        headers: {
          'Content-Type': 'application/json',
          authorization: `Bearer ${HF_API_TOKEN}`
        },
        responseType: 'arraybuffer'
      }
    );

    if (response.status !== 200) {
      return res.status(500).send(`Error: Failed to generate image, see response fuckup here: ${JSON.stringify(response)}`);  
    }

    // Deduct one credit from the user's balance
    userCredits[apiKey] -= 1;

    // Save the image to the cache directory
    fs.writeFileSync(cachePath, response.data);

    // Send either the image directly or a link to the image
    if (responseFormat == "url") {
      res.setHeader("Content-Type", "application/json")
      res.json({"image_url": "https://defactofficial-mmapi.hf.space/image_out/"+ cachePath.split("/").at(-1)})
      res.end()
    } else {
      res.setHeader('Content-Type', 'application/octet-stream');
      res.sendFile(cachePath);
      res.end()
    }
  } catch (error) {
    res.status(500).send(`Error: Failed to generate image - ${error.message}`);
  }

}
/*
app.get('/models', async(req, res) =>{
  res.send(available_models)
})

//for prompt-in-url: <img src="https://yourserver.com/generate/image?prompt=A%20large%20hamster&width=1024&height=1024"
app.get('/generate/image', async (req, res) => {
  await generateImage(req.query, res)
});

app.post("/generate", async(req, res)=> {
  await generateImage(req.body, res)
}) */
module.exports = {generateImage}