import { Router, RequestHandler } from "express";
import session from "express-session";
import bcrypt from "bcrypt";
import { db } from "./db";
import { adminUsers } from "@shared/schema";
import { eq } from "drizzle-orm";
import connectPg from "connect-pg-simple";

// Portable authentication system for self-hosted deployments
// Works with any PostgreSQL database, no Replit dependencies

export function isReplitEnvironment(): boolean {
  return !!process.env.REPL_ID;
}

export function getPortableSession() {
  const sessionTtl = 7 * 24 * 60 * 60 * 1000; // 1 week
  const pgStore = connectPg(session);
  
  const sessionStore = new pgStore({
    conString: process.env.DATABASE_URL,
    createTableIfMissing: true,
    ttl: sessionTtl,
    tableName: "portable_sessions",
    errorLog: (err: Error) => {
      // Suppress "already exists" errors - table/index already created
      if (!err.message.includes("already exists")) {
        console.error("Session store error:", err);
      }
    },
  });
  
  return session({
    secret: process.env.SESSION_SECRET || "change-this-in-production-to-a-random-string",
    store: sessionStore,
    resave: false,
    saveUninitialized: false,
    cookie: {
      httpOnly: true,
      secure: process.env.NODE_ENV === "production",
      maxAge: sessionTtl,
    },
  });
}

// Check if an admin exists
export async function hasAdminUser(): Promise<boolean> {
  const users = await db.select().from(adminUsers).limit(1);
  return users.length > 0;
}

// Create first admin user (setup flow)
export async function createAdminUser(username: string, password: string) {
  const passwordHash = await bcrypt.hash(password, 12);
  const [user] = await db.insert(adminUsers).values({
    username,
    passwordHash,
  }).returning();
  return user;
}

// Verify admin credentials
export async function verifyAdminUser(username: string, password: string) {
  const [user] = await db.select().from(adminUsers).where(eq(adminUsers.username, username));
  if (!user) return null;
  
  const isValid = await bcrypt.compare(password, user.passwordHash);
  if (!isValid) return null;
  
  return user;
}

// Middleware to check if user is authenticated
export const isPortableAuthenticated: RequestHandler = (req, res, next) => {
  if (req.session && (req.session as any).adminId) {
    return next();
  }
  res.status(401).json({ message: "Unauthorized" });
};

// Get portable auth routes
export function getPortableAuthRoutes(): Router {
  const router = Router();
  
  // Check if setup is needed
  router.get("/api/auth/status", async (_req, res) => {
    const hasAdmin = await hasAdminUser();
    res.json({ 
      setupRequired: !hasAdmin,
      isReplitEnvironment: isReplitEnvironment(),
    });
  });
  
  // Setup first admin (only works if no admin exists)
  router.post("/api/auth/setup", async (req, res) => {
    const hasAdmin = await hasAdminUser();
    if (hasAdmin) {
      return res.status(400).json({ message: "Admin already exists" });
    }
    
    const { username, password } = req.body;
    if (!username || !password) {
      return res.status(400).json({ message: "Username and password required" });
    }
    
    if (password.length < 8) {
      return res.status(400).json({ message: "Password must be at least 8 characters" });
    }
    
    try {
      const user = await createAdminUser(username, password);
      (req.session as any).adminId = user.id;
      res.json({ success: true, message: "Admin created successfully" });
    } catch (error) {
      console.error("Error creating admin:", error);
      res.status(500).json({ message: "Failed to create admin" });
    }
  });
  
  // Login
  router.post("/api/auth/login", async (req, res) => {
    const { username, password } = req.body;
    
    if (!username || !password) {
      return res.status(400).json({ message: "Username and password required" });
    }
    
    const user = await verifyAdminUser(username, password);
    if (!user) {
      return res.status(401).json({ message: "Invalid credentials" });
    }
    
    (req.session as any).adminId = user.id;
    res.json({ success: true, user: { id: user.id, username: user.username } });
  });
  
  // Logout
  router.post("/api/auth/logout", (req, res) => {
    req.session.destroy((err) => {
      if (err) {
        return res.status(500).json({ message: "Failed to logout" });
      }
      res.json({ success: true });
    });
  });
  
  // Get current user
  router.get("/api/auth/user", async (req, res) => {
    if (req.session && (req.session as any).adminId) {
      const [user] = await db.select().from(adminUsers).where(eq(adminUsers.id, (req.session as any).adminId));
      if (user) {
        res.json({ 
          id: user.id,
          username: user.username,
          authenticated: true 
        });
      } else {
        res.status(401).json({ message: "Unauthorized" });
      }
    } else {
      res.status(401).json({ message: "Unauthorized" });
    }
  });
  
  // Change password
  router.post("/api/auth/change-password", async (req, res) => {
    if (!req.session || !(req.session as any).adminId) {
      return res.status(401).json({ message: "Unauthorized" });
    }
    
    const { currentPassword, newPassword } = req.body;
    
    if (!currentPassword || !newPassword) {
      return res.status(400).json({ message: "Current and new password required" });
    }
    
    if (newPassword.length < 8) {
      return res.status(400).json({ message: "New password must be at least 8 characters" });
    }
    
    const [user] = await db.select().from(adminUsers).where(eq(adminUsers.id, (req.session as any).adminId));
    if (!user) {
      return res.status(401).json({ message: "Unauthorized" });
    }
    
    const isValid = await bcrypt.compare(currentPassword, user.passwordHash);
    if (!isValid) {
      return res.status(401).json({ message: "Current password is incorrect" });
    }
    
    const newPasswordHash = await bcrypt.hash(newPassword, 12);
    await db.update(adminUsers).set({ 
      passwordHash: newPasswordHash,
      updatedAt: new Date()
    }).where(eq(adminUsers.id, user.id));
    
    res.json({ success: true, message: "Password changed successfully" });
  });
  
  return router;
}
