require("dotenv").config();

const express = require("express");
const session = require("express-session");
const helmet = require("helmet");
const fs = require("fs");
const path = require("path");
const expressLayouts = require("express-ejs-layouts");

// ✅ DB + Session Store
const db = require("./db");
const { getSessionStore } = require("./config/session");

// ✅ Setup Middleware + Setup Routes
const requireSetup = require("./middleware/requireSetup");
const setupRoutes = require("./routes/setup.routes");

// ✅ Routes (deine)
const adminRoutes = require("./routes/admin.routes");
const dashboardRoutes = require("./routes/dashboard.routes");
const patientRoutes = require("./routes/patient.routes");
const medicationRoutes = require("./routes/medications.routes");
const patientMedicationRoutes = require("./routes/patientMedication.routes");
const waitingRoomRoutes = require("./routes/waitingRoom.routes");
const serviceRoutes = require("./routes/service.routes");
const patientServiceRoutes = require("./routes/patientService.routes");
const invoiceRoutes = require("./routes/invoice.routes");
const patientFileRoutes = require("./routes/patientFile.routes");
const companySettingsRoutes = require("./routes/companySettings.routes");
const authRoutes = require("./routes/auth.routes");
const reportRoutes = require("./routes/report.routes");
const calendarRoutes = require("./routes/calendar.routes");

const app = express();

/* ===============================
   ✅ Seriennummer / Trial Konfiguration
================================ */
const TRIAL_DAYS = 30;

/* ===============================
   ✅ Seriennummer Helper Funktionen
================================ */
function normalizeSerial(input) {
  return (input || "")
    .toUpperCase()
    .replace(/[^A-Z0-9-]/g, "")
    .trim();
}

// Format: AAAAA-AAAAA-AAAAA-AAAAA
function isValidSerialFormat(serial) {
  return /^[A-Z0-9]{5}(-[A-Z0-9]{5}){3}$/.test(serial);
}

// Modulo-3 Check (Summe aller Zeichenwerte % 3 === 0)
function passesModulo3(serial) {
  const raw = serial.replace(/-/g, "");
  let sum = 0;

  for (const ch of raw) {
    if (/[0-9]/.test(ch)) sum += parseInt(ch, 10);
    else sum += ch.charCodeAt(0) - 55; // A=10
  }

  return sum % 3 === 0;
}

/* ===============================
   MIDDLEWARE
================================ */
app.use(express.urlencoded({ extended: true }));
app.use(express.json());

app.use(
  helmet({
    contentSecurityPolicy: {
      directives: {
        defaultSrc: ["'self'"],
        scriptSrc: ["'self'"],
        styleSrc: ["'self'", "'unsafe-inline'"],
        imgSrc: ["'self'", "data:"],
      },
    },
  }),
);

app.use(
  session({
    name: "praxis.sid",
    secret: process.env.SESSION_SECRET || "dev-secret",
    store: getSessionStore(),
    resave: false,
    saveUninitialized: false,
  }),
);

// ✅ i18n Middleware (SAFE)
app.use((req, res, next) => {
  try {
    const lang = req.session.lang || "de";
    const filePath = path.join(__dirname, "locales", `${lang}.json`);

    let data = {};
    if (fs.existsSync(filePath)) {
      data = JSON.parse(fs.readFileSync(filePath, "utf-8"));
    }

    res.locals.t = data;
    res.locals.lang = lang;
    next();
  } catch (err) {
    console.error("❌ i18n Fehler:", err.message);
    res.locals.t = {};
    res.locals.lang = "de";
    next();
  }
});

const flashMiddleware = require("./middleware/flash.middleware");
app.use(flashMiddleware);

app.use(express.static("public"));
app.use("/uploads", express.static("uploads"));

app.set("view engine", "ejs");
app.set("views", path.join(__dirname, "views"));
app.use(expressLayouts);
app.set("layout", "layout");

app.use((req, res, next) => {
  res.locals.user = req.session.user || null;
  next();
});

/* ===============================
   ✅ SETUP ROUTES + SETUP GATE
   WICHTIG: /setup zuerst mounten, danach requireSetup
================================ */
app.use("/setup", setupRoutes);
app.use(requireSetup);

