const express = require("express");
const session = require("express-session");
const bcrypt = require("bcrypt");
const crypto = require("crypto");
const nodemailer = require("nodemailer");
const { Pool } = require("pg");
const path = require("path");
const moment = require("moment");
const { marked } = require("marked");
const axios = require("axios");
require("dotenv").config();
const log = require("node-file-logger");
const QRCode = require("qrcode");
const fs = require('fs/promises');

const app = express();
const port = process.env.PORT;
const DATA_DIR = path.resolve('./data/layouts');

const TeleBot = require("telebot");
const bot = new TeleBot(process.env.TELEBOT);
telebotChatID = process.env.TELECHAT;

bot.on("text", (msg) => {
  if (msg.from.id == telebotChatID) {
    msg.reply.text(msg.text);
  } else {
    msg.reply.text(
      "Entschuldige, " +
        msg.from.username +
        "\nIch darf nicht mit fremden reden."
    );
    log.Info("Telebot-nachricht: " + msg.from.username + " - " + msg.text);
  }
});

//bot.start();

const options = {
  folderPath: "./logs/",
  dateBasedFileNaming: true,
  fileNamePrefix: "DailyLogs_",
  fileNameExtension: ".log",
  dateFormat: "YYYY_MM_DD",
  timeFormat: "h:mm:ss A",
};
log.SetUserOptions(options);

// Middleware
app.use(express.static(path.join(__dirname, "public")));
app.use(express.json());
app.set("view engine", "ejs");
app.use(express.urlencoded({ extended: false }));

// Session-Konfiguration
app.use(
  session({
    secret: process.env.SESSIONSECRET,
    resave: false,
    saveUninitialized: true,
    cookie: { maxAge: 1000 * 60 * 60 * 24 * 2 },
  })
);

// Authentifizierungs-Middleware
const requireAuth = (req, res, next) => {
  if (!req.session.userId) {
    return res.redirect("/login");
  }
  next();
};

const requireAdmin = (req, res, next) => {
  if (req.session.role !== "admin") {
    return res.status(403).send("Access denied");
  }
  next();
};

// Email-Konfiguration
const transporter = nodemailer.createTransport({
  host: process.env.MAILHOST,
  port: 465,
  secure: true,
  auth: {
    user: process.env.MAILUSER,
    pass: process.env.MAILPASS,
  },
});

// Datenbankverbindung
const pool = new Pool({
  connectionString: process.env.DATABASE_URL,
});

// CORS für Hallenplaner-API
const ALLOWED_ORIGINS = ['https://halle.tkdduisburg.de'];
app.use('/api/layouts', (req, res, next) => {
  const origin = req.headers.origin;
  if (ALLOWED_ORIGINS.includes(origin)) {
    res.setHeader('Access-Control-Allow-Origin', origin);
    res.setHeader('Access-Control-Allow-Methods', 'GET, POST');
    res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
  }
  if (req.method === 'OPTIONS') {
    return res.sendStatus(204);
  }
  next();
});

// Api für Hallenplaner
app.get('/api/layouts', async (req, res) => {
  try {
    const files = await fs.readdir(DATA_DIR);
    console.log(files);
	  const names = files
      .filter(f => f.endsWith('.json'))
      .map(f => f.replace('.json', ''));

    res.json(names);
  } catch (err) {
    res.status(500).json({ error: 'Layouts konnten nicht geladen werden' });
  }
});

// GET Einzelnes
app.get('/api/layouts/:name', async (req, res) => {
  try {
    const file = path.join(DATA_DIR, `${req.params.name}.json`);
    const data = await fs.readFile(file, 'utf8');
    res.json(JSON.parse(data));
  } catch {
    res.status(404).json({ error: 'Layout nicht gefunden' });
  }
});

// ✅ Layout speichern / überschreiben
app.post('/api/layouts/:name', async (req, res) => {
  try {
    const safeName = req.params.name.replace(/[^a-zA-Z0-9-_]/g, '');
const file = path.join(DATA_DIR, `${safeName}.json`);
    await fs.writeFile(file, JSON.stringify(req.body, null, 2));
    res.json({ status: 'ok' });
  } catch (err) {
    res.status(500).json({ error: 'Speichern fehlgeschlagen' });
  }
});

