import bcrypt from "bcryptjs";
import { db, pool } from "./db";
import { users, userProfiles } from "../shared/schema";
import { eq } from "drizzle-orm";
import type { Express, Request, Response, NextFunction } from "express";
import session from "express-session";
import MySQLStore from "express-mysql-session";

const MySQLSessionStore = MySQLStore(session as any);

export interface AuthenticatedRequest extends Request {
  user?: {
    id: string;
    username: string;
    firstName: string | null;
    lastName: string | null;
    mustChangePassword: boolean;
  };
}

export async function setupAuth(app: Express) {
  const sessionSecret = process.env.SESSION_SECRET || "isb-case-management-secret-key";
  
  const sessionStoreOptions = {
    clearExpired: true,
    checkExpirationInterval: 900000,
    expiration: 24 * 60 * 60 * 1000,
    createDatabaseTable: true,
    schema: {
      tableName: 'sessions',
      columnNames: {
        session_id: 'session_id',
        expires: 'expires',
        data: 'data'
      }
    }
  };

  const sessionStore = new MySQLSessionStore(sessionStoreOptions, pool as any);
  
  app.use(
    session({
      store: sessionStore,
      secret: sessionSecret,
      resave: false,
      saveUninitialized: false,
      cookie: {
        secure: process.env.NODE_ENV === "production",
        httpOnly: true,
        maxAge: 24 * 60 * 60 * 1000,
      },
    })
  );
}

export function isAuthenticated(req: any, res: Response, next: NextFunction) {
  if (req.session && req.session.userId) {
    db.select()
      .from(users)
      .where(eq(users.id, req.session.userId))
      .then((result: any[]) => {
        const user = result[0];
        if (user) {
          req.user = {
            id: user.id,
            username: user.username || "",
            firstName: user.firstName,
            lastName: user.lastName,
            mustChangePassword: user.mustChangePassword,
          };
          next();
        } else {
          res.status(401).json({ message: "Unauthorized" });
        }
      })
      .catch(() => {
        res.status(401).json({ message: "Unauthorized" });
      });
  } else {
    res.status(401).json({ message: "Unauthorized" });
  }
}

export function registerAuthRoutes(app: Express) {
  app.post("/api/auth/login", async (req: Request, res: Response) => {
    try {
      const { username, password } = req.body;
      
      if (!username || !password) {
        return res.status(400).json({ message: "Username and password are required" });
      }
      
      const result = await db
        .select()
        .from(users)
        .where(eq(users.username, username));
      
      const user = result[0];
      
      if (!user || !user.passwordHash) {
        return res.status(401).json({ message: "Invalid username or password" });
      }
      
      const validPassword = await bcrypt.compare(password, user.passwordHash);
      
      if (!validPassword) {
        return res.status(401).json({ message: "Invalid username or password" });
      }
      
      await db
        .update(users)
        .set({ lastLoginAt: new Date() })
        .where(eq(users.id, user.id));
      
      (req.session as any).userId = user.id;
      
      res.json({
        id: user.id,
        username: user.username,
        firstName: user.firstName,
        lastName: user.lastName,
        mustChangePassword: user.mustChangePassword,
      });
    } catch (error) {
      console.error("Login error:", error);
      res.status(500).json({ message: "Login failed" });
    }
  });
  
  app.post("/api/auth/logout", (req: Request, res: Response) => {
    req.session.destroy((err) => {
      if (err) {
        return res.status(500).json({ message: "Logout failed" });
      }
      res.json({ message: "Logged out successfully" });
    });
  });
  
  app.get("/api/auth/user", isAuthenticated, async (req: any, res: Response) => {
    try {
      const userId = req.user!.id;
      
      const result = await db
        .select()
        .from(users)
        .where(eq(users.id, userId));
      
      const user = result[0];
      
      if (!user) {
        return res.status(404).json({ message: "User not found" });
      }
      
      res.json({
        id: user.id,
        username: user.username,
        firstName: user.firstName,
        lastName: user.lastName,
        email: user.email,
        mustChangePassword: user.mustChangePassword,
      });
    } catch (error) {
      console.error("Error fetching user:", error);
      res.status(500).json({ message: "Failed to fetch user" });
    }
  });
  
  app.post("/api/auth/change-password", isAuthenticated, async (req: any, res: Response) => {
    try {
      const userId = req.user!.id;
      const { currentPassword, newPassword } = req.body;
      
      if (!newPassword || newPassword.length < 8) {
        return res.status(400).json({ message: "New password must be at least 8 characters" });
      }
      
      const result = await db
        .select()
        .from(users)
        .where(eq(users.id, userId));
      
      const user = result[0];
      
      if (!user || !user.passwordHash) {
        return res.status(404).json({ message: "User not found" });
      }
      
      if (currentPassword) {
        const validPassword = await bcrypt.compare(currentPassword, user.passwordHash);
        if (!validPassword) {
          return res.status(401).json({ message: "Current password is incorrect" });
        }
      }
      
      const passwordHash = await bcrypt.hash(newPassword, 10);
      
      await db
        .update(users)
        .set({ 
          passwordHash, 
          mustChangePassword: false,
          updatedAt: new Date(),
        })
        .where(eq(users.id, userId));
      
      res.json({ message: "Password changed successfully" });
    } catch (error) {
      console.error("Error changing password:", error);
      res.status(500).json({ message: "Failed to change password" });
    }
  });
}

export async function createUser(data: {
  username: string;
  password: string;
  firstName: string;
  lastName: string;
  email?: string;
  role: string;
  badgeNumber?: string;
  department?: string;
  mustChangePassword?: boolean;
}) {
  const passwordHash = await bcrypt.hash(data.password, 10);
  
  await db
    .insert(users)
    .values({
      username: data.username,
      passwordHash,
      firstName: data.firstName,
      lastName: data.lastName,
      email: data.email,
      mustChangePassword: data.mustChangePassword ?? true,
    });
  
  const result = await db
    .select()
    .from(users)
    .where(eq(users.username, data.username));
  
  const user = result[0];
  
  if (!user) {
    throw new Error("Failed to create user");
  }
  
  await db
    .insert(userProfiles)
    .values({
      userId: user.id,
      role: data.role as any,
      badgeNumber: data.badgeNumber,
      department: data.department,
      isActive: true,
    });
  
  return user;
}

export async function resetUserPassword(userId: string, newPassword: string) {
  const passwordHash = await bcrypt.hash(newPassword, 10);
  
  await db
    .update(users)
    .set({ 
      passwordHash, 
      mustChangePassword: true,
      updatedAt: new Date(),
    })
    .where(eq(users.id, userId));
}
