import express from "express";
//import dotenv from "dotenv";
import path from "path";
import { fileURLToPath } from "url";
import fs from "fs";
import { generateSitesJson, delFieldDir, addFieldDir } from "./generateSitesJson.js";

import bodyParser from "body-parser";
import { execFile } from "child_process";

import fse from "fs-extra";


import multer from 'multer';
import sharp from 'sharp';


let sites = generateSitesJson();
//sites.unshift({ dir: 'home', name: 'Home', icon: 'home.ico' });
//console.log(sites);

////dotenv.config();
//dotenv.config({ path: './.env' });
const app = express();
app.use(bodyParser.json());

// ricostruisci __dirname in ambiente ESM
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

// Root sicura per le cartelle dei siti: ./sites
const SITES_ROOT = path.resolve(__dirname,'home' , 'sites');
const HOME_DIR = path.resolve(__dirname, 'home');
const SITES_JSON = path.resolve(HOME_DIR, 'sites.json');
const DOWNLOAD_SITES = 'home/sites';

// Leggi variabili da .env
const HOST = process.env.HOST || "0.0.0.0";
const PORT = process.env.PORT || 3000;
const TYPE = process.env.TYPE || "http";
const URL = process.env.URL || "https://sites.patachina2.casacam.net";

/** crea la dir icons se non esiste **/
const iconsDir = path.join(__dirname, 'home', 'icons');
fs.mkdirSync(iconsDir, { recursive: true });


/** Util: carica il manifest con fallback **/
function readManifest(mPath) {
  try {
    const text = fs.readFileSync(mPath, 'utf8');
    return JSON.parse(text);
  } catch (e) {
    return {};
  }
}

/** Risolve un path del sito sotto SITES_ROOT, impedendo traversal. */
function resolveSitePathSafe(dir) {
  //const clean = normalizeDirField(dir);
  //const abs = path.resolve(SITES_ROOT, clean);
  const abs = path.resolve(SITES_ROOT, dir);
  const rootWithSep = SITES_ROOT.endsWith(path.sep) ? SITES_ROOT : SITES_ROOT + path.sep;
  if (!(abs === SITES_ROOT || abs.startsWith(rootWithSep))) {
    throw new Error('Percorso non consentito.');
  }
  return abs;
}

// -------------------- Utilities --------------------
async function readJsonSafe(file) {
  try {
    return await fse.readJson(file);
  } catch (err) {
    if (err.code === 'ENOENT') return [];
    throw err;
  }
}

async function writeJsonAtomic(file, data) {
  const tmp = file + '.tmp';
  await fse.writeJson(tmp, data, { spaces: 2 });
  await fse.move(tmp, file, { overwrite: true });
}

app.post('/del-site', async (req, res) => {
  try {
    let { dir } = req.body || {};
    //console.log("dir da cancellare: ",dir);
    const siteAbsPath = resolveSitePathSafe(dir);
    //console.log("path assoluto: ", siteAbsPath);
    if (await fse.pathExists(siteAbsPath)) {
      await fse.remove(siteAbsPath);
    }

    sites = delFieldDir(dir);
    //await deleteIcon("icons/composerize.ico");
    console.log(`la dir=${dir} è stata cancellata`);
    return res.json({
      ok: true,
//      removed: { name: removed?.name, dir: removed?.dir },
      deletedPath: siteAbsPath
    });
  } catch (err) {
    console.error('del-site error:', err);
    const msg = err?.message || 'Errore interno nella cancellazione.';
    return res.status(500).json({ error: msg });
  }
});


app.use((req, res, next) => {
  res.removeHeader("X-Frame-Options");
  res.setHeader("Content-Security-Policy", "frame-ancestors *");
  next();
});

// endpoint POST /add-site
app.post("/add-site", (req, res) => {
  const { url, dir } = req.body;
  const dir1 = `${DOWNLOAD_SITES}/${dir}`;

  // 👉 qui esegui lo script Python
  execFile("downloadsite.sh", [url, dir1], (error, stdout, stderr) => {
    if (error) {
      console.error("Errore:", error);
      return res.status(500).json({ status: "error", message: stderr });
    }
    sites = addFieldDir(dir);
    res.json({ status: "ok", output: stdout });
  });
});



//const SITES = [];
// Monta le cartelle statiche in base alla lista SITES
sites.forEach(site => {
  if (site) {
    // directory reale: sites/<site>
    const dirPath = path.join(SITES_ROOT, site.dir);

    // URL pubblico: /<site>
    app.use(`/${site.dir}`, express.static(dirPath));
  }
});
// Cartella public come root
//app.use("/", express.static("public"));
app.use("/", express.static("home", { index: "sidebar.html" }));
//app.use("/root", express.static("public"));
app.use("/home", express.static("home"));
app.use("/settings", express.static("home", { index: "settings.html" }));
app.use("/manifest", express.static("home", { index: "manifest-editor.html" }));
/*
app.use('/manifest', express.static(path.join(process.cwd(), 'home'), {
  index: 'manifest-editor.html',
  redirect: false // opzionale: evita 301 /manifest -> /manifest/
}));
*/

// Endpoint per esporre la config al client
app.get("/config.json", (req, res) => {
  res.json({
    host: HOST,
    port: PORT,
//    sites: SITES,
    s: sites,
    url: URL
  });
});

const upload1 = multer();

/** GET: manifest corrente **/
app.post('/api/manifest', upload1.none(), (req, res) => {
//app.get('/api/manifest', (req, res) => {
  try {
    //console.log(req.body.dir);
    const mydir = req.body.dir; 
    const manifest = readManifest(path.join(SITES_ROOT, mydir , 'manifest.json'));
    res.json(manifest);
  } catch (e) {
    res.status(500).json({ error: 'Impossibile leggere manifest.json' });
  }
});