// Registrierung
app.post("/register", async (req, res) => {
  const { username, email, password } = req.body;
  try {
    const hashedPassword = await bcrypt.hash(password, 10);
    await pool.query(
      "INSERT INTO users (username, email, password) VALUES ($1, $2, $3)",
      [username, email, hashedPassword]
    );
    const message =
      "Registrierung erfolgreich. Ein Admin wird dich in kürze freischalten";
    const mailOptions = {
      to: "admin@boergmann.it",
      from: "admin@boergmann.it",
      subject: "[Home] Neue Registrierung",
      text: `Für home.boergmann.it hat sich ${username} registriert und wartet auf Freischaltung`,
    };

    transporter.sendMail(mailOptions, (error) => {
      if (error) {
        console.error("Error sending email:", error);
        const message = "Error sending Mail:" + error;
        res.render("error", { session: req.session, message });
      }
    });

    res.render("error", { session: req.session, message });
  } catch (error) {
    console.error("Error registering user:", error);
    const message = "Error registering user:" + error;
    res.render("error", { session: req.session, message });
  }
});

// Login
app.post("/login", async (req, res) => {
  const { username, password } = req.body;

  try {
    const userResult = await pool.query(
      "SELECT *, CASE WHEN admin_temp IS NOT NULL AND (now() - admin_temp) > interval '22 hours' THEN 'expired' ELSE 'valid' END AS admin_status FROM users WHERE username = $1",
      [username]
    );
    if (userResult.rows.length > 0) {
      const user = userResult.rows[0];
      const match = await bcrypt.compare(password, user.password);
      if (match) {
        if (user.is_active) {
          req.session.userId = user.id;
          req.session.userName = user.username;
          req.session.activeRiege = 1;
          req.session.activeTab = "geraete";
          req.session.message = [(title = ""), (message = ""), (type = "none")];
          if (user.admin_status === "expired") {
            await pool.query(
              "UPDATE users SET role = $1, admin_temp = NULL WHERE id = $2",
              ["user", user.id]
            );
            req.session.role = "user";
          } else {
            req.session.role = user.role;
          }
          res.redirect("/");
        } else {
          res.redirect("/freischaltung");
        }
      } else {
        const message = "Falscher Benutzername oder falsches Passwort";
        res.render("login", { session: req.session, username, message });
      }
    } else {
      const message = "Falscher Benutzername oder falsches Passwort";
      res.render("login", { session: req.session, username, message });
    }
  } catch (error) {
    console.error("Error logging in:", error);
    const message = "Error logging in:" + error;
    res.render("error", { session: req.session, message });
  }
});

//Wird angezeigt, wenn ein nicht freigeschalteter User sich anmelden will.
app.get("/freischaltung", async (req, res) => {
  res.render("freischaltung", { session: req.session });
});

// Logout
app.get("/logout", (req, res) => {
  req.session.destroy((err) => {
    if (err) {
      return res.status(500).send("Internal Server Error");
    }
    res.redirect("/");
  });
});

// Benutzer freischalten (nur Admin)
app.post("/userrights", requireAuth, requireAdmin, async (req, res) => {
  const { userId, type } = req.body;
  try {
    if (type === "activate") {
      await pool.query("UPDATE users SET is_active = TRUE WHERE id = $1", [
        userId,
      ]);
      const userResult = await pool.query("SELECT * FROM users WHERE id = $1", [
        userId,
      ]);
      if (userResult.rows.length > 0) {
        if (userResult.rows[0].email) {
          const mailOptions = {
            to: userResult.rows[0].email,
            from: "admin@boergmann.it",
            subject: "Freischaltung",
            text: `Hallo ${userResult.rows[0].username}, du wurdest soeben freigeschaltet.`,
          };

          transporter.sendMail(mailOptions, (error) => {
            if (error) {
              console.error("Error sending email:", error);
              const message = "Error sending Mail:" + error;
              res.render("error", { session: req.session, message });
            }
          });
        }
      }
    } else if (type === "admin") {
      await pool.query("UPDATE users SET role = $1 WHERE id = $2", [
        "admin",
        userId,
      ]);
    } else if (type === "admint") {
      await pool.query(
        "UPDATE users SET role = $1, admin_temp = $2 WHERE id = $3",
        ["admin", moment().toDate(), userId]
      );
    } else if (type === "delete") {
      await pool.query("DELETE FROM users WHERE id = $1", [userId]);
    }
    res.redirect("/admin");
  } catch (error) {
    console.error("Error activating user:", error);
    const message = "Error activating user:" + error;
    res.render("error", { session: req.session, message });
  }
});

