import express from "express";
import { createServer as createViteServer } from "vite";
import path from "path";
import fs from "fs/promises";
import fsSync from "fs";
import cors from "cors";
import multer from "multer";
import cookieParser from "cookie-parser";
import { fileURLToPath } from "url";
import { createServer as createHttpServer } from "http";
import { Server } from "socket.io";

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

console.log(`[Server] Starting...`);

const DATA_DIR = path.join(__dirname, "data");
const UPLOADS_DIR = path.join(__dirname, "uploads");
const SESSIONS_DIR = path.join(__dirname, "sessions");

// Ensure directories exist
[DATA_DIR, UPLOADS_DIR, SESSIONS_DIR].forEach(dir => {
  try {
    if (!fsSync.existsSync(dir)) {
      fsSync.mkdirSync(dir, { recursive: true });
      console.log(`[Server] Created directory: ${dir}`);
    }
  } catch (err) {
    console.error(`[Server] Failed to access or write to ${dir}:`, err);
  }
});

const storage = multer.diskStorage({
  destination: (req, file, cb) => {
    cb(null, UPLOADS_DIR);
  },
  filename: (req, file, cb) => {
    const uniqueSuffix = Date.now() + "-" + Math.round(Math.random() * 1e9);
    cb(null, uniqueSuffix + "-" + file.originalname);
  },
});

const upload = multer({ storage });

const getSessionPath = (sessionId: string) => path.join(SESSIONS_DIR, `${sessionId}.json`);

const BOOT_TIME = Date.now();

const GLOBAL_KEYS = [
  "users", 
  "roles", 
  "rolePermissions", 
  "companySettings", 
  "projects", 
  "tasks", 
  "expenses", 
  "clients", 
  "engineers", 
  "employees",
  "departments", 
  "jobs", 
  "candidates", 
  "documents", 
  "folders", 
  "bankDetails", 
  "currencies",
  "leads"
].map(k => k.toLowerCase());

const OBJECT_KEYS = ["rolepermissions", "companysettings"];

const getFilePath = (key: string) => {
  const normalizedKey = key.toLowerCase();
  return path.join(DATA_DIR, `${normalizedKey}.json`);
};

const readData = async (key: string, defaultValue: any) => {
  const filePath = getFilePath(key);
  try {
    const content = await fs.readFile(filePath, "utf-8");
    let data = JSON.parse(content);
    
    const normalizedKey = key.toLowerCase();
    if (OBJECT_KEYS.includes(normalizedKey) && Array.isArray(data)) {
      data = data[0] || defaultValue;
    } else if (!OBJECT_KEYS.includes(normalizedKey) && !Array.isArray(data) && data !== null) {
      data = [data];
    }
    
    return data;
  } catch (e) {
    return defaultValue;
  }
};

const LAST_CHANGE_FILE = path.join(DATA_DIR, "last_change.txt");
let io: any;

const getLastGlobalChange = async () => {
  try {
    const content = await fs.readFile(LAST_CHANGE_FILE, "utf-8");
    return parseInt(content);
  } catch (e) {}
  return Date.now();
};

const updateLastGlobalChange = async () => {
  const now = Date.now();
  try {
    await fs.writeFile(LAST_CHANGE_FILE, now.toString());
    return now;
  } catch (e) {
    return now;
  }
};

const writeData = async (key: string, data: any) => {
  const filePath = getFilePath(key);
  await fs.writeFile(filePath, JSON.stringify(data, null, 2));
  const lastChange = await updateLastGlobalChange();
  if (io) {
    io.emit("data:changed", { key, lastChange });
  }
  return lastChange;
};

