var express = require("express");
var router = express.Router();
var path = require("path");
const fetch = require("node-fetch");
const fs = require("fs");
const airportData = JSON.parse(
  fs.readFileSync(path.join(__dirname, "../airportdata.json"), "utf8")
);
const db = require("../models");
const { create, parse } = require("xmlbuilder2");
const speakeasy = require("speakeasy");
const QRCode = require("qrcode");
const rateLimitMap = new Map();
const multer = require("multer");
const uploadDir = path.join(__dirname, "../public/uploads");
if (!fs.existsSync(uploadDir)) fs.mkdirSync(uploadDir, { recursive: true });
const storage = multer.diskStorage({
  destination: function (req, file, cb) {
    cb(null, uploadDir);
  },
  filename: function (req, file, cb) {
    const unique = Date.now() + "-" + Math.round(Math.random() * 1e9);
    cb(null, unique + "-" + file.originalname);
  },
});
const uploadMulter = multer({ storage });

const stripe = require("stripe")(process.env.STRIPE_SECRET_KEY);
const redis = require("redis");
const client = redis.createClient();
client.connect().catch(console.error);

// 2FA middleware
function require2FA(req, res, next) {
  if (req.session && req.session.twoFactorVerified) {
    next();
  } else {
    res.status(403).json({ error: "2FA verification required" });
  }
}

/* GET home page. */
router.get("/", function (req, res, next) {
  res.sendFile(path.join(__dirname, "../views/index.html"));
});

// Weather API route
router.get("/api/weather", async function (req, res) {
  try {
    // Replace with your actual API key and city
    const apiKey = process.env.WEATHER_API_KEY;
    const city = "Toronto";
    const url = `http://api.weatherapi.com/v1/current.json?key=${apiKey}&q=${city}&aqi=no`;
    const response = await fetch(url);
    if (!response.ok) throw new Error("Weather API error");
    const data = await response.json();
    const temp_c = data.current.temp_c;
    const text = data.current.condition.text.toLowerCase();
    let condition = "sunny";
    if (text.includes("rain")) condition = "rain";
    else if (text.includes("snow")) condition = "snow";
    res.json({ temp_c, condition });
  } catch (err) {
    res.status(500).json({ error: "Failed to fetch weather" });
    console.error(err);
  }
});

// UTC time API route
router.get("/api/utc", function (req, res) {
  res.json({ utc: new Date().toISOString() });
});

// Autocomplete airports
router.get("/airports", function (req, res) {
  const search = (req.query.search || "").trim();
  if (search.length < 3) {
    return res
      .status(400)
      .json({ error: "Search term must be at least 3 characters" });
  }
  const term = search.toLowerCase();
  const matches = airportData
    .filter(
      (a) =>
        (a.name && a.name.toLowerCase().includes(term)) ||
        (a.code && a.code.toLowerCase().includes(term))
    )
    .slice(0, 10);
  res.json(matches);
});

// Log analytic event with rate limiting
router.post("/analytic", async function (req, res) {
  try {
    const ip = req.headers["x-forwarded-for"] || req.connection.remoteAddress;
    const now = Date.now();
    const windowMs = 60 * 1000;
    const maxReq = 10;
    if (!rateLimitMap.has(ip)) rateLimitMap.set(ip, []);
    let timestamps = rateLimitMap.get(ip).filter((ts) => now - ts < windowMs);
    if (timestamps.length >= maxReq) {
      return res.status(429).json({ redirect: "/pay" });
    }
    timestamps.push(now);
    rateLimitMap.set(ip, timestamps);
    const { widget_name, browser_type } = req.body;
    if (!widget_name || !browser_type) {
      return res
        .status(400)
        .json({ error: "widget_name and browser_type required" });
    }
    await db.analytic.create({ widget_name, browser_type });
    res.json({ success: true });
  } catch (err) {
    res.status(500).json({ error: "Failed to log analytic" });
  }
});

// Count analytic rows
router.get("/analytic/count", async function (req, res) {
  try {
    const count = await db.analytic.count();
    res.json({ count });
  } catch (err) {
    res.status(500).json({ error: "Failed to count analytics" });
  }
});

// Export analytic as XML
router.get("/analytic/export", async function (req, res) {
  try {
    const analytics = await db.analytic.findAll({ raw: true });
    const root = create({ version: "1.0" }).ele("analytics");
    analytics.forEach((a) => {
      root
        .ele("analytic")
        .ele("id")
        .txt(a.id)
        .up()
        .ele("create_at")
        .txt(a.create_at)
        .up()
        .ele("widget_name")
        .txt(a.widget_name)
        .up()
        .ele("browser_type")
        .txt(a.browser_type)
        .up()
        .up();
    });
    const xml = root.end({ prettyPrint: true });
    res.set("Content-Type", "application/xml");
    res.set("Content-Disposition", 'attachment; filename="analytics.xml"');
    res.send(xml);
  } catch (err) {
    res.status(500).json({ error: "Failed to export analytics" });
  }
});