/** PUT: salva il manifest (campi + icone) **/
app.put('/api/manifest', upload1.none(), (req, res) => {
  // Validazioni essenziali
  const mydir = req.body.dir;
  const incoming = JSON.parse(req.body.manifest);
  //console.log("manifest put");
  //console.log("incoming=",incoming);
  //console.log("mydir=",mydir);
  const manPath = path.join(SITES_ROOT, mydir , 'manifest.json');
  //console.log("manPath",manPath);
  if (!incoming.name || !incoming.short_name) {
    return res.status(400).json({ error: 'name e short_name sono obbligatori' });
  }
  //console.log("step1");
  if (!Array.isArray(incoming.icons)) incoming.icons = [];
  //console.log("step2");
  try {
    fs.writeFileSync(manPath, JSON.stringify(incoming, null, 2), 'utf8');
    console.log(`manifest di <${mydir}> salvato`);
    generateSitesJson();
    res.json({ ok: true });
  } catch (e) {
    console.error(e);
    res.status(500).json({ error: 'Salvataggio manifest fallito' });
  }
});

/** Multer: configurazione upload (in memoria) **/
const upload = multer({
  storage: multer.memoryStorage(),
  fileFilter: (req, file, cb) => {
    // Accetta i tipi più comuni
    const okTypes = ['image/png', 'image/jpeg', 'image/webp', 'image/jpg'];
    if (file && okTypes.includes(file.mimetype)) {
	cb(null, true);
    } else {
	cb(new Error(`Formato non supportato: ${file ? file.mimetype : 'Nessun file'}`));
    }
  },
  limits: { fileSize: 8 * 1024 * 1024 } // 8 MB
});

/** POST: upload icona + generazione 192 e 512 **/
app.post('/api/upload-icon', upload.single('icon'), async (req, res) => {
  try {
    // Log diagnostico utile
/*    console.log('Upload Content-Type:', req.headers['content-type']);
    console.log('Upload body keys:', Object.keys(req.body || {}));
    console.log('Upload file:', req.file && {
      fieldname: req.file.fieldname,
      mimetype: req.file.mimetype,
      size: req.file.size,
      originalname: req.file.originalname
    });
*/
    const mydir = req.body.mydir;
    //console.log("dir=",mydir);

    const iconsMyDir = path.join(SITES_ROOT,mydir , 'icons');
    fs.mkdirSync(iconsMyDir, { recursive: true });

    if (!req.file) {
      return res.status(400).json({
        error: 'Nessun file ricevuto nel campo "icon". Assicurati che l’input abbia name="icon" e che il JS usi FormData.append("icon", file).'
      });
    }

    const purpose = (req.body.purpose || 'any').trim();
    const baseNameSafe = (req.file.originalname || 'icon')
      .replace(/\.[^.]+$/, '')         // togli estensione
      .replace(/[^a-zA-Z0-9-_]/g, ''); // pulisci caratteri strani

    // Puoi aggiungere altre dimensioni se vuoi
    const sizes = [192, 512];
    const created = [];

    for (const size of sizes) {
      const filename = `${baseNameSafe}-${size}.png`;
      const outPath = path.join(path.join(SITES_ROOT, mydir,  'icons'), filename);

      await sharp(req.file.buffer)
        .resize(size, size, { fit: 'cover' })
        .png({ quality: 90 })
        .toFile(outPath);

      created.push({
        src: `/icons/${filename}`,
        sizes: `${size}x${size}`,
        type: 'image/png',
        purpose
      });
    }
    generateSitesJson();
    res.json({ ok: true, icons: created });
  } catch (e) {
    console.error('Errore /api/upload-icon:', e);
    res.status(500).json({ error: 'Elaborazione icona fallita' });
  }
});


/**
 * DELETE: rimuove icona dal manifest e opzionalmente elimina il file
 * query:
 *  - src (richiesto): percorso icona (es. /icons/icon-192.png)
 *  - removeFile=true|false (opzionale): se true elimina anche il file se sotto /public/icons
 */
app.delete('/api/icons', (req, res) => {
  const src = req.query.src;
  const removeFile = (req.query.removeFile || 'false') === 'true';
  const mydir = req.query.myDir;
  const manPath = path.join(SITES_ROOT, mydir , 'manifest.json');
  //console.log("delete icon:");
  //console.log("src=",src);
  //console.log("mydir=",mydir);
  if (!src) return res.status(400).json({ error: 'Parametro src mancante' });

  try {
    const manifest = readManifest(manPath);
    manifest.icons = (manifest.icons || []).filter(i => i.src !== src);

    if (removeFile) {
      const absFilePath = path.join(SITES_ROOT, mydir, src.replace(/^\//, ''));
      const isUnderIcons = absFilePath.startsWith(iconsDir + path.sep);
      if (isUnderIcons && fs.existsSync(absFilePath)) {
        fs.unlinkSync(absFilePath);
      }
    }

    fs.writeFileSync(manPath, JSON.stringify(manifest, null, 2), 'utf8');
    generateSitesJson();
    res.json({ ok: true, removedFromManifest: true, fileDeleted: removeFile });
  } catch (e) {
    console.error(e);
    res.status(500).json({ error: 'Eliminazione icona fallita' });
  }
});



app.listen(PORT, HOST, () => {
  console.log(`✅ Server pronto su http://${HOST}:${PORT}`);
});