/* ===============================
   ✅ LICENSE/TRIAL GATE
================================ */
app.use(async (req, res, next) => {
  try {
    // Setup muss erreichbar bleiben
    if (req.path.startsWith("/setup")) return next();

    // Login muss erreichbar bleiben
    if (req.path === "/" || req.path.startsWith("/login")) return next();

    // Serial Seiten müssen erreichbar bleiben
    if (req.path.startsWith("/serial-number")) return next();
    if (req.path.startsWith("/admin/serial-number")) return next();

    // Sprache ändern erlauben
    if (req.path.startsWith("/lang/")) return next();

    // Nicht eingeloggt -> auth regelt das
    if (!req.session?.user) return next();

    const [rowsSettings] = await db.promise().query(
      `SELECT id, serial_number, trial_started_at
       FROM company_settings
       ORDER BY id ASC
       LIMIT 1`,
    );

    const settings = rowsSettings?.[0];

    // ✅ Seriennummer vorhanden -> alles OK
    if (settings?.serial_number) return next();

    // ✅ Trial Start setzen wenn leer
    if (settings?.id && !settings?.trial_started_at) {
      await db
        .promise()
        .query(
          `UPDATE company_settings SET trial_started_at = NOW() WHERE id = ?`,
          [settings.id],
        );
      return next();
    }

    // Wenn noch immer kein trial start: nicht blockieren
    if (!settings?.trial_started_at) return next();

    const trialStart = new Date(settings.trial_started_at);
    const now = new Date();
    const diffDays = Math.floor((now - trialStart) / (1000 * 60 * 60 * 24));

    // ✅ Trial läuft noch
    if (diffDays < TRIAL_DAYS) return next();

    // ❌ Trial abgelaufen
    if (req.session.user.role === "admin") {
      return res.redirect("/admin/serial-number");
    }

    return res.redirect("/serial-number");
  } catch (err) {
    console.error("❌ LicenseGate Fehler:", err.message);
    return next();
  }
});

/* ===============================
   Sprache ändern
================================ */
app.get("/lang/:lang", (req, res) => {
  const newLang = req.params.lang;

  if (!["de", "es"].includes(newLang)) {
    return res.redirect(req.get("Referrer") || "/dashboard");
  }

  req.session.lang = newLang;

  req.session.save((err) => {
    if (err) console.error("❌ Session save error:", err);
    return res.redirect(req.get("Referrer") || "/dashboard");
  });
});

/* ===============================
   ✅ SERIAL PAGES
================================ */
app.get("/serial-number", async (req, res) => {
  try {
    if (!req.session?.user) return res.redirect("/");

    const [rowsSettings] = await db.promise().query(
      `SELECT id, serial_number, trial_started_at
       FROM company_settings
       ORDER BY id ASC
       LIMIT 1`,
    );

    const settings = rowsSettings?.[0];

    // ✅ Seriennummer da -> ab ins Dashboard
    if (settings?.serial_number) return res.redirect("/dashboard");

    // ✅ Trial Start setzen wenn leer
    if (settings?.id && !settings?.trial_started_at) {
      await db
        .promise()
        .query(
          `UPDATE company_settings SET trial_started_at = NOW() WHERE id = ?`,
          [settings.id],
        );
      settings.trial_started_at = new Date();
    }

    // ✅ Resttage berechnen
    let daysLeft = TRIAL_DAYS;

    if (settings?.trial_started_at) {
      const trialStart = new Date(settings.trial_started_at);
      const now = new Date();
      const diffDays = Math.floor((now - trialStart) / (1000 * 60 * 60 * 24));
      daysLeft = Math.max(0, TRIAL_DAYS - diffDays);
    }

    // ❌ Trial abgelaufen
    if (daysLeft <= 0) {
      if (req.session.user.role === "admin") {
        return res.redirect("/admin/serial-number");
      }

      return res.render("trial_expired", {
        user: req.session.user,
        lang: req.session.lang || "de",
      });
    }

    // ✅ Trial aktiv
    return res.render("serial_number_info", {
      user: req.session.user,
      lang: req.session.lang || "de",
      daysLeft,
    });
  } catch (err) {
    console.error(err);
    return res.status(500).send("Interner Serverfehler");
  }
});