// Passwort-Zurücksetzung anfordern
app.post("/send-password", async (req, res) => {
  const { email } = req.body;
  try {
    const userResult = await pool.query(
      "SELECT * FROM users WHERE email = $1",
      [email]
    );
    if (userResult.rows.length > 0) {
      const user = userResult.rows[0];
      const token = crypto.randomBytes(20).toString("hex");
      const resetLink = `http://tkd.boergmann.it/reset-password/${token}`;
      await pool.query(
        "UPDATE users SET reset_password_token = $1, reset_password_expires = $2 WHERE id = $3",
        [token, (selectedDate = moment().add(1, "d").toDate()), user.id]
      );

      const mailOptions = {
        to: user.email,
        from: "admin@boergmann.it",
        subject: "Password Reset",
        text: `Click the following link to reset your password: ${resetLink}`,
      };

      transporter.sendMail(mailOptions, (err) => {
        if (err) {
          console.error("Error sending email:", err);
          const message = "Error sending Mail:" + error;
          res.render("error", { session: req.session, message });
        } else {
          const message = "Password reset link sent";
          res.render("error", { session: req.session, message });
        }
      });
    } else {
      const message = "Email not found";
      res.render("error", { session: req.session, message });
    }
  } catch (error) {
    console.error("Error in forgot-password:", error);
    const message = "Error in forgot-password";
    res.render("error", { session: req.session, message });
  }
});

app.get("/forgot-password", async (req, res) => {
  res.render("forgot-password", { session: req.session });
});

// Passwort zurücksetzen
app.get("/reset-password/:token", async (req, res) => {
  const { token } = req.params;
  try {
    const userResult = await pool.query(
      "SELECT * FROM users WHERE reset_password_token = $1 AND reset_password_expires > $2",
      [token, Date.now()]
    );
    if (userResult.rows.length > 0) {
      res.render("reset-password", { token }); // Stelle sicher, dass es eine reset-password.ejs gibt
    } else {
      const message = "Token ungültig oder abgelaufen";
      res.render("error", { session: req.session, message });
    }
  } catch (error) {
    console.error("Error in reset-password:", error);
    const message = "Error in reset-password";
    res.render("error", { session: req.session, message });
  }
});

app.post("/reset-password/:token", async (req, res) => {
  const { token } = req.params;
  const { password } = req.body;
  try {
    const userResult = await pool.query(
      "SELECT * FROM users WHERE reset_password_token = $1 AND reset_password_expires > $2",
      [token, Date.now()]
    );
    if (userResult.rows.length > 0) {
      const user = userResult.rows[0];
      const hashedPassword = await bcrypt.hash(password, 10);
      await pool.query(
        "UPDATE users SET password = $1, reset_password_token = NULL, reset_password_expires = NULL WHERE id = $2",
        [hashedPassword, user.id]
      );
      res.redirect("/login");
    } else {
      const message = "Token ungültig oder abgelaufen";
      res.render("error", { session: req.session, message });
    }
  } catch (error) {
    console.error("Error in reset-password:", error);
    const message = "Error in reset-password";
    res.render("error", { session: req.session, message });
  }
});

// Profilseite
app.get("/profile", requireAuth, (req, res) => {
  res.render("profile", { session: req.session }); // Stelle sicher, dass es eine profile.ejs gibt
});

app.post("/profile", requireAuth, async (req, res) => {
  const { email, password } = req.body;
  try {
    if (email) {
      await pool.query("UPDATE users SET email = $1 WHERE id = $2", [
        email,
        req.session.userId,
      ]);
    }
    if (password) {
      const hashedPassword = await bcrypt.hash(password, 10);
      await pool.query("UPDATE users SET password = $1 WHERE id = $2", [
        hashedPassword,
        req.session.userId,
      ]);
    }
    res.redirect("/profile");
  } catch (error) {
    console.error("Error updating profile:", error);
    const message = "Error updating profile:" + error;
    res.render("error", { session: req.session, message });
  }
});

// Route für die Projektliste
app.get("/projects", async (req, res) => {
  req.session.message = ["", "", "none"];
  try {
    const result = await pool.query("SELECT * FROM projects");
    const projekte = result.rows;
    res.render("projects", { projekte, session: req.session });
  } catch (err) {
    console.error(err);
    res.send(err);
  }
});

app.post("/projects", async (req, res) => {
  const { name, beschreibung } = req.body;
  log.Info("projects loaded");
  req.session.message = ["", "", "none"];
  try {
    await pool.query(
      "INSERT INTO projects (name, beschreibung) VALUES ($1, $2)",
      [name, beschreibung]
    );
  } catch (err) {
    console.error(err);
    res.send(err);
  }
  try {
    const result = await pool.query(
      "SELECT id, name, beschreibung FROM projects"
    );
    const projekte = result.rows;
    res.render("projects", { projekte, session: req.session });
  } catch (err) {
    console.error(err);
    res.send(err);
  }
});