// Import analytic from XML
router.post(
  "/analytic/import",
  uploadMulter.single("xmlfile"),
  async function (req, res) {
    try {
      if (!req.file) {
        return res.status(400).json({ error: "No XML file uploaded" });
      }

      // Check file type
      if (
        req.file.mimetype !== "text/xml" &&
        req.file.mimetype !== "application/xml"
      ) {
        return res.status(400).json({ error: "File must be XML format" });
      }

      // Read and parse the uploaded XML file
      const xmlContent = fs.readFileSync(req.file.path, "utf8");
      const doc = parse(xmlContent);

      // Extract analytics data from XML
      const analytics = [];
      const analyticNodes = doc.find("//analytic");

      for (const node of analyticNodes) {
        const id = node.find("id")[0]?.text || null;
        const createAt = node.find("create_at")[0]?.text || null;
        const widgetName = node.find("widget_name")[0]?.text || null;
        const browserType = node.find("browser_type")[0]?.text || null;

        if (widgetName && browserType) {
          analytics.push({
            id: id ? parseInt(id) : undefined,
            create_at: createAt ? new Date(createAt) : new Date(),
            widget_name: widgetName,
            browser_type: browserType,
          });
        }
      }

      if (analytics.length === 0) {
        return res
          .status(400)
          .json({ error: "No valid analytics data found in XML" });
      }

      // Insert analytics into database
      const insertedAnalytics = await db.analytic.bulkCreate(analytics, {
        ignoreDuplicates: true,
        updateOnDuplicate: ["widget_name", "browser_type"],
      });

      // Clean up uploaded file
      fs.unlinkSync(req.file.path);

      res.json({
        success: true,
        message: `Successfully imported ${insertedAnalytics.length} analytics records`,
        count: insertedAnalytics.length,
      });
    } catch (err) {
      // Clean up uploaded file on error
      if (req.file && req.file.path) {
        try {
          fs.unlinkSync(req.file.path);
        } catch (cleanupErr) {
          console.error("Failed to cleanup uploaded file:", cleanupErr);
        }
      }

      console.error("Import error:", err);
      res.status(500).json({ error: "Failed to import analytics from XML" });
    }
  }
);

// Reddit widget route
router.get("/reddit", async function (req, res) {
  try {
    const response = await fetch("https://www.reddit.com/r/programming.json");
    if (!response.ok) throw new Error("Reddit API error");
    const data = await response.json();
    const posts = (data.data.children || [])
      .map((c) => c.data)
      .filter((_, i) => i % 2 === 0)
      .slice(0, 4)
      .map((post) => ({
        title: post.title,
        url: "https://reddit.com" + post.permalink,
        author: post.author,
      }));
    res.json(posts);
  } catch (err) {
    res.status(500).json({ error: "Failed to fetch reddit posts" });
  }
});

// Coin calculator route
router.post("/coin-calc", function (req, res) {
  try {
    let { amount } = req.body;
    amount = parseFloat(amount);
    if (isNaN(amount) || amount < 0)
      return res.status(400).json({ error: "Invalid amount" });
    const denoms = [20, 10, 5, 1, 0.25, 0.1, 0.05, 0.01];
    const result = [];
    let remaining = Math.round(amount * 100); // work in cents
    for (let d of denoms) {
      let denomCents = Math.round(d * 100);
      let count = Math.floor(remaining / denomCents);
      if (count > 0) {
        result.push({ denomination: d, count });
        remaining -= count * denomCents;
      }
    }
    res.json(result);
  } catch (err) {
    res.status(500).json({ error: "Failed to calculate coins" });
  }
});