async function startServer() {
  const app = express();
  const httpServer = createHttpServer(app);
  const PORT = Number(process.env.PORT) || 3000;

  // Initialize Socket.io
  io = new Server(httpServer, {
    cors: {
      origin: true,
      credentials: true
    }
  });

  io.on("connection", (socket: any) => {
    console.log(`[Socket] Client connected: ${socket.id}`);
    socket.on("disconnect", () => {
      console.log(`[Socket] Client disconnected: ${socket.id}`);
    });
  });

  app.use(cors({ origin: true, credentials: true }));
  app.use(cookieParser());
  app.use(express.json({ limit: '50mb' }));
  app.use(express.urlencoded({ extended: true, limit: '50mb' }));
  app.use("/uploads", express.static(UPLOADS_DIR));

  app.use((req, res, next) => {
    res.set('Cache-Control', 'no-store, no-cache, must-revalidate, proxy-revalidate');
    res.set('Pragma', 'no-cache');
    res.set('Expires', '0');
    next();
  });

  const getUserId = async (req: express.Request) => {
    const sessionId = (req.headers['x-session-id'] as string) || req.cookies.sessionId;
    if (!sessionId) return null;
    const sessionPath = getSessionPath(sessionId);
    try {
      const content = await fs.readFile(sessionPath, "utf-8");
      const sessionData = JSON.parse(content);
      return sessionData.uid || sessionData.id || (sessionData.profile && (sessionData.profile.uid || sessionData.profile.id));
    } catch (e) {
      return null;
    }
  };

  // API Routes
  app.get("/api/health", async (req, res) => {
    res.json({ 
      status: "ok", 
      timestamp: new Date().toISOString(),
      lastChange: await getLastGlobalChange(),
      bootTime: BOOT_TIME
    });
  });

  app.get("/api/data/:key", async (req, res) => {
    const { key } = req.params;
    const normalizedKey = key.toLowerCase();
    const userId = await getUserId(req);
    
    // Allow read if authenticated OR if it's a known global key
    if (!userId && !GLOBAL_KEYS.includes(normalizedKey)) {
      return res.json([]);
    }

    const data = await readData(key, []);
    res.json(data);
  });

  app.post("/api/data/:key", async (req, res) => {
    const { key } = req.params;
    const data = req.body;
    const userId = await getUserId(req);
    
    if (!userId && !["users"].includes(key.toLowerCase())) {
      return res.status(401).json({ error: "Unauthorized" });
    }

    try {
      const lastChange = await writeData(key, data);
      res.json({ success: true, lastChange });
    } catch (error: any) {
      res.status(500).json({ success: false, error: error.message });
    }
  });

  app.get("/api/auth/user", async (req, res) => {
    const sessionId = (req.headers['x-session-id'] as string) || req.cookies.sessionId;
    if (!sessionId) return res.json(null);
    const sessionPath = getSessionPath(sessionId);
    try {
      const content = await fs.readFile(sessionPath, "utf-8");
      const user = JSON.parse(content);
      res.json(user);
    } catch (e) {
      res.json(null);
    }
  });

  app.post("/api/auth/user", async (req, res) => {
    const sessionId = (req.headers['x-session-id'] as string) || req.cookies.sessionId || (Math.random().toString(36).substring(2) + Date.now().toString(36));
    res.cookie("sessionId", sessionId, { 
      httpOnly: true, 
      secure: true,
      sameSite: "none",
      maxAge: 30 * 24 * 60 * 60 * 1000
    });
    try {
      await fs.writeFile(getSessionPath(sessionId), JSON.stringify(req.body, null, 2));
      res.json({ success: true, sessionId });
    } catch (error) {
      res.status(500).json({ success: false, error: "Failed to save session" });
    }
  });

  app.post("/api/auth/logout", async (req, res) => {
    const sessionId = (req.headers['x-session-id'] as string) || req.cookies.sessionId;
    if (sessionId) {
      const sessionPath = getSessionPath(sessionId);
      try {
        await fs.unlink(sessionPath);
      } catch (e) {}
    }
    res.clearCookie("sessionId", { httpOnly: true, secure: true, sameSite: "none" });
    res.json({ success: true });
  });

  app.get("/api/admin/migrate", async (req, res) => {
    const userId = await getUserId(req);
    if (!userId) return res.status(401).json({ error: "Unauthorized" });
    
    try {
      // In this local JSON implementation, all data is in the shared data/ directory already.
      // We just update the last change timestamp to force all clients to refresh.
      const lastChange = await updateLastGlobalChange();
      if (io) {
        io.emit("data:changed", { key: "global", lastChange });
      }
      res.json({ success: true, lastChange });
    } catch (e: any) {
      res.status(500).json({ success: false, error: e.message });
    }
  });

  app.post("/api/upload", upload.single("file"), (req, res) => {
    if (!req.file) return res.status(400).json({ error: "No file uploaded" });
    res.json({
      name: req.file.originalname,
      url: `/uploads/${req.file.filename}`,
      size: `${(req.file.size / 1024).toFixed(2)} KB`,
      type: req.file.mimetype,
      uploadedAt: new Date().toISOString(),
    });
  });

  // Vite middleware for development
  if (process.env.NODE_ENV !== "production") {
    const vite = await createViteServer({
      server: { middlewareMode: true },
      appType: "spa",
    });
    app.use(vite.middlewares);
  } else {
    const distPath = path.join(__dirname, "dist");
    app.use(express.static(distPath));
    app.get("*", (req, res) => {
      res.sendFile(path.join(distPath, "index.html"));
    });
  }

  httpServer.listen(PORT, "0.0.0.0", async () => {
    console.log(`Server running on http://localhost:${PORT}`);
    // Trigger a global change event on startup to refresh clients if needed
    try {
      await updateLastGlobalChange();
    } catch (e) {}
  });
}

startServer();