app.get("/project/:id", async (req, res) => {
  req.session.message = ["", "", "none"];
  try {
    const { id } = req.params;
	log.Info(id + " loaded");
    const result = await pool.query("SELECT * FROM projects WHERE id = $1", [
      id,
    ]);
    const projekt = result.rows[0];
    var htmlContent = projekt.beschreibung;
    try {
      const url = `${projekt.repository_link}/raw/branch/main/README.md`;
      // Abrufen der Datei
      const response = await axios.get(url);
      // Markdown-Inhalt in HTML umwandeln
      htmlContent = marked(response.data);
      //const htmlContent = "Testdata";
      if (!projekt) {
        return res.status(404).send("Projekt nicht gefunden");
      }
    } catch (err) {
      console.error(err);
    }

    res.render("project", {
      projekt,
      session: req.session,
      content: htmlContent,
    });
  } catch (err) {
    console.error(err);
  }
});

app.post("/projectedit", requireAuth, async (req, res) => {
  req.session.message = ["", "", "none"];
  const { id, name, kurzbeschreibung, beschreibung, repository_link } =
    req.body;
  try {
    await pool.query(
      "UPDATE projects SET name = $1, kurzbeschreibung = $2, beschreibung = $3, repository_link =$4 WHERE id = $5",
      [name, kurzbeschreibung, beschreibung, repository_link, id]
    );
  } catch (err) {
    console.error(err);
    res.send(err);
  }
  res.redirect("/project/" + id);
});

// Admin-Seite
app.get("/admin", requireAuth, requireAdmin, async (req, res) => {
  const usersResult = await pool.query("SELECT * FROM users");
  res.render("admin", { users: usersResult.rows, session: req.session }); // Stelle sicher, dass es eine admin.ejs gibt
});

// Login und Registrierung anzeigen
app.get("/login", (req, res) => {
  req.session.message = ["", "", "none"];
  res.render("login", { session: req.session }); // Stelle sicher, dass es eine login.ejs gibt
});

// Registrierung
app.get("/register", (req, res) => {
  res.render("register", { session: req.session }); // Stelle sicher, dass es eine register.ejs gibt
});

app.get("/contact", (req, res) => {
  res.render("contact", { session: req.session });
});

app.get("/car", (req, res) => {
  res.render("car", { session: req.session });
});

app.post("/car", (req, res) => {
  const { kennzeichen, message, contact } = req.body;
  let formattedLicensePlate = kennzeichen
    .replace(/[^a-zA-Z0-9]/g, "")
    .toUpperCase();

  if (formattedLicensePlate == "DUKL445") {
    if (contact != "") {
      bot.sendMessage(telebotChatID, contact);
    }
    bot.sendMessage(telebotChatID, message);
    console.log(message + contact);
    req.session.message = ["Success", "Nachricht versendet.", "none"];
  } else {
    log.Info(
      "Kontakt: " +
        contact +
        " - Nachricht: " +
        message +
        " - Kennzeichen: " +
        kennzeichen
    );
  }
  res.render("carsend", { session: req.session });
});

app.get("/qr", requireAuth, (req, res) => {
  res.render("qrcode", { session: req.session });
});

app.post("/qr", requireAuth, (req, res) => {
  const { text, invert } = req.body;

  const options = {
    color: {
      dark: invert ? "#FFFFFF" : "#000000", // Farbe der QR-Code-Muster
      light: invert ? "#000000" : "#FFFFFF", // Farbe des Hintergrunds
    },
  };

  QRCode.toDataURL(text, options, (err, url) => {
    if (err) {
      return res.send("Fehler beim Generieren des QR-Codes");
    }

    res.render("qrcodeshow", {
      session: req.session,
      qrCodeUrl: url,
    });
  });
});

// Startseite
app.get("/", (req, res) => {
  req.session.message = ["", "", "none"];
  res.render("index", { session: req.session });
});

//Datenschutz und Impressum
app.get("/impressum", (req, res) => {
  req.session.message = ["", "", "none"];
  res.render("impressum", { session: req.session });
});

const server = app.listen(port, "0.0.0.0", () => {
  log.Info(`Server is running on ${process.env.HOST}:${port}/`);
});