app.get("/admin/serial-number", async (req, res) => {
  try {
    if (!req.session?.user) return res.redirect("/");
    if (req.session.user.role !== "admin")
      return res.status(403).send("Forbidden");

    const [rowsSettings] = await db
      .promise()
      .query(
        `SELECT serial_number FROM company_settings ORDER BY id ASC LIMIT 1`,
      );

    const currentSerial = rowsSettings?.[0]?.serial_number || "";

    return res.render("serial_number_admin", {
      user: req.session.user,
      lang: req.session.lang || "de",
      active: "serialnumber",
      currentSerial,
      error: null,
      success: null,
    });
  } catch (err) {
    console.error(err);
    return res.status(500).send("Interner Serverfehler");
  }
});

app.post("/admin/serial-number", async (req, res) => {
  try {
    if (!req.session?.user) return res.redirect("/");
    if (req.session.user.role !== "admin")
      return res.status(403).send("Forbidden");

    let serial = normalizeSerial(req.body.serial_number);

    if (!serial) {
      return res.render("serial_number_admin", {
        user: req.session.user,
        lang: req.session.lang || "de",
        active: "serialnumber",
        currentSerial: "",
        error: "Bitte Seriennummer eingeben.",
        success: null,
      });
    }

    if (!isValidSerialFormat(serial)) {
      return res.render("serial_number_admin", {
        user: req.session.user,
        lang: req.session.lang || "de",
        active: "serialnumber",
        currentSerial: serial,
        error: "Ungültiges Format. Beispiel: ABC12-3DE45-FG678-HI901",
        success: null,
      });
    }

    if (!passesModulo3(serial)) {
      return res.render("serial_number_admin", {
        user: req.session.user,
        lang: req.session.lang || "de",
        active: "serialnumber",
        currentSerial: serial,
        error: "Modulo-3 Prüfung fehlgeschlagen. Seriennummer ungültig.",
        success: null,
      });
    }

    await db
      .promise()
      .query(`UPDATE company_settings SET serial_number = ? WHERE id = 1`, [
        serial,
      ]);

    return res.render("serial_number_admin", {
      user: req.session.user,
      lang: req.session.lang || "de",
      active: "serialnumber",
      currentSerial: serial,
      error: null,
      success: "✅ Seriennummer gespeichert!",
    });
  } catch (err) {
    console.error(err);

    let msg = "Fehler beim Speichern.";
    if (err.code === "ER_DUP_ENTRY")
      msg = "Diese Seriennummer ist bereits vergeben.";

    return res.render("serial_number_admin", {
      user: req.session.user,
      lang: req.session.lang || "de",
      active: "serialnumber",
      currentSerial: req.body.serial_number || "",
      error: msg,
      success: null,
    });
  }
});

/* ===============================
   DEINE ROUTES (unverändert)
================================ */
app.use(companySettingsRoutes);
app.use("/", authRoutes);
app.use("/dashboard", dashboardRoutes);
app.use("/admin", adminRoutes);

app.use("/patients", patientRoutes);
app.use("/patients", patientMedicationRoutes);
app.use("/patients", patientServiceRoutes);

app.use("/medications", medicationRoutes);
console.log("🧪 /medications Router mounted");

app.use("/services", serviceRoutes);

app.use("/", patientFileRoutes);
app.use("/", waitingRoomRoutes);
app.use("/invoices", invoiceRoutes);

app.use("/reportview", reportRoutes);

app.use("/calendar", calendarRoutes);

app.get("/logout", (req, res) => {
  req.session.destroy(() => res.redirect("/"));
});

/* ===============================
   ERROR HANDLING
================================ */
app.use((err, req, res, next) => {
  console.error(err);
  res.status(500).send("Interner Serverfehler");
});

/* ===============================
   SERVER
================================ */
const PORT = process.env.PORT || 51777;
const HOST = process.env.HOST || "0.0.0.0";

console.log("DB HOST:", process.env.DBSERVER_HOST);
console.log("DB PORT:", process.env.DBSERVER_PORT);

app.listen(PORT, HOST, () => {
  console.log(`Server läuft auf http://${HOST}:${PORT}`);
});