// Stripe payment page
router.get("/pay", async function (req, res) {
  const session = await stripe.checkout.sessions.create({
    payment_method_types: ["card"],
    line_items: [
      {
        price_data: {
          currency: "usd",
          product_data: { name: "Widget Analytics Access" },
          unit_amount: 500,
        },
        quantity: 1,
      },
    ],
    mode: "payment",
    success_url: req.protocol + "://" + req.get("host") + "/?paid=1",
    cancel_url: req.protocol + "://" + req.get("host") + "/pay?cancel=1",
  });
  res.send(`
    <html>
      <head><title>Pay for Analytics</title></head>
      <body style="display:flex;align-items:center;justify-content:center;height:100vh;flex-direction:column;">
        <h2>Rate limit exceeded</h2>
        <p>You need to pay $5 to continue using analytics.</p>
        <button id="checkout">Pay with Stripe</button>
        <script src="https://js.stripe.com/v3/"></script>
        <script>
          document.getElementById('checkout').onclick = function() {
            var stripe = Stripe('${STRIPE_PUBLIC_KEY}');
            stripe.redirectToCheckout({ sessionId: '${session.id}' });
          }
        </script>
      </body>
    </html>
  `);
});

// Upload image
router.post("/upload", uploadMulter.single("image"), async function (req, res) {
  try {
    if (!req.file) return res.status(400).json({ error: "No file uploaded" });
    const file = req.file;
    const dbUpload = await db.upload.create({
      filename: file.filename,
      mimetype: file.mimetype,
      path: "/uploads/" + file.filename,
    });
    res.json({ url: "/uploads/" + file.filename });
  } catch (err) {
    res.status(500).json({ error: "Failed to upload image" });
  }
});

// Get latest uploaded image
router.get("/upload/latest", async function (req, res) {
  try {
    const latest = await db.upload.findOne({ order: [["created_at", "DESC"]] });
    if (!latest) return res.json({ url: null });
    res.json({ url: latest.path });
  } catch (err) {
    res.status(500).json({ error: "Failed to fetch latest upload" });
  }
});

// Chat routes
router.post("/send", async function (req, res) {
  try {
    const { message } = req.body;
    if (!message) return res.status(400).json({ error: "Message required" });

    const chatMessage = {
      message,
      timestamp: new Date().toISOString(),
      id: Date.now(),
    };

    await client.lPush("chatroom", JSON.stringify(chatMessage));
    res.json({ success: true });
  } catch (err) {
    res.status(500).json({ error: "Failed to send message" });
  }
});

router.get("/chat/all", async function (req, res) {
  try {
    const messages = await client.lRange("chatroom", 0, -1);
    const parsedMessages = messages.map((msg) => JSON.parse(msg));
    res.json(parsedMessages);
  } catch (err) {
    res.status(500).json({ error: "Failed to fetch messages" });
  }
});

router.get("/poll", async function (req, res) {
  try {
    const messageCount = await client.lLen("chatroom");
    const lastCheck = req.query.lastCheck || 0;

    if (messageCount > lastCheck) {
      res.json({ updated: true, count: messageCount });
    } else {
      res.json({ updated: false, count: messageCount });
    }
  } catch (err) {
    res.status(500).json({ error: "Poll failed" });
  }
});

router.post("/chat/save", async function (req, res) {
  try {
    const messages = await client.lRange("chatroom", 0, -1);
    const chatMessages = JSON.stringify(messages.map((msg) => JSON.parse(msg)));

    await db.chat.create({
      chat_messages: chatMessages,
    });

    res.json({ success: true });
  } catch (err) {
    res.status(500).json({ error: "Failed to save chat" });
  }
});

// Chat page
router.get("/chat", function (req, res) {
  res.sendFile(path.join(__dirname, "../views/chat.html"));
});

// Flow Builder routes
router.get("/flow", function (req, res) {
  res.sendFile(path.join(__dirname, "../views/flow.html"));
});

// Get all flows
router.get("/flows", async function (req, res) {
  try {
    const flows = await db.flow.findAll({
      order: [["created_at", "DESC"]],
    });
    res.json(flows);
  } catch (err) {
    res.status(500).json({ error: "Failed to fetch flows" });
  }
});

// Create new flow
router.post("/flow", async function (req, res) {
  try {
    const { name, description } = req.body;
    if (!name) return res.status(400).json({ error: "Flow name required" });

    const flow = await db.flow.create({ name, description });
    res.json({ id: flow.id, name: flow.name });
  } catch (err) {
    res.status(500).json({ error: "Failed to create flow" });
  }
});

// Add task to flow
router.post("/flow/:id/task", async function (req, res) {
  try {
    const flowId = req.params.id;
    const { action_type, input_data, order_index } = req.body;

    if (!action_type || !input_data || order_index === undefined) {
      return res
        .status(400)
        .json({ error: "action_type, input_data, and order_index required" });
    }

    const task = await db.task.create({
      flow_id: flowId,
      action_type,
      input_data,
      order_index,
    });

    res.json({ id: task.id, action_type: task.action_type });
  } catch (err) {
    res.status(500).json({ error: "Failed to add task" });
  }
});

// Get flow details
router.get("/flow/:id", async function (req, res) {
  try {
    const flowId = req.params.id;
    const flow = await db.flow.findByPk(flowId);
    const tasks = await db.task.findAll({
      where: { flow_id: flowId },
      order: [["order_index", "ASC"]],
    });

    res.json({ flow, tasks });
  } catch (err) {
    res.status(500).json({ error: "Failed to fetch flow" });
  }
});

// Execute flow
router.post("/flow/:id/execute", async function (req, res) {
  try {
    const flowId = req.params.id;
    const { payload } = req.body;

    const flow = await db.flow.findByPk(flowId);
    const tasks = await db.task.findAll({
      where: { flow_id: flowId },
      order: [["order_index", "ASC"]],
    });

    const results = [];

    for (const task of tasks) {
      let result = "";
      let status = "success";

      try {
        switch (task.action_type) {
          case "send_test_mail":
            // Simulate sending email
            result = `Email sent to: ${task.input_data}`;
            break;

          case "http_get_request":
            const response = await fetch(task.input_data);
            result = `HTTP GET ${task.input_data}: ${response.status}`;
            break;

          case "mysql_select":
            const [table, id] = task.input_data.split("|");
            const query = `SELECT * FROM ${table} WHERE id = ${id}`;
            result = `Query executed: ${query}`;
            break;

          case "drive_upload":
            result = `File uploaded to Google Drive with content: ${task.input_data}`;
            break;

          default:
            result = `Unknown action type: ${task.action_type}`;
            status = "error";
        }
      } catch (err) {
        result = `Error: ${err.message}`;
        status = "error";
      }

      // Log the execution
      await db.flow_log.create({
        flow_id: flowId,
        task_id: task.id,
        result,
        status,
      });

      results.push({ task_id: task.id, result, status });
    }

    res.json({ flow_id: flowId, results });
  } catch (err) {
    res.status(500).json({ error: "Failed to execute flow" });
  }
});

// Webhook trigger
router.get("/flow/:id/trigger", async function (req, res) {
  try {
    const flowId = req.params.id;
    const payload = req.query.payload;

    if (!payload) {
      return res.status(400).json({ error: "Payload required" });
    }

    // Execute the flow with the payload
    const response = await fetch(
      `${req.protocol}://${req.get("host")}/flow/${flowId}/execute`,
      {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ payload }),
      }
    );

    const result = await response.json();
    res.json(result);
  } catch (err) {
    res.status(500).json({ error: "Failed to trigger flow" });
  }
});

// 2FA Routes
// Generate 2FA secret
router.get("/2fa/generate", async function (req, res) {
  try {
    const secret = speakeasy.generateSecret({
      name: "Dashboard 2FA",
      length: 20,
    });

    // Store secret in session for verification
    req.session = req.session || {};
    req.session.tempSecret = secret.base32;

    // Generate QR code as data URL
    const qrCodeDataUrl = await QRCode.toDataURL(secret.otpauth_url, {
      width: 200,
      margin: 2,
      color: {
        dark: "#000000",
        light: "#FFFFFF",
      },
    });

    res.json({
      secret: secret.base32,
      qrCode: qrCodeDataUrl,
      qrCodeUrl: secret.otpauth_url,
    });
  } catch (err) {
    console.error("2FA generation error:", err);
    res.status(500).json({ error: "Failed to generate 2FA secret" });
  }
});

// Verify 2FA token
router.post("/2fa/verify", function (req, res) {
  try {
    const { token } = req.body;
    const tempSecret = req.session?.tempSecret;

    if (!tempSecret) {
      return res
        .status(400)
        .json({ error: "No 2FA secret found. Please generate a new one." });
    }

    if (!token) {
      return res.status(400).json({ error: "Token required" });
    }

    const verified = speakeasy.totp.verify({
      secret: tempSecret,
      encoding: "base32",
      token: token,
      window: 2, // Allow 2 time steps for clock skew
    });

    if (verified) {
      // Mark 2FA as verified in session
      req.session = req.session || {};
      req.session.twoFactorVerified = true;
      delete req.session.tempSecret; // Clean up temp secret

      res.json({ success: true, message: "2FA verification successful" });
    } else {
      res.status(400).json({ error: "Invalid 2FA token" });
    }
  } catch (err) {
    res.status(500).json({ error: "Failed to verify 2FA token" });
  }
});

// Check 2FA status
router.get("/2fa/status", function (req, res) {
  const verified = req.session?.twoFactorVerified || false;
  res.json({ verified });
});

module.exports = router;
