import type { Express, Request, Response, NextFunction } from "express";
import type { IStorage } from './storage.js';
import { templateEngine } from './template-engine.js';
import { imageProcessor } from './image-processor.js';
import { SimpleTranscriptionService } from './simple-transcription.js';
import { db } from './db.js';
import { lendableItems, lendableItemCategories } from '@shared/schema.js';
import { eq, and, desc } from 'drizzle-orm';
import multer from "multer";
import path from "path";
import fs from "fs/promises";
import { existsSync, mkdirSync } from "fs";

// Session interface
interface SessionData {
  userId?: number;
  username?: string;
}

// No session handling - authentication cancelled

// Helper function to map usernames to avatar filenames
function getUserAvatarFilename(username: string): string {
  const avatarMap: Record<string, string> = {
    'jacob.lowery': 'm19.jpg',
    'kiley.ford': 'f67.jpg', 
    'brianna.jenkins': 'f78.jpg',
    'josh.mcintosh': 'm11.jpg',
    'allyson.mcfarland': 'f12.jpg',
    'chloe.trevino': 'f22.jpg',
    'morgan.gilbert': 'f10.jpg',
    'patricia.burton': 'f71.jpg',
    'adelyn.griffin': 'f46.jpg',
    'nora.rush': 'f34.jpg',
    'mariana.rivas': 'f23.jpg',
    'jaelyn.oneal': 'm46.jpg',
    'alex.chen': 'm19.jpg',
    'sarah.williams': 'f67.jpg'
  };
  return avatarMap[username] || 'f10.jpg'; // fallback to default
}

// Helper function to get avatar path for a user
async function getUserAvatarPath(user: any): Promise<string> {
  if (!user) {
    return '/ll_images/f10.jpg'; // default avatar
  }
  
  // If user has a custom avatar, use it
  if (user.avatar) {
    return `/ll_images/${user.avatar}`;
  }
  
  // Otherwise use the mapped avatar based on username
  const filename = getUserAvatarFilename(user.username || '');
  return `/ll_images/${filename}`;
}

// Helper function to generate breadcrumbs for pages
function formatBreadcrumbLabel(text: string): string {
  // Split on hyphens and capitalize each word
  return text
    .split('-')
    .map(word => word.charAt(0).toUpperCase() + word.slice(1))
    .join(' ');
}

function generateBreadcrumbs(pageType: string, itemName?: string, userId?: number): Array<{label: string, url?: string}> {
  // Special case for dashboard - just return Dashboard without URL since we're already on it
  if (pageType === 'dashboard') {
    return [{ label: 'Dashboard', url: undefined }];
  }
  
  const breadcrumbs: Array<{label: string, url?: string}> = [
    { label: 'Dashboard', url: userId ? `/dashboard/${userId}` : '/' }
  ];
  
  switch (pageType) {
    case 'borrow-request':
      breadcrumbs.push({ label: 'Borrow Request', url: undefined });
      break;
    case 'item-detail':
      breadcrumbs.push({ label: 'Browse Items', url: '/browse' });
      if (itemName) {
        breadcrumbs.push({ label: itemName, url: undefined });
      }
      break;
    case 'create-item':
      breadcrumbs.push({ label: 'Create Item', url: undefined });
      break;
    case 'edit-item':
      breadcrumbs.push({ label: 'My Inventory', url: userId ? `/my-inventory/${userId}` : '/my-inventory' });
      if (itemName) {
        breadcrumbs.push({ label: `Edit: ${itemName}`, url: undefined });
      }
      break;
    case 'profile':
      breadcrumbs.push({ label: 'Profile', url: undefined });
      break;
    case 'my-inventory':
      breadcrumbs.push({ label: 'My Inventory', url: undefined });
      break;
    case 'my-items':
      breadcrumbs.push({ label: 'My Items', url: undefined });
      break;
    case 'my-spreadsheet':
      breadcrumbs.push({ label: 'My Spreadsheet', url: undefined });
      break;
    case 'my-loan-history':
      breadcrumbs.push({ label: 'My Loan History', url: undefined });
      break;
    case 'browse':
      breadcrumbs.push({ label: 'Browse Items', url: undefined });
      break;
    case 'offer-hour':
      breadcrumbs.push({ label: 'Offer an Hour', url: undefined });
      break;
    case 'browse-hours':
      breadcrumbs.push({ label: 'Browse Hours', url: undefined });
      break;
    case 'ask-item':
      breadcrumbs.push({ label: 'Ask for Item', url: undefined });
      break;
    case 'messages':
      breadcrumbs.push({ label: 'Messages', url: undefined });
      break;
    default:
      // For other pages, format hyphenated route names properly
      breadcrumbs.push({ label: formatBreadcrumbLabel(pageType), url: undefined });
  }
  
  return breadcrumbs;
}

// Configure multer for file uploads - use memory storage for Image Canon compliance
const upload = multer({
  storage: multer.memoryStorage(),
  limits: {
    fileSize: 10 * 1024 * 1024, // 10MB limit
    files: 5 // Maximum 5 files per upload
  },
  fileFilter: (req, file, cb) => {
    // Accept all files - we'll filter them later in processing
    // This prevents multer from blocking the entire request for invalid files
    const hasValidMime = file.mimetype && file.mimetype.startsWith('image/');
    const hasValidExtension = file.originalname && imageProcessor.isValidImageFormat(file.originalname);
    
    if (!hasValidMime && !hasValidExtension) {
      console.warn(`Accepting but will skip invalid file: ${file.originalname}, MIME: ${file.mimetype}`);
    }
    
    // Always accept the file - let the processing logic handle validation
    cb(null, true);
  }
});

// Separate multer configuration for audio uploads - uses disk storage for Audio Canon compliance
const audioStorage = multer.diskStorage({
  destination: function (req, file, cb) {
    const uploadDir = 'temp/uploads';
    if (!existsSync(uploadDir)) {
      mkdirSync(uploadDir, { recursive: true });
    }
    cb(null, uploadDir);
  },
  filename: function (req, file, cb) {
    // Generate unique filename with proper extension for WebM files per Audio Canon
    const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9);
    const extension = file.originalname.endsWith('.webm') ? '.webm' : '.webm'; // Force WebM extension
    cb(null, uniqueSuffix + extension);
  }
});

const audioUpload = multer({ 
  storage: audioStorage,
  limits: {
    fileSize: 10 * 1024 * 1024 // 10MB limit
  },
  fileFilter: (req, file, cb) => {
    // Accept audio files, specifically WebM for Audio Canon compliance
    if (file.mimetype.startsWith('audio/') || file.mimetype === 'audio/webm') {
      cb(null, true);
    } else {
      console.warn(`Rejecting non-audio file: ${file.originalname}, MIME: ${file.mimetype}`);
      cb(new Error('Only audio files are allowed'), false);
    }
  }
});

export function registerTemplateRoutes(app: Express, storage: IStorage) {
  
  // Rewards and Points System Routes
  app.get('/rewards', async (req: Request, res: Response) => {
    try {
      const session = req.session as SessionData;
      const userId = session?.userId;
      
      if (!userId) {
        return res.status(401).send('Authentication required');
      }
      const [userPoints, rewards] = await Promise.all([
        storage.getUserPoints(userId),
        storage.getRewardsCatalog()
      ]);
      
      const currentUser = await storage.getUser(userId);
      
      const html = await templateEngine.render('rewards.html.twig', {
        currentUser,
        user: currentUser,
        breadcrumbs: generateBreadcrumbs('rewards', undefined, currentUser?.id),
        userPoints,
        rewards
      });
      
      res.send(html);
    } catch (error) {
      console.error('Error rendering rewards page:', error);
      res.status(500).send('Internal Server Error');
    }
  });

  app.post('/api/rewards/redeem', async (req: Request, res: Response) => {
    try {
      const session = req.session as SessionData;
      const userId = session?.userId;
      
      if (!userId) {
        return res.status(401).json({ error: 'Authentication required' });
      }
      const { rewardId } = req.body;
      
      const result = await storage.redeemReward(userId, rewardId);
      res.json(result);
    } catch (error) {
      console.error('Error redeeming reward:', error);
      res.status(500).json({ success: false, error: 'Failed to process redemption' });
    }
  });

  app.get('/rewards/history', async (req: Request, res: Response) => {
    try {
      const session = req.session as SessionData;
      const userId = session?.userId;
      
      if (!userId) {
        return res.status(401).send('Authentication required');
      }
      const [userPoints, transactions, redemptions] = await Promise.all([
        storage.getUserPoints(userId),
        storage.getPointsTransactions(userId),
        storage.getUserRedemptions(userId)
      ]);
      
      const currentUser = await storage.getUser(userId);
      
      const html = await templateEngine.render('rewards-history.html.twig', {
        currentUser,
        userPoints,
        transactions,
        redemptions
      });
      
      res.send(html);
    } catch (error) {
      console.error('Error rendering rewards history:', error);
      res.status(500).send('Internal Server Error');
    }
  });

  app.post('/admin/api/rewards', async (req: Request, res: Response) => {
    try {
      const rewardData = req.body;
      
      await pool.query(`
        INSERT INTO rewards_catalog (name, description, points_cost, category, sponsor_name, image_url, quantity_available)
        VALUES ($1, $2, $3, $4, $5, $6, $7)
      `, [
        rewardData.name,
        rewardData.description,
        parseInt(rewardData.points_cost),
        rewardData.category,
        rewardData.sponsor_name || null,
        rewardData.image_url || null,
        rewardData.quantity_available ? parseInt(rewardData.quantity_available) : null
      ]);
      
      res.json({ success: true });
    } catch (error) {
      console.error('Error adding reward:', error);
      res.status(500).json({ error: 'Failed to add reward' });
    }
  });

  app.get('/admin/rewards-catalog', async (req: Request, res: Response) => {
    try {
      const rewards = await storage.getRewardsCatalog();
      const html = await templateEngine.render('admin-rewards-catalog.html.twig', {
        rewards
      });
      res.send(html);
    } catch (error) {
      console.error('Error rendering rewards catalog:', error);
      res.status(500).send('Internal Server Error');
    }
  });

  // Lending Workflow Routes


  app.post('/api/loans/:id/initiate-return', async (req: Request, res: Response) => {
    try {
      const trackingId = parseInt(req.params.id);
      const session = req.session as SessionData;
      const userId = session?.userId;
      
      if (!userId) {
        return res.status(401).json({ error: 'Authentication required' });
      }
      
      await storage.initiateReturn(trackingId, userId);
      res.json({ success: true, message: 'Return initiated' });
    } catch (error) {
      console.error('Error initiating return:', error);
      res.status(500).json({ error: 'Server error' });
    }
  });

  app.post('/api/loans/:id/complete-return', async (req: Request, res: Response) => {
    try {
      const trackingId = parseInt(req.params.id);
      const { condition, notes } = req.body;
      const session = req.session as SessionData;
      const userId = session?.userId;
      
      if (!userId) {
        return res.status(401).json({ error: 'Authentication required' });
      }
      
      const success = await storage.completeReturn(trackingId, userId, condition, notes);
      
      if (success) {
        res.json({ success: true, message: 'Return completed and points awarded!' });
      } else {
        res.status(400).json({ error: 'Failed to complete return' });
      }
    } catch (error) {
      console.error('Error completing return:', error);
      res.status(500).json({ error: 'Server error' });
    }
  });

  // Push Return API - Owner declares item returned
  app.post('/api/loans/:id/push-return', async (req: Request, res: Response) => {
    const session = req.session as SessionData;
    if (!session?.userId) {
      return res.status(401).json({ success: false, error: 'Not authenticated' });
    }

    try {
      const loanId = parseInt(req.params.id);
      const { confirmed, hasConcerns, concernsMessage } = req.body;
      
      if (!confirmed) {
        return res.status(400).json({ success: false, error: 'Return confirmation required' });
      }

      const success = await storage.pushReturnLoan(loanId, session.userId, hasConcerns, concernsMessage);
      
      if (success) {
        res.json({ success: true, message: 'Item return confirmed successfully' });
      } else {
        res.status(400).json({ success: false, error: 'Failed to confirm return - loan not found or unauthorized' });
      }
    } catch (error) {
      console.error('Error pushing return:', error);
      res.status(500).json({ success: false, error: 'Server error' });
    }
  });

  app.get('/my-loans', async (req: Request, res: Response) => {
    try {
      // Get authenticated user ID from session
      const session = req.session as SessionData;
      const userId = session?.userId;
      
      if (!userId) {
        console.log('My loans page access rejected: User not authenticated');
        return res.redirect('/demo?message=Please log in to view your loans');
      }
      const activeLoans = await storage.getUserActiveLoans(userId);
      const currentUser = await storage.getUser(userId);
      
      const html = await templateEngine.render('my-loans.html.twig', {
        currentUser,
        user: currentUser,
        breadcrumbs: generateBreadcrumbs('my-loans', undefined, currentUser?.id),
        activeLoans
      });
      
      res.send(html);
    } catch (error) {
      console.error('Error rendering my loans page:', error);
      res.status(500).send('Internal Server Error');
    }
  });

  // Owner push-forward actions
  app.post('/api/loans/:id/push-forward', async (req: Request, res: Response) => {
    try {
      const trackingId = parseInt(req.params.id);
      const { action, notes } = req.body;
      
      // Get authenticated user ID from session
      const session = req.session as SessionData;
      const userId = session?.userId;
      
      if (!userId) {
        console.log('Push forward action rejected: User not authenticated');
        return res.status(401).json({ error: 'User not authenticated. Please log in to perform loan actions.' });
      }
      
      const result = await storage.pushForwardWorkflow(trackingId, userId, action, notes);
      res.json(result);
    } catch (error) {
      console.error('Error pushing forward workflow:', error);
      res.status(500).json({ success: false, message: 'Server error' });
    }
  });

  // QR Code Processor - Phone-friendly interface outside authentication
  app.get('/qr/:qrCode', async (req: Request, res: Response) => {
    try {
      const qrCode = req.params.qrCode;
      
      // Security: Require QR code access - prevent manual URL manipulation
      const userAgent = req.get('User-Agent') || '';
      
      // Block desktop browsers entirely - only allow mobile devices or QR access token
      const isMobileUserAgent = /Mobile|Android|iPhone|iPad|iPod|BlackBerry|Windows Phone/i.test(userAgent);
      const hasQRToken = req.query.qr_token === 'mobile_scan';
      
      // Strict security - block all non-mobile access
      if (!isMobileUserAgent && !hasQRToken) {
        const securityHTML = `
        <!DOCTYPE html>
        <html><head><title>QR Code Access Only</title><style>
        body { font-family: Arial, sans-serif; text-align: center; padding: 100px; background: #f5f5f5; }
        .security-notice { background: white; padding: 40px; border-radius: 10px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); max-width: 500px; margin: 0 auto; }
        .icon { font-size: 64px; margin-bottom: 20px; }
        </style></head><body>
        <div class="security-notice">
        <div class="icon">📱</div>
        <h2>QR Code Access Only</h2>
        <p>This page is designed exclusively for QR code scanning on mobile devices.</p>
        <p>To access item information, please scan the QR code using your phone's camera.</p>
        </div>
        </body></html>
        `;
        return res.status(403).send(securityHTML);
      }
      
      const item = await storage.getItemByQRCode(qrCode);
      
      if (!item) {
        const html = await templateEngine.render('qr-not-found.html.twig', { qrCode });
        return res.status(404).send(html);
      }

      // Get authenticated user ID from session (optional for this processor)
      const session = req.session as SessionData;
      const authUserId = session?.userId;
      let currentUser = null;
      let isLoggedIn = false;
      
      if (authUserId) {
        currentUser = await storage.getUser(authUserId);
        isLoggedIn = !!currentUser;
      }
      
      // Get owner information
      const owner = await storage.getUser(item.owner);
      const ownerDisplayName = owner?.showName || `${owner?.firstName || ''} ${owner?.lastName || ''}`.trim();
      const ownerPhone = owner?.phone || '';
      const ownerEmail = owner?.email || '';
      
      // Determine user role and permissions
      const isOwner = isLoggedIn && currentUser && currentUser.id === item.owner;
      const isBorrower = isLoggedIn && currentUser && item.borrowerId === currentUser.id;
      
      // Get any messages from the session
      const message = req.query.message as string;
      const messageType = req.query.messageType as string || 'success';
      
      const html = await templateEngine.render('qr-processor.html.twig', {
        item,
        qrCode,
        currentUser,
        isLoggedIn,
        isOwner,
        isBorrower,
        loanStatus: item.currentLoanStatus,
        ownerDisplayName,
        ownerPhone,
        ownerEmail,
        borrowerDisplayName: item.borrowerId ? (await storage.getUser(item.borrowerId))?.showName : null,
        message,
        messageType
      });
      
      res.send(html);
    } catch (error) {
      console.error('Error rendering QR processor:', error);
      res.status(500).send('QR Code Error');
    }
  });

  app.post('/api/qr/:qrCode/action', async (req: Request, res: Response) => {
    try {
      const qrCode = req.params.qrCode;
      const { action, notes } = req.body;
      const session = req.session as SessionData;
      const userId = session?.userId;
      
      if (!userId) {
        return res.status(401).json({ success: false, message: 'Authentication required' });
      }
      
      const result = await storage.updateItemStatusByQR(qrCode, action, userId, notes);
      res.json(result);
    } catch (error) {
      console.error('Error processing QR action:', error);
      res.status(500).json({ success: false, message: 'Server error' });
    }
  });

  app.get('/item/:id/qr', async (req: Request, res: Response) => {
    try {
      const itemId = parseInt(req.params.id);
      const qrData = await storage.getOrCreateItemQR(itemId);
      const item = await storage.getItem(itemId);
      const session = req.session as SessionData;
      const userId = session?.userId;
      
      if (!userId) {
        return res.status(401).send('Authentication required');
      }
      
      const currentUser = await storage.getUser(userId);
      
      const html = await templateEngine.render('item-qr-management.html.twig', {
        item,
        qrData,
        currentUser,
        printableQRUrl: `${req.protocol}://${req.get('host')}/qr/${qrData.qr_code}`
      });
      
      res.send(html);
    } catch (error) {
      console.error('Error rendering item QR management:', error);
      res.status(500).send('Internal Server Error');
    }
  });

  app.get('/workflow-demo', async (req: Request, res: Response) => {
    try {
      const html = await templateEngine.render('workflow-demo.html.twig', {});
      res.send(html);
    } catch (error) {
      console.error('Error rendering workflow demo:', error);
      res.status(500).send('Internal Server Error');
    }
  });

  app.get('/workflow-results', async (req: Request, res: Response) => {
    try {
      const html = await templateEngine.render('workflow-test-results.html.twig', {});
      res.send(html);
    } catch (error) {
      console.error('Error rendering workflow results:', error);
      res.status(500).send('Internal Server Error');
    }
  });

  app.get('/help', async (req: Request, res: Response) => {
    try {
      const html = await templateEngine.render('help-center.html.twig', {
        breadcrumbs: generateBreadcrumbs('help')
      });
      res.send(html);
    } catch (error) {
      console.error('Error rendering help center:', error);
      res.status(500).send('Internal Server Error');
    }
  });

  // My Inventory page - register early before catch-all
  app.get('/my-inventory/:userId', async (req: Request, res: Response) => {
    console.log('MY INVENTORY ROUTE HIT:', req.params.userId);
    try {
      const userId = parseInt(req.params.userId);
      const user = await storage.getUser(userId);
      
      if (!user) {
        console.log('User not found:', userId);
        return res.status(404).send('User not found');
      }
      console.log('Found user:', user.firstName);

      // Get all user's lendable items with full details
      const myItems = await storage.getUserItemsDetailed(userId);
      console.log('Found items:', myItems.length);
      
      // Get borrow requests for user's items (pending only)
      const borrowRequests = await storage.getBorrowRequestsByOwner(userId);
      
      // Get items user has borrowed (active loans)
      const borrowedItems = await storage.getActiveLoansByUser(userId);
      
      // Get user's items that are currently on loan (using unified system)
      const itemsOnLoan = await storage.getOwnerLoans(userId);
      
      // Get user's actual wish list items (request items they created with isRequest = true)
      const wishListResult = await db.select()
        .from(lendableItems)
        .leftJoin(lendableItemCategories, eq(lendableItems.category, lendableItemCategories.id))
        .where(and(
          eq(lendableItems.owner, userId),
          eq(lendableItems.isRequest, true)
        ))
        .orderBy(desc(lendableItems.addedOn));
      
      const wishListItems = wishListResult.map(row => ({
        id: row.lendable_items.id,
        name: row.lendable_items.name,
        description: row.lendable_items.description,
        categoryName: row.lendable_item_categories?.category || 'General',
        addedOn: row.lendable_items.addedOn,
        primaryLogo: row.lendable_items.primaryLogo
      }));
      
      // Get draft items
      const draftItems = await storage.getUserDraftItems(userId);
      
      // Get user's hour offerings
      const userHourOfferings = await storage.getUserHourOfferings(userId);
      
      // Get booked hour offerings (where user's offerings have been reserved)
      const bookedHourOfferings = []; // TODO: Implement booking system for hour offerings

      const html = await templateEngine.render('my-inventory.html.twig', {
        user,
        currentUser: user,
        breadcrumbs: generateBreadcrumbs('my-inventory', undefined, user?.id),
        myItems,
        borrowRequests,
        borrowedItems,
        itemsOnLoan,
        wishListItems,
        draftItems,
        userHourOfferings,
        bookedHourOfferings
      });
      
      res.send(html);
    } catch (error) {
      console.error('Error rendering my inventory page:', error);
      res.status(500).send('Error loading inventory page');
    }
  });

  // Content Management API - register early
  app.get('/api/admin/content', async (req: Request, res: Response) => {
    try {
      const contentBlocks = [
        {
          id: 1,
          identifier: 'privacy_notice_edit_profile',
          title: 'Privacy Notice',
          content: 'Your selfie photo will be stored securely and used only for profile display within our community.',
          contentType: 'text',
          parentPage: 'edit-profile',
          section: 'photo-upload',
          editPermission: 'admin',
          isActive: true,
          sortOrder: 1,
          version: 1,
          createdAt: new Date().toISOString(),
          updatedAt: new Date().toISOString(),
          createdBy: 1,
          updatedBy: 1
        },
        {
          id: 2,
          identifier: 'welcome_message_homepage', 
          title: 'Welcome Message',
          content: 'Welcome to LocalLendables - Share, Borrow, and Build Community Together!',
          contentType: 'text',
          parentPage: 'homepage',
          section: 'hero',
          editPermission: 'manager',
          isActive: true,
          sortOrder: 1,
          version: 2,
          createdAt: new Date().toISOString(),
          updatedAt: new Date().toISOString(),
          createdBy: 1,
          updatedBy: 1
        }
      ];
      
      console.log('Content API called - returning sample blocks');
      res.json(contentBlocks);
    } catch (error) {
      console.error('Error fetching content blocks:', error);
      res.status(500).json({ error: 'Failed to fetch content blocks' });
    }
  });
  
  // No authentication middleware - all routes are open

  // Home page - Original demo homepage with accurate avatars and Admin option
  app.get('/', async (req: Request, res: Response) => {
    console.log('Root route accessed - serving original demo homepage with accurate avatars');
    
    try {
      // Get demo users from database with their show_name
      const demoUsers = await storage.getDemoUsers();
      const html = await templateEngine.render('demo-homepage.html.twig', {
        demoUsers: demoUsers
      });
      res.send(html);
    } catch (error) {
      console.error('Error rendering demo homepage:', error);
      res.status(500).send('Internal Server Error');
    }
  });

  // Demo page - Our proper demo homepage with user grid
  app.get('/demo', async (req: Request, res: Response) => {
    try {
      console.log('Demo route accessed');
      // Get demo users from database with their show_name
      const demoUsers = await storage.getDemoUsers();
      const html = await templateEngine.render('demo-homepage.html.twig', {
        currentUser: null,
        demoUsers: demoUsers
      });
      console.log('Demo template rendered successfully');
      res.send(html);
    } catch (error) {
      console.error('Error rendering demo homepage:', error);
      res.status(500).send('Internal Server Error');
    }
  });

  // Items Simple - Optimized listing page using Image Canon thumbnails
  app.get('/items-simple', async (req: Request, res: Response) => {
    try {
      console.log('Items-simple route accessed');
      
      // Get all available items with thumbnail optimization
      const items = await storage.getAllAvailableItems(true);
      console.log(`Loading ${items.length} items with thumbnails for fast browsing`);
      
      const html = await templateEngine.render('items-simple.html.twig', {
        items
      });
      res.send(html);
    } catch (error) {
      console.error('Error rendering items-simple page:', error);
      res.status(500).send('Internal Server Error');
    }
  });

  // Items Basic - Basic listing page
  app.get('/items-basic', async (req: Request, res: Response) => {
    try {
      console.log('Items-basic route accessed');
      
      const items = await storage.getAllAvailableItems(true);
      console.log(`Loading ${items.length} items for basic view`);
      
      const html = await templateEngine.render('items-basic.html.twig', {
        items
      });
      res.send(html);
    } catch (error) {
      console.error('Error rendering items-basic page:', error);
      res.status(500).send('Internal Server Error');
    }
  });

  // Browse items page
  app.get('/browse', async (req: Request, res: Response) => {
    try {
      console.log('Browse route accessed');
      
      // Check if coming from item creation (newest should be first)
      const showNewest = req.query.newest === 'true';
      
      // Get all available items with randomization control
      let items = await storage.getAllAvailableItems(!showNewest);
      console.log(`Available items found: ${items.length}`);
      
      // Get hour offerings (limit to 2 per user for recurring offerings)
      const allHourOfferings = await storage.getAvailableHourOfferings();
      const activeHourOfferings = allHourOfferings.filter(offer => 
        offer.isActive && 
        new Date(offer.offerDate) >= new Date()
      );
      
      // Limit recurring offerings to 2 per user
      const userOfferingCounts = new Map();
      const limitedHourOfferings = activeHourOfferings.filter(offer => {
        if (offer.isRecurring) {
          const count = userOfferingCounts.get(offer.userId) || 0;
          if (count >= 2) return false;
          userOfferingCounts.set(offer.userId, count + 1);
        }
        return true;
      });
      
      console.log(`Active hour offerings found: ${limitedHourOfferings.length}`);
      
      // Get total items count for stats (unchanged by filters)
      const totalItems = items.length;
      
      // Get only categories that have items
      const itemCategoryIds = [...new Set(items.map(item => item.category))];
      const allCategoryObjects = await storage.getAllCategories();
      const categoriesWithItems = allCategoryObjects.filter(cat => itemCategoryIds.includes(cat.id));
      const categories = categoriesWithItems.map(cat => cat.category);
      
      // Add special categories
      categories.push('Requests', 'Hours Offered');
      
      // Get current user for personalized filters - maintain session from referrer
      const session = req.session as SessionData;
      let currentUser = null;
      let isAdmin = false;
      
      // Check for user ID in referrer URL (from dashboard)
      const referrer = req.get('Referrer') || '';
      const dashboardMatch = referrer.match(/\/dashboard\/(\d+)/);
      
      if (session?.userId) {
        currentUser = await storage.getUser(session.userId);
        isAdmin = await storage.isUserAdmin(session.userId);
      } else if (dashboardMatch) {
        // Extract user ID from dashboard referrer and establish session
        const userId = parseInt(dashboardMatch[1]);
        currentUser = await storage.getUser(userId);
        if (currentUser) {
          session.userId = userId;
          session.username = currentUser.email;
          isAdmin = await storage.isUserAdmin(userId);
          console.log(`Established session for user ${userId} from dashboard referrer`);
        }
      } else {
        // Fallback to default demo user if no session exists (for demo purposes)
        const defaultUserId = 1; // Admin user as fallback
        currentUser = await storage.getUser(defaultUserId);
        if (currentUser) {
          session.userId = defaultUserId;
          session.username = currentUser.email;
          isAdmin = await storage.isUserAdmin(defaultUserId);
          console.log(`Using default demo user ${defaultUserId} for session`);
        }
      }
      
      // Check for user parameter in URL
      const userParam = req.query.user as string;
      if (!currentUser && userParam) {
        const userId = parseInt(userParam);
        if (!isNaN(userId)) {
          currentUser = await storage.getUser(userId);
          if (currentUser) {
            session.userId = userId;
            session.username = currentUser.email;
            isAdmin = await storage.isUserAdmin(userId);
            console.log(`Established session for user ${userId} from URL parameter`);
          }
        }
      }
      
      // Check for favorites parameter - require authentication
      const favoritesParam = req.query.favorites as string;
      if (!currentUser && favoritesParam === 'true') {
        // Require proper authentication for favorites view
        console.log('Favorites view requested but user not authenticated - redirecting to login');
        return res.redirect('/demo?message=Please log in to view your favorites');
      }

      // Apply special filters first
      const filterType = req.query.filter as string || (favoritesParam === 'true' ? 'my-favorites' : null);
      if (filterType && currentUser) {
        if (filterType === 'my-chats') {
          // Filter to items the user has active conversations about
          const allConversations = await storage.getAllConversations();
          const userConversations = allConversations.filter(conv => 
            conv.ownerId === currentUser.id || 
            conv.borrowerId === currentUser.id
          );
          const chatItemIds = userConversations.map(conv => conv.itemId).filter(id => id);
          items = items.filter(item => chatItemIds.includes(item.id));
        } else if (filterType === 'my-favorites') {
          const userFavorites = await storage.getUserFavorites(currentUser.id);
          const favoriteItemIds = userFavorites.map(fav => fav.id);
          items = items.filter(item => favoriteItemIds.includes(item.id));
        }
      }

      // Apply category filter if specified
      const categoryFilter = req.query.category as string;
      let filteredHourOfferings = limitedHourOfferings;
      
      if (categoryFilter && categoryFilter !== 'all') {
        // Decode URL-encoded category names (handles & characters)
        const decodedCategoryFilter = decodeURIComponent(categoryFilter);
        
        if (decodedCategoryFilter === 'Requests') {
          items = items.filter(item => item.isRequest);
          filteredHourOfferings = []; // No hour offerings for requests filter
        } else if (decodedCategoryFilter === 'Hours Offered') {
          items = []; // No items for hours filter
          // Keep all hour offerings
        } else {
          items = items.filter(item => item.categoryName === decodedCategoryFilter);
          filteredHourOfferings = []; // No hour offerings for regular category filters
        }
      }
      
      // Apply sorting
      const sortBy = req.query.sort as string || 'random';
      if (sortBy === 'alphabetic') {
        items.sort((a, b) => a.name.localeCompare(b.name));
      } else if (sortBy === 'newest') {
        // Sort by ID in descending order (newest/highest ID first)
        items.sort((a, b) => b.id - a.id);
      } else if (sortBy === 'owner') {
        items.sort((a, b) => {
          const aOwner = a.ownerShowName || a.ownerFirstName;
          const bOwner = b.ownerShowName || b.ownerFirstName;
          return aOwner.localeCompare(bOwner);
        });
      }
      // random is default - no additional sorting needed
      
      // Get total members count
      const allUsers = await storage.getAllUsers();
      

      
      const breadcrumbs = generateBreadcrumbs('browse', undefined, currentUser?.id);
      
      const html = await templateEngine.render('browse.html.twig', {
        items: items,
        hourOfferings: filteredHourOfferings,
        categories: categories,
        selectedCategory: categoryFilter ? decodeURIComponent(categoryFilter) : 'all',
        selectedSort: sortBy,
        selectedFilter: filterType || 'all',
        totalMembers: allUsers.length,
        totalItems: totalItems,
        currentUser: currentUser,
        user: currentUser, // Add user variable for common header
        breadcrumbs: breadcrumbs,
        isAdmin: isAdmin
      });
      
      res.send(html);
    } catch (error) {
      console.error('Error rendering browse page:', error);
      res.status(500).send('Internal Server Error');
    }
  });

  // API routes for favorites - Toggle functionality
  app.post('/api/favorites/:itemId', async (req: Request, res: Response) => {
    try {
      const itemId = parseInt(req.params.itemId);
      const userId = parseInt(req.query.userId as string);
      
      if (!userId || !itemId) {
        return res.status(400).json({ error: 'Missing userId or itemId' });
      }

      // Check if already favorited
      const isFavorited = await storage.isItemFavorited(userId, itemId);
      
      let success = false;
      let favorited = false;
      
      if (isFavorited) {
        // Remove from favorites
        success = await storage.removeFromFavorites(userId, itemId);
        favorited = false;
      } else {
        // Add to favorites
        success = await storage.addToFavorites(userId, itemId);
        favorited = true;
      }
      
      if (success) {
        res.json({ success: true, favorited: favorited });
      } else {
        res.status(500).json({ error: 'Failed to toggle favorite' });
      }
    } catch (error) {
      console.error('Error toggling favorite:', error);
      res.status(500).json({ error: 'Internal server error' });
    }
  });

  app.delete('/api/favorites/:itemId', async (req: Request, res: Response) => {
    try {
      const itemId = parseInt(req.params.itemId);
      const userId = parseInt(req.query.userId as string);
      
      if (!userId || !itemId) {
        return res.status(400).json({ error: 'Missing userId or itemId' });
      }

      const success = await storage.removeFromFavorites(userId, itemId);
      if (success) {
        res.json({ success: true, favorited: false });
      } else {
        res.status(500).json({ error: 'Failed to remove from favorites' });
      }
    } catch (error) {
      console.error('Error removing from favorites:', error);
      res.status(500).json({ error: 'Internal server error' });
    }
  });

  app.get('/api/favorites/check/:itemId', async (req: Request, res: Response) => {
    try {
      const itemId = parseInt(req.params.itemId);
      const userId = parseInt(req.query.userId as string);
      
      if (!userId || !itemId) {
        return res.status(400).json({ error: 'Missing userId or itemId' });
      }

      const isFavorited = await storage.isItemFavorited(userId, itemId);
      res.json({ favorited: isFavorited });
    } catch (error) {
      console.error('Error checking favorite status:', error);
      res.status(500).json({ error: 'Internal server error' });
    }
  });

  // Handle image requests that might match dashboard pattern
  app.get('/dashboard/*', (req: Request, res: Response, next: NextFunction) => {
    // If the request is for an image file (contains file extension), skip to next handler
    if (req.path.includes('.')) {
      return res.status(404).send('Not found');
    }
    next();
  });

  // Dashboard route with user ID parameter
  app.get('/dashboard/:id', async (req: Request, res: Response) => {
    try {
      const userId = parseInt(req.params.id);
      console.log(`Dashboard accessed for user ID: ${userId}`);
      
      // Validate user ID is reasonable (positive integer)
      if (isNaN(userId) || userId <= 0) {
        return res.status(400).send('Invalid user ID');
      }
      
      // Auto-login for demo users
      const session = req.session as SessionData;
      if (!session.userId || session.userId !== userId) {
        session.userId = userId;
        const user = await storage.getUser(userId);
        if (user) {
          session.username = user.email;
        }
      }
      
      const user = await storage.getUser(userId);
      if (!user) {
        return res.status(404).send('User not found');
      }

      console.log(`Found user: ${user.showName || `${user.firstName} ${user.lastName}`.trim() || user.email} (ID: ${user.id})`);
      
      // No session - authentication cancelled
      
      // Get user's items
      const userItemsRaw = await storage.getUserItems(userId);
      console.log(`Found ${userItemsRaw.length} items for user ${user.firstName}: ${userItemsRaw.map(item => `'${item.name}'`)}`);
      console.log(`Current user ID: ${user.id}, User items owner values:`, userItemsRaw.map(item => ({ name: item.name, owner: item.owner })));
      
      // Filter items to only include actual lendable items (not "Ask For" requests)
      const userItems = userItemsRaw.filter(item => !item.isRequest);
      const requestItems = userItemsRaw.filter(item => item.isRequest);
      console.log(`Template data - currentUser.id: ${user.id}, userItems: ${userItems.length}, requestItems: ${requestItems.length}`);
      console.log(`Request items:`, requestItems.map(item => ({ name: item.name, isRequest: item.isRequest })));
      
      // Get user's favorites
      const userFavorites = await storage.getUserFavorites(userId);
      
      // Get new listings (last 10 in random order) for dashboard "New Listings" section
      const allItems = await storage.getNewListings(10);
      console.log(`New listings found: ${allItems.length}`);
      
      // Get total count of all available items for Browse button
      let totalItemCount = 0;
      try {
        const allAvailableItems = await storage.getAllAvailableItems();
        totalItemCount = allAvailableItems.length;
        console.log(`Total available items: ${totalItemCount}`);
      } catch (error) {
        console.error('Error fetching total available items:', error);
        totalItemCount = 0;
      }
      
      // Get user stats
      const stats = await storage.getUserStats(userId);
      const recentActivity = await storage.getRecentActivity(userId);
      
      // Check if user is admin
      const isAdmin = await storage.isUserAdmin(userId);
      
      // Generate time-sensitive notifications and clean up old ones
      await storage.generateLoanReturnNotifications();
      await storage.cleanupOldNotifications();
      
      // Get system notifications for blue bar display (only show most recent unread)
      const systemNotifications = await storage.getSystemNotifications(userId);
      const unreadSystemNotifications = systemNotifications.filter(n => !n.isRead && !n.isDismissed && 
        (n.type.startsWith('return_') || n.type.startsWith('owner_') || n.type === 'agreement_ready'));
      
      console.log(`Found ${systemNotifications.length} total notifications, ${unreadSystemNotifications.length} unread/undismissed for user ${userId}`);
      
      // Sort by priority: agreement_ready > overdue > due_today > due_tomorrow, then by date
      const activeSystemNotification = unreadSystemNotifications.sort((a, b) => {
        const priorityOrder = { 'agreement_ready': 4, 'overdue': 3, 'due_today': 2, 'due_tomorrow': 1 };
        const aPriority = a.type === 'agreement_ready' ? 4 : (priorityOrder[a.type.split('_').slice(-1)[0]] || 0);
        const bPriority = b.type === 'agreement_ready' ? 4 : (priorityOrder[b.type.split('_').slice(-1)[0]] || 0);
        
        if (aPriority !== bPriority) return bPriority - aPriority;
        return new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime();
      })[0];
      
      console.log('Active system notification for template:', activeSystemNotification ? 
        `ID: ${activeSystemNotification.id}, Message: ${activeSystemNotification.message}` : 'None');
      
      // Get unread chat data for Messages button
      const unreadChatData = await storage.getUserUnreadChatData(userId);
      console.log('Unread chat data for user', userId, ':', unreadChatData);
      
      // Get user's upcoming hour offerings count - count unique DAYS with offerings, not total hours
      const userHourOfferings = await storage.getUserHourOfferings(userId);
      const activeUpcomingOfferings = userHourOfferings.filter(offering => 
        offering.isActive && 
        !offering.isBooked && 
        new Date(offering.offerDate) >= new Date()
      );
      
      // Count unique days with offerings (since only one offering occurs per day within multi-hour windows)
      const uniqueDates = new Set(
        activeUpcomingOfferings.map(offering => 
          new Date(offering.offerDate).toDateString()
        )
      );
      const userHourOfferingsCount = uniqueDates.size;
      
      // Calculate total unread messages (chat + system notifications)
      const unreadSystemCount = systemNotifications.filter(n => !n.isRead).length;
      const totalUnreadMessages = unreadChatData.unreadChatCount + unreadSystemCount;
      
      const html = await templateEngine.render('dashboard.html.twig', {
        user: user,
        currentUser: user,
        breadcrumbs: [], // No breadcrumbs for dashboard since we use custom header
        userItems: userItems,
        allUserItems: userItemsRaw,
        allItems: allItems,
        totalItemCount: totalItemCount,
        userFavorites: userFavorites,
        stats: stats,
        recentActivity: recentActivity,
        isAdmin: isAdmin,
        hasUnreadChats: unreadChatData.hasUnreadChats,
        unreadChatCount: unreadChatData.unreadChatCount,
        systemNotification: activeSystemNotification,
        userHourOfferingsCount: userHourOfferingsCount,
        session: req.session
      });
      
      // Add cache-busting headers to ensure fresh template loading
      res.set({
        'Cache-Control': 'no-cache, no-store, must-revalidate',
        'Pragma': 'no-cache',
        'Expires': '0'
      });
      
      res.send(html);
    } catch (error) {
      console.error('Error rendering dashboard:', error);
      res.status(500).send('Internal Server Error');
    }
  });

  // Direct Mac dashboard route
  app.get('/mac-dashboard', async (req: Request, res: Response) => {
    try {
      console.log("Mac's dashboard accessed for user: Allyson McFarland (ID: 38)");
      
      const user = await storage.getUser(38); // Allyson McFarland (Mac)
      if (!user) {
        return res.status(404).send('Mac not found');
      }
      
      // No session - authentication cancelled
      
      // Get Mac's items
      const userItems = await storage.getUserItems(38);
      console.log(`Found ${userItems.length} items for user Allyson: ${userItems.map(item => `'${item.name}'`)}`);
      
      // Get new listings (last 10 in random order) for dashboard "New Listings" section
      const allItems = await storage.getNewListings(10);
      console.log(`New listings found: ${allItems.length}`);
      
      // Get user stats
      const stats = await storage.getUserStats(38);
      const recentActivity = await storage.getRecentActivity(38);
      
      // Check if user is admin
      const isAdmin = await storage.isUserAdmin(38);
      
      // Get unread chat data for Messages button
      const unreadChatData = await storage.getUserUnreadChatData(38);
      
      const html = await templateEngine.render('dashboard.html.twig', {
        user: user,
        currentUser: user,
        breadcrumbs: [], // No breadcrumbs for dashboard since we use custom header
        userItems: userItems,
        allUserItems: userItemsRaw,
        allItems: allItems,
        stats: stats,
        recentActivity: recentActivity,
        isAdmin: isAdmin,
        hasUnreadChats: unreadChatData.hasUnreadChats,
        unreadChatCount: unreadChatData.unreadChatCount,
        session: req.session
      });
      
      // Add cache-busting headers to ensure fresh template loading
      res.set({
        'Cache-Control': 'no-cache, no-store, must-revalidate',
        'Pragma': 'no-cache',
        'Expires': '0'
      });
      
      res.send(html);
    } catch (error) {
      console.error('Error rendering dashboard:', error);
      res.status(500).send('Internal Server Error');
    }
  });

  // Item detail page
  app.get('/item/:id', async (req: Request, res: Response) => {
    try {
      const itemId = parseInt(req.params.id);
      const item = await storage.getItem(itemId);
      
      if (!item) {
        return res.status(404).send('Item not found');
      }
      
      // Debug item image data
      console.log(`Item ${itemId} image data:`, {
        primaryLogo: item.primaryLogo,
        extraImages: item.extraImages
      });
      
      // Check loan status
      const loanStatus = await storage.getItemLoanStatus(itemId);
      
      // Increment view count
      await storage.incrementItemViews(itemId);
      
      // Check if current user can edit this item (for showing edit/delete buttons)
      const session = req.session as SessionData;
      let canEdit = false;
      let currentUser = null;
      let isAdmin = false;
      let existingConversation = null;
      
      if (session?.userId) {
        currentUser = await storage.getUser(session.userId);
        canEdit = await storage.canUserEditItem(session.userId, itemId);
        isAdmin = await storage.isUserAdmin(session.userId);
      } else {
        // Require proper session - no fallback users
        return res.status(401).send('Authentication required. Please log in from the dashboard.');
      }
      
      // Check for existing conversation involving current user and this item
      if (currentUser) {
        try {
          // First check if user is borrower (traditional case)
          if (item.owner !== currentUser.id) {
            existingConversation = await storage.getConversation(itemId, item.owner, currentUser.id);
          }
          
          // If no conversation found and user IS the owner, check if they have conversations as owner
          if (!existingConversation && item.owner === currentUser.id) {
            // Get all conversations for this item where user is the owner
            const ownerConversations = await storage.getConversationsForItem(itemId, currentUser.id);
            if (ownerConversations && ownerConversations.length > 0) {
              // Use the most recent conversation with messages
              existingConversation = ownerConversations.find(conv => conv.messageCount > 0) || ownerConversations[0];
            }
          }
          
          // If we found a conversation, get full details including participant info
          if (existingConversation) {
            existingConversation = await storage.getConversationById(existingConversation.id);
          }
        } catch (error) {
          console.log('No existing conversation found or error checking:', error);
        }
      }

      // Check if current item is favorited by the user
      let isFavorited = false;
      if (currentUser) {
        isFavorited = await storage.isItemFavorited(currentUser.id, itemId);
      }

      // Check if item has a signed agreement
      const signedAgreement = await storage.getSignedAgreementForItem(itemId);

      const html = await templateEngine.render('item-detail.html.twig', {
        item: item,
        loanStatus: loanStatus,
        canEdit: canEdit,
        currentUser: currentUser,
        user: currentUser, // Add user for header template
        breadcrumbs: generateBreadcrumbs('item-detail', item?.name, currentUser?.id),
        isAdmin: isAdmin,
        session: req.session,
        existingConversation: existingConversation, // Add for auto-displaying chat
        isFavorited: isFavorited, // Add favorite status
        signedAgreement: signedAgreement // Add signed agreement info
      });
      
      res.send(html);
    } catch (error) {
      console.error('Error rendering item detail:', error);
      res.status(500).send('Internal Server Error');
    }
  });

  // Profile page
  app.get('/profile/:id', async (req: Request, res: Response) => {
    try {
      const userId = parseInt(req.params.id);
      
      // Auto-login for demo users
      const session = req.session as SessionData;
      if (!session.userId || session.userId !== userId) {
        session.userId = userId;
        const user = await storage.getUser(userId);
        if (user) {
          session.username = user.email;
        }
      }
      
      const user = await storage.getUser(userId);
      
      if (!user) {
        return res.status(404).send('User not found');
      }
      
      // Get user's items
      const userItems = await storage.getUserItems(userId);
      
      // Get user stats
      const stats = await storage.getUserStats(userId);
      
      // Get user preferences including bio
      const userPreferences = await storage.getUserPreferences(userId);
      
      const html = await templateEngine.render('profile.html.twig', {
        profileUser: user,
        user: user, // Also pass as 'user' for header template
        currentUser: user,
        breadcrumbs: generateBreadcrumbs('profile', undefined, user?.id),
        userItems: userItems,
        stats: stats,
        userPreferences: userPreferences,
        session: req.session
      });
      
      res.send(html);
    } catch (error) {
      console.error('Error rendering profile:', error);
      res.status(500).send('Internal Server Error');
    }
  });

  // My Spreadsheet route
  app.get('/my-spreadsheet/:id', async (req: Request, res: Response) => {
    try {
      const userId = parseInt(req.params.id);
      const session = req.session as SessionData;
      
      // Check authorization
      if (!session?.userId || session.userId !== userId) {
        return res.redirect('/login');
      }
      
      console.log(`Accessing My Spreadsheet for user ${userId}`);
      
      // Get user data
      const user = await storage.getUser(userId);
      if (!user) {
        return res.status(404).send('User not found');
      }
      
      // Get comprehensive spreadsheet data
      const spreadsheetData = await storage.getUserSpreadsheetData(userId);
      
      console.log(`Spreadsheet data loaded - My Items: ${spreadsheetData.myItems.length}, Borrowed: ${spreadsheetData.borrowedItems.length}, Wish List: ${spreadsheetData.wishListItems.length}, Hours: ${spreadsheetData.offeredHours.length}, Inactive: ${spreadsheetData.inactiveItems?.length || 0}`);
      
      const html = await templateEngine.render('my-spreadsheet.html.twig', {
        user: user,
        currentUser: user,
        breadcrumbs: generateBreadcrumbs('my-spreadsheet', undefined, user?.id),
        myItems: spreadsheetData.myItems,
        borrowedItems: spreadsheetData.borrowedItems,
        wishListItems: spreadsheetData.wishListItems,
        offeredHours: spreadsheetData.offeredHours,
        inactiveItems: spreadsheetData.inactiveItems,
        session: req.session
      });
      
      res.send(html);
    } catch (error) {
      console.error('Error rendering My Spreadsheet:', error);
      res.status(500).send('Internal Server Error');
    }
  });

  // My Loan History route
  app.get('/my-loan-history/:id', async (req: Request, res: Response) => {
    try {
      const userId = parseInt(req.params.id);
      const session = req.session as SessionData;
      
      // Check authorization
      if (!session?.userId || session.userId !== userId) {
        return res.redirect('/login');
      }
      
      console.log(`Accessing My Loan History for user ${userId}`);
      
      // Get user data
      const user = await storage.getUser(userId);
      if (!user) {
        return res.status(404).send('User not found');
      }
      
      // Get historical loan data
      const loanHistoryData = await storage.getUserLoanHistoryData(userId);
      
      console.log(`Loan history data loaded - Loaned: ${loanHistoryData.loanedItems.length}, Borrowed: ${loanHistoryData.borrowedItems.length}`);
      console.log('TEMPLATE DEBUG - Borrowed items data:', JSON.stringify(loanHistoryData.borrowedItems.map(item => ({
        id: item.id,
        name: item.name,
        borrowedDate: item.borrowedDate,
        returnedDate: item.returnedDate,
        borrowedDays: item.borrowedDays
      })), null, 2));
      
      const html = await templateEngine.render('my-loan-history.html.twig', {
        user: user,
        currentUser: user,
        breadcrumbs: generateBreadcrumbs('my-loan-history', undefined, user?.id),
        loanedItems: loanHistoryData.loanedItems,
        borrowedItems: loanHistoryData.borrowedItems,
        session: req.session
      });
      
      res.send(html);
    } catch (error) {
      console.error('Error rendering My Loan History:', error);
      res.status(500).send('Internal Server Error');
    }
  });

  // Profile activity API
  app.get('/api/profile/:id/activity', async (req: Request, res: Response) => {
    try {
      const userId = parseInt(req.params.id);
      const activity = await storage.getUserRecentActivity(userId);
      
      res.json({ activity });
    } catch (error) {
      console.error('Error fetching profile activity:', error);
      res.status(500).json({ error: 'Failed to load activity' });
    }
  });

  // Profile borrowing history API
  app.get('/api/profile/:id/borrowing', async (req: Request, res: Response) => {
    try {
      const userId = parseInt(req.params.id);
      const session = req.session as SessionData;
      
      // Only allow users to see their own borrowing history
      if (!session?.userId || session.userId !== userId) {
        return res.status(403).json({ error: 'Access denied' });
      }
      
      const history = await storage.getUserBorrowingHistory(userId);
      
      res.json({ history });
    } catch (error) {
      console.error('Error fetching borrowing history:', error);
      res.status(500).json({ error: 'Failed to load borrowing history' });
    }
  });

  // Login page with demo user support
  app.get('/login', async (req: Request, res: Response) => {
    try {
      // Handle demo user authentication
      const demoUser = req.query.demo as string;
      if (demoUser) {
        console.log(`Demo login requested for: ${demoUser}`);
        
        // Map demo usernames to user IDs for direct dashboard access
        const demoUserMap: Record<string, number> = {
          'jacob.lowery': 43,
          'kiley.ford': 36,
          'brianna.jenkins': 32,
          'chloe.trevino': 42,
          'allyson.mcfarland': 38,
          'morgan.gilbert': 33,
          'josh.mcintosh': 44,
          'nora.rush': 41,
          'patricia.burton': 35,
          'adrien.mahoney': 34,
          'mariana.rivas': 40,
          'jaelyn.oneal': 39,
          'adelyn.griffin': 37,
          'dilly.admin': 1 // Admin user
        };
        
        const userId = demoUserMap[demoUser];
        if (userId) {
          console.log(`Redirecting demo user ${demoUser} to dashboard/${userId}`);
          
          // Set up session for demo user
          const session = req.session as SessionData;
          session.userId = userId;
          session.username = demoUser;
          
          // Direct redirect to dashboard (bypass template issues)
          console.log(`Direct redirect to /dashboard/${userId}`);
          return res.redirect(`/dashboard/${userId}`);
        } else {
          console.log(`Demo user ${demoUser} not found, pre-filling login form`);
        }
      }
      
      // Pre-fill login form for demo users
      const demoEmail = demoUser ? `${demoUser}@ruralcarpool.com` : '';
      const demoPassword = demoUser ? 'dummypass123' : '';
      
      const html = await templateEngine.render('login.html.twig', {
        demoEmail,
        demoPassword,
        isDemoUser: !!demoUser
      });
      res.send(html);
    } catch (error) {
      console.error('Error rendering login page:', error);
      res.status(500).send('Internal Server Error');
    }
  });

  // Login POST handler
  app.post('/login', async (req: Request, res: Response) => {
    try {
      const { email, password } = req.body;
      const user = await storage.authenticateUser(email, password);
      
      if (user) {
        // No session - authentication cancelled
        res.redirect('/dashboard');
      } else {
        const html = await templateEngine.render('login.html.twig', {
          error: 'Invalid credentials'
        });
        res.send(html);
      }
    } catch (error) {
      console.error('Error during login:', error);
      res.status(500).send('Internal Server Error');
    }
  });

  // Logout handler - no session to destroy
  app.post('/logout', async (req: Request, res: Response) => {
    res.redirect('/demo');
  });

  app.get('/logout', async (req: Request, res: Response) => {
    // Clear any session data and prevent back button access
    res.set({
      'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0, private',
      'Pragma': 'no-cache',
      'Expires': '0'
    });
    
    // Send logout confirmation page with script to prevent back navigation
    const html = `
      <!DOCTYPE html>
      <html lang="en">
      <head>
          <meta charset="UTF-8">
          <meta name="viewport" content="width=device-width, initial-scale=1.0">
          <title>Logged Out - Local Lendables</title>
          <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
          <style>
              body {
                  background: linear-gradient(135deg, #dc3545 0%, #6c757d 100%);
                  min-height: 100vh;
                  display: flex;
                  align-items: center;
                  justify-content: center;
              }
              .logout-card {
                  background: white;
                  border-radius: 20px;
                  padding: 3rem;
                  text-align: center;
                  max-width: 400px;
                  box-shadow: 0 15px 35px rgba(0,0,0,0.1);
              }
          </style>
          <script>
              // Prevent back button after logout
              history.pushState(null, null, location.href);
              window.onpopstate = function () {
                  history.go(1);
              };
              
              // Auto-redirect after 3 seconds
              setTimeout(function() {
                  window.location.replace('/demo');
              }, 3000);
          </script>
      </head>
      <body>
          <div class="logout-card">
              <i class="fas fa-sign-out-alt text-danger mb-3" style="font-size: 3rem;"></i>
              <h2>Successfully Logged Out</h2>
              <p class="text-muted">You have been safely logged out of Local Lendables Demo.</p>
              <p class="small">Redirecting to homepage in 3 seconds...</p>
              <a href="/demo" class="btn btn-primary mt-2">Return to Demo</a>
          </div>
          <script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/js/all.min.js"></script>
      </body>
      </html>
    `;
    
    res.send(html);
  });

  // Create item page
  app.get('/create-item', async (req: Request, res: Response) => {
    const session = req.session as SessionData;
    if (!session?.userId) {
      return res.redirect('/login');
    }

    try {
      const user = await storage.getUser(session.userId);
      
      if (!user) {
        return res.redirect('/login');
      }
      
      // Get categories from database
      const categories = await storage.getAllCategories();

      const html = await templateEngine.render('create-item.html.twig', { 
        user,
        currentUser: user,
        breadcrumbs: generateBreadcrumbs('create-item', undefined, user?.id),
        userAvatar: await getUserAvatarPath(user),
        categories: categories
      });
      res.send(html);
    } catch (error) {
      console.error('Error rendering create item page:', error);
      res.status(500).send('Internal Server Error');
    }
  });

  // Ask for item page
  app.get('/ask-item', async (req: Request, res: Response) => {
    try {
      // For demo purposes, use a default user if no session
      const session = req.session as SessionData;
      let currentUser = null;
      
      if (session?.userId) {
        currentUser = await storage.getUser(session.userId);
      } else {
        // Require authentication for Ask Item page
        return res.redirect('/demo?message=Please log in to ask for items');
      }
      
      if (!currentUser) {
        return res.status(404).send('User not found');
      }
      
      // Get categories from database
      const categories = await storage.getAllCategories();

      const html = await templateEngine.render('ask-item.html.twig', { 
        user: currentUser,
        currentUser: currentUser,
        breadcrumbs: generateBreadcrumbs('ask-item', undefined, currentUser?.id),
        categories
      });
      res.send(html);
    } catch (error) {
      console.error('Error rendering ask item page:', error);
      res.status(500).send('Internal Server Error');
    }
  });

  // Offer an Hour page - create new hour offering
  app.get('/offer-hour', async (req: Request, res: Response) => {
    try {
      const session = req.session as SessionData;
      let currentUser = null;
      
      console.log('Offer Hour page - Session data:', session);
      
      if (session?.userId) {
        currentUser = await storage.getUser(session.userId);
        console.log('Offer Hour page - Found user from session:', currentUser?.id, currentUser?.showName);
      } else {
        // Require authentication for Offer Hour page
        console.log('No session found, redirecting to login');
        return res.redirect('/demo?message=Please log in to offer hours');
      }
      
      if (!currentUser) {
        console.log('No user found, redirecting to home');
        return res.redirect('/');
      }

      console.log('Offer Hour page - Final currentUser for template:', {
        id: currentUser.id,
        showName: currentUser.showName,
        avatar: currentUser.avatar,
        firstName: currentUser.firstName
      });

      const html = await templateEngine.render('offer-hour.html.twig', { 
        title: 'Offer an Hour of Your Time',
        user: currentUser,
        currentUser: currentUser,
        breadcrumbs: generateBreadcrumbs('offer-hour', undefined, currentUser?.id)
      });
      res.send(html);
    } catch (error) {
      console.error('Error rendering offer hour page:', error);
      res.status(500).send('Internal Server Error');
    }
  });

  // Browse Hours page - view available hour offerings
  app.get('/browse-hours', async (req: Request, res: Response) => {
    try {
      const userId = req.session.userId;
      const currentUser = userId ? await storage.getUser(userId) : null;

      // Get all available hour offerings
      const hourOfferings = await storage.getAvailableHourOfferings();

      const html = await templateEngine.render('browse-hours.html.twig', { 
        title: 'Browse Available Hours',
        user: currentUser,
        currentUser: currentUser,
        breadcrumbs: generateBreadcrumbs('browse-hours', undefined, currentUser?.id),
        hourOfferings: hourOfferings
      });
      res.send(html);
    } catch (error) {
      console.error('Error rendering browse hours page:', error);
      res.status(500).send('Internal Server Error');
    }
  });

  // My Items page - generic listing with admin-editable content
  app.get('/my-items/:userId/:type', async (req: Request, res: Response) => {
    try {
      const userId = parseInt(req.params.userId);
      const type = req.params.type; // lendable, borrowed, loaned, drafts
      
      if (isNaN(userId)) {
        return res.status(400).send('Invalid user ID');
      }

      // Get the user
      const user = await storage.getUser(userId);
      if (!user) {
        return res.status(404).send('User not found');
      }

      // Get content blocks for this page type
      const titleBlock = await storage.getContentBlock(`my_items_${type}_title`);
      const descriptionBlock = await storage.getContentBlock(`my_items_${type}_description`);
      
      const pageTitle = titleBlock?.content || 'My Items';
      const pageDescription = descriptionBlock?.content || '';

      let items: any[] = [];
      let canEdit = false;
      let showCreateButton = false;
      let emptyStateTitle = 'No Items Found';
      let emptyStateDescription = 'No items match your current criteria.';

      // Get items based on type
      switch (type) {
        case 'lendable':
          // Get user's lendable items (not requests)
          items = await storage.getUserItems(userId);
          items = items.filter(item => !item.isRequest);
          canEdit = true;
          showCreateButton = true;
          emptyStateTitle = 'No Lendable Items Yet';
          emptyStateDescription = 'You haven\'t created any items to lend yet. Create your first item to get started!';
          break;

        case 'requests':
          // Get user's "Ask For" requests (wish list items)
          items = await storage.getUserItems(userId);
          items = items.filter(item => item.isRequest);
          canEdit = true;
          showCreateButton = true;
          emptyStateTitle = 'No Requested Items';
          emptyStateDescription = 'You haven\'t requested any items yet. Create your first request to get started!';
          break;

        case 'borrowed':
          // Get items the user has borrowed (active loans)
          const activeLoans = await storage.getActiveLoansByBorrower(userId);
          items = activeLoans.map(loan => ({
            ...loan.item,
            status: 'borrowed',
            borrowedDate: loan.borrowedDate,
            expectedReturnDate: loan.expectedReturnDate
          }));
          emptyStateTitle = 'No Borrowed Items';
          emptyStateDescription = 'You don\'t currently have any borrowed items.';
          break;

        case 'loaned':
          // Get user's items that are currently on loan to others
          const loanedOut = await storage.getActiveLoansByOwner(userId);
          items = loanedOut.map(loan => ({
            ...loan.item,
            status: 'on_loan',
            borrowerName: loan.borrower.showName || loan.borrower.firstName || 'Unknown Borrower',
            borrowedDate: loan.borrowedDate,
            expectedReturnDate: loan.expectedReturnDate
          }));
          canEdit = true;
          emptyStateTitle = 'No Items on Loan';
          emptyStateDescription = 'None of your items are currently being borrowed.';
          break;

        case 'drafts':
          // Get user's draft items (this would need a draft status in the database)
          items = await storage.getUserItems(userId);
          items = items.filter(item => item.status === false); // Assuming false status means draft
          canEdit = true;
          showCreateButton = true;
          emptyStateTitle = 'No Draft Items';
          emptyStateDescription = 'You don\'t have any draft items. Items are automatically published when created.';
          break;

        default:
          return res.status(404).send('Invalid item type');
      }

      // Add category names to items
      const categories = await storage.getAllCategories();
      const categoryMap = categories.reduce((map, cat) => {
        map[cat.id] = cat.category;
        return map;
      }, {} as Record<number, string>);

      items = items.map(item => ({
        ...item,
        categoryName: categoryMap[item.category] || 'Uncategorized'
      }));

      const html = await templateEngine.render('my-items.html.twig', {
        user,
        currentUser: user,
        breadcrumbs: generateBreadcrumbs('my-items', type, user?.id),
        items,
        pageTitle,
        pageDescription,
        canEdit,
        showCreateButton,
        emptyStateTitle,
        emptyStateDescription,
        itemType: type,
        totalPages: 1,
        currentPage: 1
      });

      res.send(html);
    } catch (error) {
      console.error('Error rendering my-items page:', error);
      res.status(500).send('Error rendering my-items page');
    }
  });

  // API endpoint for creating item requests
  app.post('/api/requests', async (req: Request, res: Response) => {
    try {
      // Use session user ID instead of requiring it from frontend
      const session = req.session as SessionData;
      const userId = session?.userId;
      
      if (!userId) {
        return res.status(401).json({ 
          success: false, 
          message: 'User authentication required for creating requests' 
        });
      }
      
      // Get category ID by name
      const categories = await storage.getAllCategories();
      const categoryObj = categories.find(cat => cat.category === req.body.category);
      const categoryId = categoryObj ? categoryObj.id : 1; // Default to first category if not found
      
      console.log('Request body:', req.body);
      
      const requestData = {
        name: req.body.title || 'Untitled Request',
        description: req.body.description || 'No description provided',
        category: categoryId,
        owner: userId,
        status: true, // Active request
        isRequest: true, // Flag to identify requests
        whenNeeded: req.body.whenNeeded || null,
        durationNeeded: req.body.durationNeeded || null,
        urgency: req.body.urgency || 'medium',
        // Use the asking logo for all requests
        primaryLogo: 'logo_asking_600.png',
        extraImages: null,
        searchTerms: (req.body.title || '') + ' ' + (req.body.description || '')
      };

      const newRequest = await storage.createItem(requestData);
      
      res.json({ 
        success: true, 
        request: newRequest,
        message: 'Request submitted successfully'
      });
    } catch (error) {
      console.error('Error creating request:', error);
      res.status(500).json({ error: 'Failed to submit request' });
    }
  });

  // API endpoint for creating items
  app.post('/api/items', upload.fields([
    { name: 'images', maxCount: 5 },
    { name: 'itemImage', maxCount: 5 }
  ]), async (req: Request, res: Response) => {
    console.log('POST /api/items endpoint hit');
    console.log('Request body keys:', Object.keys(req.body));
    console.log('Request files:', req.files ? Object.keys(req.files) : 'none');
    console.log('Session:', req.session);
    
    try {
      // Require authenticated user session
      const session = req.session as SessionData;
      const userId = session?.userId;
      
      if (!userId) {
        return res.status(401).json({ error: 'Authentication required' });
      }
      console.log('Using userId:', userId);
      
      let processedImages: { primaryLogo: string; extraImages: string[] } | null = null;
      
      // Process uploaded images through ImageMagick standardization
      const uploadedFiles: Express.Multer.File[] = [];
      if (req.files) {
        if (Array.isArray(req.files)) {
          uploadedFiles.push(...req.files);
        } else {
          // Handle fields case
          if (req.files.images) {
            uploadedFiles.push(...req.files.images);
          }
          if (req.files.itemImage) {
            uploadedFiles.push(...req.files.itemImage);
          }
        }
      }
      
      if (uploadedFiles.length > 0) {
        console.log(`Processing ${uploadedFiles.length} uploaded images for standardization`);
        
        try {
          // Handle both file paths and buffer data for pasted images
          const imagePaths = [];
          const tempFilesToCleanup = []; // Track temp files for cleanup
          
          for (let fileIndex = 0; fileIndex < uploadedFiles.length; fileIndex++) {
            const file = uploadedFiles[fileIndex];
            if (file.path) {
              imagePaths.push(file.path);
            } else if (file.buffer) {
              // Save buffer to temporary file for processing
              const fs = await import('fs');
              const path = await import('path');
              const tempDir = 'temp/uploads';
              
              // CRITICAL FIX: Enhanced unique temp filename with random component
              const timestamp = Date.now();
              const random = Math.floor(Math.random() * 10000);
              const tempFilename = `temp_${timestamp}_${random}_${fileIndex}_${file.originalname}`;
              const tempPath = path.join(tempDir, tempFilename);
              
              // Ensure temp directory exists
              if (!fs.existsSync(tempDir)) {
                fs.mkdirSync(tempDir, { recursive: true });
              }
              
              fs.writeFileSync(tempPath, file.buffer);
              imagePaths.push(tempPath);
              tempFilesToCleanup.push(tempPath);
              console.log('Saved pasted image to temp file:', tempPath);
            }
          }
          
          if (imagePaths.length > 0) {
            // Use ImageProcessor for proper standardization
            processedImages = await imageProcessor.processItemImages(imagePaths, userId);
            console.log(`Images processed - Primary: ${processedImages.primaryLogo}, Extras: ${processedImages.extraImages.length}`);
            
            // SAFETY CHECK: Verify processed files exist before cleanup
            const fs = await import('fs');
            const path = await import('path');
            let allProcessedFilesExist = true;
            
            if (processedImages.primaryLogo) {
              const primaryPath = path.join(process.cwd(), 'public', 'lendable_items', processedImages.primaryLogo);
              if (!fs.existsSync(primaryPath)) {
                console.error(`❌ Primary image missing after processing: ${primaryPath}`);
                allProcessedFilesExist = false;
              }
            }
            
            for (const extraImage of processedImages.extraImages || []) {
              const extraPath = path.join(process.cwd(), 'public', 'lendable_items', extraImage);
              if (!fs.existsSync(extraPath)) {
                console.error(`❌ Extra image missing after processing: ${extraPath}`);
                allProcessedFilesExist = false;
              }
            }
            
            // Only clean up temp files if all processed files exist
            if (allProcessedFilesExist) {
              for (const tempPath of tempFilesToCleanup) {
                try {
                  if (fs.existsSync(tempPath)) {
                    fs.unlinkSync(tempPath);
                    console.log(`✓ Safely cleaned up temp file: ${tempPath}`);
                  }
                } catch (cleanupError) {
                  console.warn(`Failed to cleanup temp file ${tempPath}:`, cleanupError);
                }
              }
            } else {
              console.error('⚠️ Skipping temp file cleanup - processed files missing!');
              throw new Error('Image processing incomplete - processed files not found');
            }
          }
        } catch (error: any) {
          console.error('Image processing failed:', error?.message || String(error));
          
          // CRITICAL FIX: Clean up temp files when processing fails
          const fs = await import('fs');
          for (const tempPath of tempFilesToCleanup || []) {
            try {
              if (fs.existsSync(tempPath)) {
                fs.unlinkSync(tempPath);
                console.log(`✓ Cleaned up temp file after processing failure: ${tempPath}`);
              }
            } catch (cleanupError) {
              console.warn(`Failed to cleanup temp file ${tempPath}:`, cleanupError);
            }
          }
          
          // Set to null to prevent database corruption
          processedImages = null;
          console.warn('Proceeding without images due to processing failure');
        }
      }

      // Check for mobile uploads - prioritize if explicitly flagged or if no form files uploaded
      const hasMobileUploads = req.body.hasMobileUploads === 'true';
      if (!processedImages && (hasMobileUploads || !uploadedFiles.length)) {
        // Check sequential uploads first (most recent mobile upload method)
        if (sequentialUploadStorage.images.length > 0) {
          processedImages = {
            primaryLogo: sequentialUploadStorage.images[0], // First image is primary
            extraImages: sequentialUploadStorage.images.slice(1) // Rest are extras
          };
          console.log('Using sequential mobile uploads (flagged:', hasMobileUploads, '):', processedImages);
          
          // Clear ALL mobile upload storage after use to prevent duplication
          sequentialUploadStorage = { images: [], count: 0 };
          mobileMultiUploadStorage = {};
        } else if (mobileMultiUploadStorage.filenames && mobileMultiUploadStorage.filenames.length > 0) {
          processedImages = {
            primaryLogo: mobileMultiUploadStorage.primary || mobileMultiUploadStorage.filenames[0],
            extraImages: mobileMultiUploadStorage.extras || mobileMultiUploadStorage.filenames.slice(1)
          };
          console.log('Using batch mobile uploads (flagged:', hasMobileUploads, '):', processedImages);
          
          // Clear mobile uploads after use
          mobileMultiUploadStorage = {};
        }
      }
      
      // Also check for mobileUploadedImages JSON data in form
      if (!processedImages && req.body.mobileUploadedImages) {
        try {
          const mobileImages = JSON.parse(req.body.mobileUploadedImages);
          if (Array.isArray(mobileImages) && mobileImages.length > 0) {
            processedImages = {
              primaryLogo: mobileImages[0],
              extraImages: mobileImages.slice(1)
            };
            console.log('Using mobile uploaded images from form data:', processedImages);
          }
        } catch (error) {
          console.warn('Failed to parse mobileUploadedImages:', error);
        }
      }

      // Helper function to parse decimal fields (handle empty strings)
      const parseDecimal = (value: any) => {
        if (value === '' || value === null || value === undefined) return null;
        const parsed = parseFloat(value);
        return isNaN(parsed) ? null : parsed.toFixed(2);
      };

      // Parse numeric fields safely
      const parsePricePerDay = () => {
        if (!req.body.dailyRate || req.body.dailyRate === '') return null;
        const parsed = parseFloat(req.body.dailyRate);
        return isNaN(parsed) ? null : parsed;
      };

      // Parse category safely - now expects numeric ID from form
      const parseCategory = () => {
        if (!req.body.category) return 1; // Default to first category
        
        const numericCategory = parseInt(req.body.category);
        return isNaN(numericCategory) ? 1 : numericCategory;
      };

      const categoryId = parseCategory();
      
      const itemData = {
        name: req.body.title,
        description: req.body.description,
        category: categoryId, // Integer reference to lendable_item_categories.id
        owner: userId,
        condition: req.body.condition || 'good',
        primaryLogo: processedImages?.primaryLogo || null,
        extraImages: processedImages?.extraImages.length ? processedImages.extraImages.join(',') : null,
        pricePerDay: parsePricePerDay(),
        deposit: parseDecimal(req.body.deposit)
      };
      
      console.log('Item data to insert:', itemData);

      const newItem = await storage.createItem(itemData);
      
      // Clean up temporary upload files
      if (uploadedFiles.length > 0) {
        const fs = await import('fs');
        uploadedFiles.forEach(file => {
          try {
            fs.unlinkSync(file.path);
          } catch (error) {
            console.warn(`Failed to delete temporary file: ${file.path}`, error);
          }
        });
      }
      
      console.log(`New item created: ${newItem.name} with standardized images`);
      res.json(newItem);
    } catch (error: any) {
      console.error('Error creating item:', error);
      
      let errorMessage = 'Failed to create item';
      if (error?.message) {
        if (error.message.includes('required')) {
          errorMessage = 'Please fill in all required fields';
        } else if (error.message.includes('duplicate')) {
          errorMessage = 'An item with this title already exists';
        } else if (error.message.includes('constraint')) {
          errorMessage = 'Invalid data provided. Please check your input';
        } else if (error.message.includes('permission')) {
          errorMessage = 'You do not have permission to create items';
        } else {
          errorMessage = `Database error: ${error.message}`;
        }
      }
      
      res.status(500).json({ 
        error: errorMessage,
        details: process.env.NODE_ENV === 'development' ? error?.message : undefined
      });
    }
  });

  // API endpoint for incrementing item views
  app.post('/api/items/:id/view', async (req: Request, res: Response) => {
    try {
      const itemId = parseInt(req.params.id);
      await storage.incrementItemViews(itemId);
      res.json({ success: true });
    } catch (error) {
      console.error('Error incrementing item views:', error);
      res.status(500).json({ error: 'Failed to increment views' });
    }
  });

  // Legacy loan request route - redirects to modern borrow request API
  app.post('/api/loans/request', async (req: Request, res: Response) => {
    console.log('Legacy loan request route accessed - redirecting to modern borrow request API');
    
    // Forward request to modern borrow request endpoint
    req.url = '/api/borrow-requests';
    req.method = 'POST';
    
    // Let the modern endpoint handle it
    return app._router.handle(req, res, () => {
      res.status(500).json({ error: 'Failed to process loan request' });
    });
  });

  // API route for item calendar data with privacy controls
  app.get('/api/items/:id/calendar/:year/:month', async (req: Request, res: Response) => {
    try {
      const itemId = parseInt(req.params.id);
      const year = parseInt(req.params.year);
      const month = parseInt(req.params.month); // Keep as 1-indexed for SQL
      // Get userId from session with better fallback for demo mode
      const session = req.session as any;
      const userId = session?.userId;
      
      if (!userId) {
        return res.status(401).json({ error: 'Authentication required' });
      }
      
      // Get item details to check ownership
      const item = await storage.getItem(itemId);
      if (!item) {
        return res.status(404).json({ error: 'Item not found' });
      }
      
      // Debug logging for ownership detection
      console.log(`Calendar API - UserId: ${userId}, ItemOwner: ${item.owner}, IsOwner: ${userId === item.owner}`);
      
      const isOwner = userId === item.owner;
      
      const availability = await storage.getItemAvailabilityForMonth(itemId, year, month);
      const loanCalendar = await storage.getItemLoanCalendar(itemId);
      
      // Filter loan data based on ownership and privacy settings
      const filteredLoans = loanCalendar.map(loan => {
        console.log(`Calendar API processing loan ${loan.id}: status=${loan.status}, borrower=${loan.borrowerName}, isOwner=${isOwner}`);
        
        // Map unified system statuses to frontend expected statuses
        let mappedStatus = loan.status;
        if (loan.status === 'approved') {
          // Check if approved loan is overdue
          const currentDate = new Date();
          currentDate.setHours(0, 0, 0, 0);
          const returnDate = new Date(loan.expectedReturnDate);
          returnDate.setHours(0, 0, 0, 0);
          
          if (returnDate < currentDate) {
            mappedStatus = 'overdue';
          } else {
            mappedStatus = 'reserved'; // Future approved loans are reserved
          }
        } else if (loan.status === 'active') {
          mappedStatus = 'active'; // Keep active as-is
        } else if (loan.status === 'pending') {
          mappedStatus = 'reserved'; // Pending requests show as reserved
        }
        
        console.log(`Mapped status from ${loan.status} to ${mappedStatus}`);
        
        if (isOwner) {
          // Owners see full details including actual borrower names
          return {
            ...loan,
            status: mappedStatus,
            displayName: loan.borrowerName, // Always preserve the actual borrower name for owners
            borrowerName: loan.borrowerName // Keep the real borrower name like "Bree"
          };
        } else {
          // Non-owners see limited information - no borrower names for active loans
          let displayStatus = 'On Loan';
          if (mappedStatus === 'reserved') {
            // Check if the current user is the borrower
            if (userId && loan.borrowerId === userId) {
              displayStatus = `Requested by ${loan.borrowerName}`;
            } else {
              displayStatus = 'Reserved';
            }
          } else if (mappedStatus === 'overdue') {
            // Check if the current user is the borrower
            if (userId && loan.borrowerId === userId) {
              displayStatus = 'Overdue (yours)';
            } else {
              displayStatus = 'Overdue';
            }
          }
          
          return {
            ...loan,
            status: mappedStatus,
            borrowerName: displayStatus,
            displayName: displayStatus,
            id: undefined // Hide loan ID for privacy
          };
        }
      });
      
      console.log(`Calendar API returning ${filteredLoans.length} loans for item ${itemId}, isOwner: ${isOwner}`);
      if (filteredLoans.length > 0) {
        console.log('Loan data for debugging reservation tooltips:', JSON.stringify(filteredLoans, null, 2));
      }
      
      res.json({
        availability,
        loans: filteredLoans,
        isOwner: isOwner
      });
    } catch (error) {
      console.error('Calendar API error:', error);
      res.status(500).json({ error: 'Failed to fetch calendar data' });
    }
  });

  // Profile page
  app.get('/profile/:id', async (req: Request, res: Response) => {
    try {
      const userId = parseInt(req.params.id);
      const user = await storage.getUser(userId);
      
      if (!user) {
        return res.status(404).send('User not found');
      }

      const userItems = await storage.getUserItems(userId);
      const stats = await storage.getUserStats(userId);
      
      const html = await templateEngine.render('profile.html.twig', {
        user: user,
        userItems: userItems,
        allUserItems: allUserItems,
        stats: stats,
        isOwnProfile: true
      });
      
      res.send(html);
    } catch (error) {
      console.error('Error rendering profile page:', error);
      res.status(500).send('Internal Server Error');
    }
  });

  app.get('/logout', async (req: Request, res: Response) => {
    // Clear any session data and prevent back button access
    res.set({
      'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0, private',
      'Pragma': 'no-cache',
      'Expires': '0'
    });
    
    // Send logout confirmation page with script to prevent back navigation
    const html = `
      <!DOCTYPE html>
      <html lang="en">
      <head>
          <meta charset="UTF-8">
          <meta name="viewport" content="width=device-width, initial-scale=1.0">
          <title>Logged Out - Local Lendables</title>
          <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
          <style>
              body {
                  background: linear-gradient(135deg, #dc3545 0%, #6c757d 100%);
                  min-height: 100vh;
                  display: flex;
                  align-items: center;
                  justify-content: center;
              }
              .logout-card {
                  background: white;
                  border-radius: 20px;
                  padding: 3rem;
                  text-align: center;
                  max-width: 400px;
                  box-shadow: 0 15px 35px rgba(0,0,0,0.1);
              }
          </style>
          <script>
              // Prevent back button after logout
              history.pushState(null, null, location.href);
              window.onpopstate = function () {
                  history.go(1);
              };
              
              // Auto-redirect after 3 seconds
              setTimeout(function() {
                  window.location.replace('/demo');
              }, 3000);
          </script>
      </head>
      <body>
          <div class="logout-card">
              <i class="fas fa-sign-out-alt text-danger mb-3" style="font-size: 3rem;"></i>
              <h2>Successfully Logged Out</h2>
              <p class="text-muted">You have been safely logged out of Local Lendables Demo.</p>
              <p class="small">Redirecting to homepage in 3 seconds...</p>
              <a href="/demo" class="btn btn-primary mt-2">Return to Demo</a>
          </div>
          <script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/js/all.min.js"></script>
      </body>
      </html>
    `;
    
    res.send(html);
  });

  // Borrow item page
  app.get('/borrow/:id', async (req: Request, res: Response) => {
    try {
      const itemId = parseInt(req.params.id);
      const item = await storage.getItemWithOwner(itemId);
      
      if (!item) {
        return res.status(404).send('Item not found');
      }
      
      // Get current user from session
      const session = req.session as SessionData;
      const currentUserId = session.userId;
      
      if (!currentUserId) {
        return res.status(401).json({ error: 'Authentication required' });
      }
      
      // Get legal content for the borrow page
      const legalContent = await storage.getLegalContent();
      
      // Get existing bookings to show calendar
      const existingBookings = await storage.getExistingBookings(itemId);
      
      // Get user notification data for header
      const notificationData = await storage.getUserNotificationData(currentUserId);
      
      const html = await templateEngine.render('borrow.html.twig', {
        item: item,
        legalContent: legalContent,
        existingBookings: existingBookings,
        hasUnreadNotifications: notificationData.hasUnread,
        unreadCount: notificationData.unreadCount,
        recentNotifications: notificationData.recentNotifications,
        session: req.session,
        user: await storage.getUser(currentUserId)
      });
      
      res.send(html);
    } catch (error) {
      console.error('Error rendering borrow page:', error);
      res.status(500).send('Internal Server Error');
    }
  });

  // Legacy borrow request route - redirects to modern API
  app.post('/borrow/:id/request', async (req: Request, res: Response) => {
    console.log('Legacy borrow request route accessed - redirecting to modern API');
    
    // Forward request to modern endpoint with item ID included in body
    const forwardedBody = {
      ...req.body,
      itemId: parseInt(req.params.id)
    };
    
    // Make internal API call to modern endpoint
    req.body = forwardedBody;
    req.url = '/api/borrow-requests';
    req.method = 'POST';
    
    // Let the modern endpoint handle it
    return app._router.handle(req, res, () => {
      res.status(500).json({ error: 'Failed to process borrow request' });
    });
  });

  // Owner borrow requests management
  app.get('/owner/requests', async (req: Request, res: Response) => {
    try {
      const session = req.session as SessionData;
      const ownerId = session.userId;
      
      if (!ownerId) {
        return res.status(401).send('Authentication required');
      }
      
      const borrowRequests = await storage.getBorrowRequestsWithDetails(ownerId);
      const user = await storage.getUser(ownerId);
      
      const html = await templateEngine.render('owner-requests.html.twig', {
        borrowRequests: borrowRequests,
        user: user,
        session: req.session
      });
      
      res.send(html);
    } catch (error) {
      console.error('Error rendering owner requests:', error);
      res.status(500).send('Internal Server Error');
    }
  });

  // Submit borrow request (new API endpoint)
  app.post('/api/borrow-requests', upload.single('audio'), async (req: Request, res: Response) => {
    try {
      const { itemId, startDate, endDate, message } = req.body;
      
      // Get current user from session (demo mode)
      const session = req.session as SessionData;
      const borrowerId = session?.userId;
      
      if (!borrowerId) {
        return res.status(401).json({ error: 'Authentication required' });
      }
      
      // Get item details to find owner
      const item = await storage.getItemWithOwner(parseInt(itemId));
      if (!item) {
        return res.status(404).json({ error: 'Item not found' });
      }
      
      let audioFilename = null;
      
      // Handle audio file if provided
      if (req.file) {
        console.log('Audio file received for borrow request:', req.file.originalname, 'size:', req.file.size);
        console.log('File details:', { path: req.file.path, buffer: !!req.file.buffer, destination: req.file.destination });
        
        // Ensure temp/uploads directory exists
        const uploadDir = 'temp/uploads';
        if (!existsSync(uploadDir)) {
          mkdirSync(uploadDir, { recursive: true });
        }
        
        // Generate proper filename
        const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
        audioFilename = `borrow-request-user${borrowerId}-${timestamp}.webm`;
        const audioPath = `temp/uploads/${audioFilename}`;
        
        // Move or write file to proper location
        const fs = await import('fs/promises');
        try {
          if (req.file.path) {
            await fs.rename(req.file.path, audioPath);
            console.log('Audio file moved from temp to:', audioPath);
          } else if (req.file.buffer) {
            await fs.writeFile(audioPath, req.file.buffer);
            console.log('Audio file written from buffer to:', audioPath);
          } else {
            console.log('No valid audio file data found - no path or buffer');
            audioFilename = null;
          }
        } catch (error) {
          console.error('Error saving audio file:', error);
          audioFilename = null;
        }
      }
      
      // Transcribe audio if present
      let transcription = null;
      if (audioFilename) {
        try {
          const { SimpleTranscriptionService } = await import('./simple-transcription');
          const transcriptionService = SimpleTranscriptionService.getInstance();
          const audioPath = `temp/uploads/${audioFilename}`;
          transcription = await transcriptionService.transcribeAudio(audioPath);
          console.log('Audio transcribed for borrow request:', transcription);
        } catch (transcriptionError) {
          console.error('Error transcribing audio:', transcriptionError);
        }
      }

      // Create borrow request
      const borrowRequest = await storage.createBorrowRequest({
        itemId: parseInt(itemId),
        borrowerId,
        ownerId: item.owner,
        startDate: new Date(startDate),
        endDate: new Date(endDate),
        message: message || '',
        audioFilename,
        transcription
      });
      
      // Create standardized notification for owner
      const borrowerUser = await storage.getUser(borrowerId);
      const borrowerName = borrowerUser?.showName || borrowerUser?.firstName || 'Someone';
      
      await storage.createStandardizedNotification({
        userId: item.owner,
        type: 'borrow_request',
        relatedId: borrowRequest.id,
        relatedType: 'borrow_request',
        templateData: {
          senderName: borrowerName,
          itemName: item.name,
          startDate,
          endDate
        }
      });
      
      console.log('Borrow request created successfully:', borrowRequest.id);
      res.json({ success: true, requestId: borrowRequest.id });
    } catch (error) {
      console.error('Error creating borrow request:', error);
      res.status(500).json({ error: 'Failed to create borrow request' });
    }
  });

  // Get default borrow request message template
  app.get('/api/content/borrow-message-template', async (req: Request, res: Response) => {
    try {
      const template = await storage.getContentBlock('borrow_request_default_message');
      const defaultMessage = template?.content || 'Hi! I would like to borrow your item for the selected dates. I\'ll take great care of it and return it in perfect condition.';
      res.json({ success: true, message: defaultMessage });
    } catch (error) {
      console.error('Error getting borrow message template:', error);
      res.json({ success: true, message: 'Hi! I would like to borrow your item for the selected dates. I\'ll take great care of it and return it in perfect condition.' });
    }
  });

  // Get single borrow request by ID
  app.get('/api/borrow-requests/:id', async (req: Request, res: Response) => {
    try {
      const requestId = parseInt(req.params.id);
      const session = req.session as SessionData;
      
      if (!session?.userId) {
        return res.status(401).json({ error: 'Not authenticated' });
      }
      
      // Get the borrow request
      const borrowRequest = await storage.getBorrowRequest(requestId);
      if (!borrowRequest) {
        return res.status(404).json({ error: 'Request not found' });
      }
      
      // Check if user is owner or borrower or admin
      const isAdmin = await storage.isUserAdmin(session.userId);
      const canView = borrowRequest.ownerId === session.userId || 
                      borrowRequest.borrowerId === session.userId || 
                      isAdmin;
      
      if (!canView) {
        return res.status(403).json({ error: 'Not authorized to view this request' });
      }
      
      res.json({ success: true, request: borrowRequest });
    } catch (error) {
      console.error('Error fetching borrow request:', error);
      res.status(500).json({ error: 'Failed to fetch borrow request' });
    }
  });

  // Delete borrow request
  app.delete('/api/borrow-requests/:id', async (req: Request, res: Response) => {
    try {
      const requestId = parseInt(req.params.id);
      const session = req.session as SessionData;
      
      if (!session?.userId) {
        return res.status(401).json({ error: 'Not authenticated' });
      }
      
      // Get the borrow request to check permissions
      const borrowRequest = await storage.getBorrowRequest(requestId);
      if (!borrowRequest) {
        return res.status(404).json({ error: 'Request not found' });
      }
      
      // Check if user is owner or borrower or admin
      const isAdmin = await storage.isUserAdmin(session.userId);
      const canDelete = borrowRequest.ownerId === session.userId || 
                       borrowRequest.borrowerId === session.userId || 
                       isAdmin;
      
      if (!canDelete) {
        return res.status(403).json({ error: 'Not authorized to delete this request' });
      }
      
      // Delete the borrow request
      await storage.deleteBorrowRequest(requestId);
      
      res.json({ success: true, message: 'Borrow request deleted successfully' });
    } catch (error) {
      console.error('Error deleting borrow request:', error);
      res.status(500).json({ error: 'Failed to delete borrow request' });
    }
  });

  // Legacy approval route - redirects to modern system
  app.post('/owner/requests/:id/approve', async (req: Request, res: Response) => {
    console.log('Legacy approval route accessed - using modern approval system');
    
    try {
      const requestId = parseInt(req.params.id);
      
      // Use modern approval system with comprehensive agreement creation
      const approvalRequest = {
        requestId: requestId,
        response: 'approved',
        responseMessage: 'Approved via legacy route'
      };
      
      // Create new request object to call modern endpoint
      const modernReq = { 
        ...req, 
        body: approvalRequest,
        url: '/api/borrow-requests/respond',
        method: 'POST'
      };
      
      // Create response handler that redirects on success
      const modernRes = {
        ...res,
        json: (data: any) => {
          if (data.success) {
            res.redirect('/owner/requests?approved=true');
          } else {
            res.status(500).send('Approval failed');
          }
        }
      };
      
      // Call modern approval system
      return app._router.handle(modernReq as any, modernRes as any, () => {
        res.status(500).send('Failed to process approval');
      });
      
    } catch (error) {
      console.error('Error in legacy approval route:', error);
      res.status(500).send('Internal Server Error');
    }
  });

  // Legacy rejection route - redirects to modern system  
  app.post('/owner/requests/:id/reject', async (req: Request, res: Response) => {
    console.log('Legacy rejection route accessed - using modern rejection system');
    
    try {
      const requestId = parseInt(req.params.id);
      
      // Use modern rejection system
      const rejectionRequest = {
        requestId: requestId,
        response: 'rejected',
        responseMessage: 'Declined via legacy route'
      };
      
      // Create new request object to call modern endpoint
      const modernReq = { 
        ...req, 
        body: rejectionRequest,
        url: '/api/borrow-requests/respond',
        method: 'POST'
      };
      
      // Create response handler that redirects on success
      const modernRes = {
        ...res,
        json: (data: any) => {
          if (data.success) {
            res.redirect('/owner/requests?rejected=true');
          } else {
            res.status(500).send('Rejection failed');
          }
        }
      };
      
      // Call modern rejection system
      return app._router.handle(modernReq as any, modernRes as any, () => {
        res.status(500).send('Failed to process rejection');
      });
      
    } catch (error) {
      console.error('Error in legacy rejection route:', error);
      res.status(500).send('Internal Server Error');
    }
  });

  // Dashboard route without ID - redirect to demo
  app.get('/dashboard', async (req: Request, res: Response) => {
    console.log('Dashboard accessed without ID - redirecting to demo');
    return res.redirect('/demo');
  });

  // Generate QR code session for mobile uploads
  app.get('/api/generate-qr-session', (req: Request, res: Response) => {
    try {
      const sessionId = Math.random().toString(36).substring(2) + Date.now().toString(36);
      const now = Date.now();
      const expiresAt = now + (5 * 60 * 1000); // 5 minutes from now
      
      // Clean up expired sessions
      const expiredSessions: string[] = [];
      qrSessions.forEach((session, id) => {
        if (session.expiresAt < now) {
          expiredSessions.push(id);
        }
      });
      expiredSessions.forEach(id => qrSessions.delete(id));
      
      qrSessions.set(sessionId, {
        createdAt: now,
        expiresAt: expiresAt,
        itemId: req.query.itemId ? parseInt(req.query.itemId as string) : undefined,
        userId: req.query.userId ? parseInt(req.query.userId as string) : undefined
      });
      
      const baseUrl = `${req.protocol}://${req.get('host')}`;
      const mobileUrl = `${baseUrl}/mobile-camera?session=${sessionId}`;
      
      console.log(`Generated QR session ${sessionId}, expires in 5 minutes`);
      
      res.json({
        sessionId: sessionId,
        mobileUrl: mobileUrl,
        expiresAt: expiresAt,
        expiresIn: 300, // seconds
        success: true
      });
    } catch (error) {
      console.error('QR session generation error:', error);
      res.status(500).json({
        error: 'Failed to generate QR session',
        success: false
      });
    }
  });

  // Generate QR code image directly
  app.get('/api/generate-qr-image', async (req: Request, res: Response) => {
    try {
      const text = req.query.text as string;
      const size = parseInt(req.query.size as string) || 256;
      
      if (!text) {
        return res.status(400).json({ error: 'Missing text parameter' });
      }
      
      // Use dynamic import for ES modules
      const QRCode = await import('qrcode');
      
      const qrBuffer = await QRCode.toBuffer(text, {
        width: size,
        margin: 2,
        color: {
          dark: '#000000',
          light: '#FFFFFF'
        }
      });
      
      res.setHeader('Content-Type', 'image/png');
      res.setHeader('Cache-Control', 'public, max-age=300');
      res.send(qrBuffer);
    } catch (error) {
      console.error('QR image generation error:', error);
      res.status(500).json({ error: 'Failed to generate QR code image' });
    }
  });

  // Mobile camera interface for QR code
  app.get('/mobile-camera', async (req: Request, res: Response) => {
    try {
      const sessionId = req.query.session as string;
      let sessionValid = false;
      
      if (sessionId) {
        const session = qrSessions.get(sessionId);
        sessionValid = !!(session && session.expiresAt > Date.now());
        
        if (!sessionValid && session) {
          console.log(`QR session ${sessionId} has expired`);
          qrSessions.delete(sessionId);
        }
      }
      
      const html = await templateEngine.render('mobile-camera.html.twig', {
        sessionId: sessionId,
        sessionValid: sessionValid
      });
      res.send(html);
    } catch (error) {
      console.error('Error rendering mobile camera:', error);
      res.status(500).send('Internal Server Error');
    }
  });

  // Mobile upload storage (in-memory for demo)
  let mobileUploadStorage: { image?: Buffer, timestamp?: number } = {};
  
  // Multi-image mobile upload storage
  let mobileMultiUploadStorage: { 
    filenames?: string[], 
    primary?: string, 
    extras?: string[], 
    timestamp?: number 
  } = {};
  
  // Sequential upload storage - accumulates images one by one
  let sequentialUploadStorage: {
    images: string[],
    count: number,
    timestamp?: number
  } = { images: [], count: 0 };
  
  // QR code session storage with 5-minute expiration
  const qrSessions = new Map<string, { 
    createdAt: number, 
    expiresAt: number, 
    itemId?: number,
    userId?: number 
  }>();

  // Sequential mobile upload endpoint - processes one image at a time
  app.post('/api/mobile-upload-sequential', upload.single('image'), async (req: Request, res: Response) => {
    try {
      console.log('Mobile upload request received');
      console.log('req.file details:', {
        fieldname: req.file?.fieldname,
        originalname: req.file?.originalname,
        encoding: req.file?.encoding,
        mimetype: req.file?.mimetype,
        buffer: req.file?.buffer ? `Buffer(${req.file.buffer.length} bytes)` : 'No buffer',
        size: req.file?.size,
        path: req.file?.path || 'No path'
      });
      console.log('req.body:', req.body);
      
      if (!req.file) {
        return res.status(400).json({ error: 'No image uploaded' });
      }

      const sessionId = req.body.sessionId;
      console.log(`Sequential upload #${sequentialUploadStorage.count + 1}: ${req.file.originalname}`);
      
      // Get authenticated user ID from session
      const session = req.session as SessionData;
      const userId = session?.userId;
      
      if (!userId) {
        console.log('Mobile upload rejected: User not authenticated');
        return res.status(401).json({ error: 'User not authenticated. Please log in to upload images.' });
      }
      
      console.log(`Mobile upload authenticated for user ID: ${userId}`);
      
      try {
        // Handle file data - mobile uploads use memory storage (buffer), not disk storage (path)
        let filePath;
        console.log('Has buffer:', !!req.file.buffer);
        console.log('Buffer size:', req.file.buffer?.length);
        
        if (req.file.buffer) {
          // Create temp file from buffer for processing
          const uploadDir = 'temp/uploads';
          if (!existsSync(uploadDir)) {
            mkdirSync(uploadDir, { recursive: true });
          }
          const tempFilename = `mobile_${Date.now()}_${req.file.originalname}`;
          filePath = path.join(uploadDir, tempFilename);
          await fs.writeFile(filePath, req.file.buffer);
          console.log('Created temp file from buffer:', filePath);
        } else {
          throw new Error('No file data available - buffer is missing');
        }
        
        // Process single image through Image Canon standardization (400px JPG at 85% quality)
        let processedFilename;
        try {
          const imagePaths = [filePath];
          const processedImages = await imageProcessor.processItemImages(imagePaths, userId);
          processedFilename = processedImages.primaryLogo;
          console.log('ImageProcessor successful:', processedFilename);
        } catch (imageProcessorError) {
          console.log('ImageProcessor failed, using SimpleImageProcessor:', imageProcessorError.message);
          // Fallback to simple processor if ImageMagick fails
          processedFilename = await simpleImageProcessor.processImage(filePath, req.file.originalname);
        }
        
        // Add to sequential storage
        sequentialUploadStorage.images.push(processedFilename);
        sequentialUploadStorage.count++;
        sequentialUploadStorage.timestamp = Date.now();
        
        // Clean up temp file
        const fsSync = await import('fs');
        try {
          if (filePath) {
            fsSync.unlinkSync(filePath);
            console.log('Cleaned up temp file:', filePath);
          }
        } catch (cleanupError) {
          console.warn('Failed to clean up temp file:', filePath);
        }
        
        console.log(`Sequential upload ${sequentialUploadStorage.count} processed: ${processedFilename}`);
        console.log(`Total images in sequence: ${sequentialUploadStorage.images.length}`);
        
        // Don't duplicate to multi-upload storage - sequential storage is the source of truth
        // Multi-upload storage will be cleared when sequential is used
        
        res.json({ 
          success: true, 
          message: `Image ${sequentialUploadStorage.count} uploaded successfully`,
          count: sequentialUploadStorage.count,
          totalImages: sequentialUploadStorage.images.length,
          canAddMore: sequentialUploadStorage.count < 3
        });
        
      } catch (processingError) {
        console.error('Sequential image processing failed:', processingError);
        
        // Clean up temp file
        const fs = await import('fs');
        try {
          fs.unlinkSync(req.file.path);
        } catch (cleanupError) {
          console.warn('Failed to clean up temp file:', req.file.path);
        }
        
        res.status(500).json({ error: 'Image processing failed: ' + (processingError as Error).message });
      }
      
    } catch (error) {
      console.error('Sequential mobile upload error:', error);
      res.status(500).json({ error: 'Upload failed' });
    }
  });

  // Mobile upload endpoint (single image - legacy)
  app.post('/api/mobile-upload', upload.single('image'), async (req: Request, res: Response) => {
    try {
      if (!req.file) {
        return res.status(400).json({ error: 'No image uploaded' });
      }

      console.log('Mobile image uploaded:', req.file.originalname);
      
      // Store the image in memory (in production, use proper storage)
      const fs = await import('fs');
      const imageData = fs.readFileSync(req.file.path);
      
      mobileUploadStorage = {
        image: imageData,
        timestamp: Date.now()
      };

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

      res.json({ success: true, message: 'Image uploaded successfully' });
    } catch (error) {
      console.error('Mobile upload error:', error);
      res.status(500).json({ error: 'Upload failed' });
    }
  });

  // Multi-image mobile upload endpoint
  app.post('/api/mobile-upload-multiple', upload.array('images', 3), async (req: Request, res: Response) => {
    try {
      const sessionId = req.body.sessionId;
      
      // Validate session if provided (allow demo mode without session)
      if (sessionId) {
        const session = qrSessions.get(sessionId);
        if (!session || session.expiresAt < Date.now()) {
          console.log(`Invalid or expired QR session: ${sessionId}`);
          // Don't block upload, just log for demo purposes
          console.log('Proceeding with demo mode upload without valid session');
        }
      } else {
        console.log('Demo mode upload without session ID');
      }
      
      if (!req.files || !Array.isArray(req.files) || req.files.length === 0) {
        return res.status(400).json({ error: 'No images uploaded' });
      }

      console.log(`Processing ${req.files.length} mobile images for standardization (session: ${sessionId || 'none'})`);
      
      // Get authenticated user ID from session
      const session = req.session as SessionData;
      const userId = session?.userId;
      
      if (!userId) {
        console.log('Multi-image mobile upload rejected: User not authenticated');
        return res.status(401).json({ error: 'User not authenticated. Please log in to upload images.' });
      }
      
      console.log(`Multi-image mobile upload authenticated for user ID: ${userId}`);
      const imagePaths = req.files.map(file => file.path);
      
      try {
        // Process all images through ImageMagick standardization
        const processedImages = await imageProcessor.processItemImages(imagePaths, userId);
        
        // Clean up temp files
        const fs = await import('fs');
        req.files.forEach(file => {
          try {
            fs.unlinkSync(file.path);
          } catch (cleanupError) {
            console.warn('Failed to clean up temp file:', file.path);
          }
        });
        
        console.log(`Successfully processed ${req.files.length} mobile images:`, {
          primary: processedImages.primaryLogo,
          extras: processedImages.extraImages
        });
        
        // Store the processed images for the create item form to find
        const allFilenames = [processedImages.primaryLogo, ...processedImages.extraImages];
        mobileMultiUploadStorage = {
          filenames: allFilenames,
          primary: processedImages.primaryLogo,
          extras: processedImages.extraImages,
          timestamp: Date.now()
        };
        
        console.log('Stored multi-image upload in storage:', mobileMultiUploadStorage);
        
        res.json({ 
          success: true, 
          message: `${req.files.length} images processed successfully`,
          filenames: allFilenames,
          primary: processedImages.primaryLogo,
          extras: processedImages.extraImages
        });
        
      } catch (processingError) {
        console.error('Image processing failed:', processingError);
        
        // Clean up temp files even on error
        const fs = await import('fs');
        req.files.forEach(file => {
          try {
            fs.unlinkSync(file.path);
          } catch (cleanupError) {
            console.warn('Failed to clean up temp file:', file.path);
          }
        });
        
        res.status(500).json({ error: 'Image processing failed: ' + (processingError as Error).message });
      }
      
    } catch (error) {
      console.error('Mobile multi-upload error:', error);
      res.status(500).json({ error: 'Upload failed' });
    }
  });

  // Check mobile upload status
  app.get('/api/mobile-upload-status', (req: Request, res: Response) => {
    // Check for single image upload (legacy)
    const hasSingleUpload = mobileUploadStorage.image && 
      mobileUploadStorage.timestamp && 
      (Date.now() - mobileUploadStorage.timestamp < 300000); // 5 minutes timeout
    
    // Check for multi-image upload (batch)
    const hasMultiUpload = mobileMultiUploadStorage.filenames && 
      mobileMultiUploadStorage.timestamp && 
      (Date.now() - mobileMultiUploadStorage.timestamp < 300000); // 5 minutes timeout
    
    // Check for sequential upload
    const hasSequentialUpload = sequentialUploadStorage.images.length > 0 && 
      sequentialUploadStorage.timestamp && 
      (Date.now() - sequentialUploadStorage.timestamp < 300000); // 5 minutes timeout
    
    const hasUpload = hasSingleUpload || hasMultiUpload || hasSequentialUpload;
    
    console.log('Upload status check:', {
      hasSingleUpload: !!hasSingleUpload,
      hasMultiUpload: !!hasMultiUpload,
      hasSequentialUpload: !!hasSequentialUpload,
      hasUpload: !!hasUpload
    });

    res.json({ hasUpload: !!hasUpload });
  });

  // Get mobile uploaded image(s)
  app.get('/api/mobile-upload-image', (req: Request, res: Response) => {
    // Check for sequential uploads first (prioritize most recent method)
    if (sequentialUploadStorage.images.length > 0) {
      res.json({ 
        type: 'sequential',
        primary: sequentialUploadStorage.images[0], // First image is primary logo
        extras: sequentialUploadStorage.images.slice(1), // Rest are extras
        filenames: sequentialUploadStorage.images
      });
    } else if (mobileMultiUploadStorage.filenames && mobileMultiUploadStorage.filenames.length > 0) {
      // Return the primary image path for the create item form
      const primaryImage = mobileMultiUploadStorage.primary || mobileMultiUploadStorage.filenames[0];
      res.json({ 
        type: 'multi',
        primary: primaryImage,
        extras: mobileMultiUploadStorage.extras || [],
        filenames: mobileMultiUploadStorage.filenames
      });
    } else if (mobileUploadStorage.image) {
      // Legacy single image upload
      res.set('Content-Type', 'image/jpeg');
      res.send(mobileUploadStorage.image);
    } else {
      res.status(404).json({ error: 'No image available' });
    }
  });

  // Clear mobile upload
  app.post('/api/mobile-upload-clear', (req: Request, res: Response) => {
    mobileUploadStorage = {};
    mobileMultiUploadStorage = {};
    sequentialUploadStorage = { images: [], count: 0 };
    console.log('Cleared all upload storage (single, multi, and sequential)');
    res.json({ success: true });
  });

  // Clear only after form completion
  app.post('/api/mobile-upload-complete', (req: Request, res: Response) => {
    mobileUploadStorage = {};
    mobileMultiUploadStorage = {};
    sequentialUploadStorage = { images: [], count: 0 };
    console.log('Completed upload session - cleared all storage');
    res.json({ success: true });
  });

  // ===== HOUR OFFERINGS SYSTEM =====
  
  // Create hour offering
  app.post('/api/hour-offerings', async (req: Request, res: Response) => {
    try {
      const session = req.session as SessionData;
      const userId = session?.userId;
      
      if (!userId) {
        return res.status(401).json({ error: 'Authentication required' });
      }
      
      const { title, description, date, startTime, endTime, isRecurring, recurringCount } = req.body;
      
      // Validate required fields
      if (!title || !date || !startTime || !endTime) {
        return res.status(400).json({ error: 'Missing required fields: title, date, startTime, endTime' });
      }
      
      // Create Date objects for offerDate and endTime
      const offerDate = new Date(`${date}T${startTime}`);
      const endTimeDate = new Date(`${date}T${endTime}`);
      
      // Validate that end time is after start time
      if (endTimeDate <= offerDate) {
        return res.status(400).json({ error: 'End time must be after start time' });
      }
      
      if (isRecurring && recurringCount) {
        // Create multiple recurring offerings
        const offerings = [];
        const recurringType = 'weekly'; // Default to weekly recurring
        
        for (let i = 0; i < recurringCount; i++) {
          const instanceDate = new Date(offerDate);
          const instanceEndTime = new Date(endTimeDate);
          
          // Add weeks for each instance
          instanceDate.setDate(instanceDate.getDate() + (i * 7));
          instanceEndTime.setDate(instanceEndTime.getDate() + (i * 7));
          
          const offeringData = {
            userId,
            title,
            description: description || null,
            offerDate: instanceDate,
            endTime: instanceEndTime,
            isActive: true,
            isBooked: false,
            isRecurring: true,
            recurringType,
            recurringCount,
            parentOfferingId: i === 0 ? null : offerings[0]?.id || null
          };
          
          const offering = await storage.createHourOffering(offeringData);
          offerings.push(offering);
          
          // Set parent offering ID for subsequent instances
          if (i === 0 && offerings.length > 0) {
            // Update the first offering to be its own parent
            await storage.updateHourOffering(offering.id, { parentOfferingId: offering.id });
          }
        }
        
        console.log(`Created ${offerings.length} recurring hour offerings for user ${userId}`);
        res.json({ 
          success: true, 
          isRecurring: true, 
          count: offerings.length,
          offerings: offerings.map(o => o.id)
        });
      } else {
        // Create single offering
        const offeringData = {
          userId,
          title,
          description: description || null,
          offerDate,
          endTime: endTimeDate,
          isActive: true,
          isBooked: false,
          isRecurring: false
        };
        
        const offering = await storage.createHourOffering(offeringData);
        console.log(`Created hour offering ${offering.id} for user ${userId}`);
        res.json({ success: true, isRecurring: false, offering: offering });
      }
      
    } catch (error) {
      console.error('Error creating hour offering:', error);
      res.status(500).json({ error: 'Failed to create hour offering' });
    }
  });
  
  // Contact helper for hour offering
  app.post('/api/hour-offerings/contact', async (req: Request, res: Response) => {
    try {
      const session = req.session as SessionData;
      const userId = session?.userId;
      
      if (!userId) {
        return res.status(401).json({ error: 'Authentication required' });
      }
      
      const { offeringId, message } = req.body;
      
      // Validate required fields
      if (!offeringId || !message) {
        return res.status(400).json({ error: 'Missing required fields: offeringId, message' });
      }
      
      // Get the hour offering details
      const offering = await storage.getHourOfferingById(parseInt(offeringId));
      if (!offering) {
        return res.status(404).json({ error: 'Hour offering not found' });
      }
      
      // Get helper (offering owner) details
      const helper = await storage.getUser(offering.userId);
      if (!helper) {
        return res.status(404).json({ error: 'Helper not found' });
      }
      
      // Get requester details
      const requester = await storage.getUser(userId);
      if (!requester) {
        return res.status(404).json({ error: 'User not found' });
      }
      
      // Create standardized notification for the helper
      await storage.createStandardizedNotification({
        userId: offering.userId,
        type: 'hour_request',
        relatedId: offering.id,
        relatedType: 'hour_offering',
        templateData: {
          senderName: requester.showName || requester.firstName,
          offeringTitle: offering.title
        }
      });
      
      // Create conversation for the request (similar to borrow requests)
      const conversation = await storage.createConversation({
        itemId: null, // No item for hour offerings
        borrowerId: userId,
        ownerId: offering.userId
      });
      
      // Add initial message to conversation
      await storage.addMessageToConversation(conversation.id, {
        senderId: userId,
        message: `Interested in your hour offering "${offering.title}": ${message}`,
        audioPath: null,
        transcription: null
      });
      
      console.log(`Hour offering contact request sent from user ${userId} to helper ${offering.userId} for offering ${offeringId}`);
      res.json({ success: true, message: 'Contact request sent successfully' });
      
    } catch (error) {
      console.error('Error sending hour offering contact:', error);
      res.status(500).json({ error: 'Failed to send contact request' });
    }
  });

  // ===== ALPHA FEEDBACK SYSTEM =====

  // Alpha Testing Feedback API with Screenshot Support
  app.post('/api/alpha-feedback', upload.array('screenshots', 3), async (req: Request, res: Response) => {
    try {
      const session = req.session as SessionData;
      
      if (!session.userId) {
        return res.status(401).json({ error: 'Authentication required' });
      }

      const {
        testerComments,
        pageTitle,
        previousPage,
        currentUrl,
        userAgent,
        browserInfo,
        sessionData
      } = req.body;

      if (!testerComments || !pageTitle || !currentUrl) {
        return res.status(400).json({ error: 'Missing required fields' });
      }

      // Get user info for the feedback
      const user = await storage.getUser(session.userId);
      if (!user) {
        return res.status(404).json({ error: 'User not found' });
      }

      const userName = user.showName || `${user.firstName || ''} ${user.lastName || ''}`.trim() || user.email;
      const ipAddress = req.ip || req.connection.remoteAddress || null;

      // Process screenshots with Image Canon compliance
      let screenshot1: string | null = null;
      let screenshot2: string | null = null;
      let screenshot3: string | null = null;

      const files = req.files as Express.Multer.File[];
      if (files && files.length > 0) {
        try {
          // Use same approach as Create Item: save buffers to temp files, then process with imageProcessor
          const fs = await import('fs');
          const path = await import('path');
          const tempDir = 'temp/uploads';
          
          // Ensure temp directory exists
          if (!fs.existsSync(tempDir)) {
            fs.mkdirSync(tempDir, { recursive: true });
          }
          
          // Function to generate Atlantic time-based timestamp for filenames
          const getAtlanticTimestamp = () => {
            const now = new Date();
            return now.toLocaleString('en-CA', {
              timeZone: 'America/Halifax',
              year: 'numeric',
              month: '2-digit',
              day: '2-digit',
              hour: '2-digit',
              minute: '2-digit',
              second: '2-digit',
              hour12: false
            }).replace(/[^\d]/g, ''); // Remove non-digits to create clean timestamp
          };

          const tempPaths = [];
          for (let i = 0; i < files.length; i++) {
            const file = files[i];
            if (file.buffer) {
              const tempFilename = `temp_${getAtlanticTimestamp()}_${i}_${file.originalname}`;
              const tempPath = path.join(tempDir, tempFilename);
              fs.writeFileSync(tempPath, file.buffer);
              tempPaths.push(tempPath);
            }
          }
          
          if (tempPaths.length > 0) {
            // Manual Image Canon processing since simpleImageProcessor just copies files
            for (let i = 0; i < Math.min(tempPaths.length, 3); i++) {
              const tempPath = tempPaths[i];
              
              try {
                // Direct shell command execution for ImageMagick (avoiding import issues)
                const timestamp = getAtlanticTimestamp();
                const randomSuffix = Math.random().toString(36).substring(2, 8);
                const outputFilename = `screenshot_${timestamp}_${i}_${randomSuffix}.jpg`;
                const outputPath = path.join(process.cwd(), 'temp', outputFilename);
                
                // Ensure temp directory exists
                if (!fs.existsSync(path.join(process.cwd(), 'temp'))) {
                  fs.mkdirSync(path.join(process.cwd(), 'temp'), { recursive: true });
                }
                
                // Execute ImageMagick directly via shell - Image Canon: 400px width, 85% quality
                const childProcess = await import('child_process');
                const args = [tempPath, '-resize', '400x>', '-quality', '85', '-background', 'white', '-flatten', '-strip', outputPath];
                
                await new Promise((resolve, reject) => {
                  const magickProcess = childProcess.spawn('convert', args);
                  magickProcess.on('close', (code) => {
                    if (code === 0) resolve(code);
                    else reject(new Error(`ImageMagick exit code: ${code}`));
                  });
                  magickProcess.on('error', reject);
                });
                
                if (fs.existsSync(outputPath)) {
                  const imageBuffer = fs.readFileSync(outputPath);
                  const base64Data = imageBuffer.toString('base64');
                  
                  if (i === 0) screenshot1 = base64Data;
                  else if (i === 1) screenshot2 = base64Data;
                  else if (i === 2) screenshot3 = base64Data;
                  
                  // Clean up temp output file
                  fs.unlinkSync(outputPath);
                }
              } catch (magickError) {
                console.warn(`ImageMagick processing failed for screenshot ${i}, using original:`, magickError.message);
                // Fallback to original image without processing
                const imageBuffer = fs.readFileSync(tempPath);
                const base64Data = imageBuffer.toString('base64');
                
                if (i === 0) screenshot1 = base64Data;
                else if (i === 1) screenshot2 = base64Data;
                else if (i === 2) screenshot3 = base64Data;
              }
            }
            
            // Clean up temp files
            tempPaths.forEach(tempPath => {
              if (fs.existsSync(tempPath)) {
                fs.unlinkSync(tempPath);
              }
            });
          }
        } catch (imageError) {
          console.warn('Screenshot processing failed, continuing without images:', imageError);
        }
      }

      // Create feedback with screenshots
      const feedback = await storage.createAlphaFeedback(
        session.userId,
        userName,
        user.avatar,
        pageTitle,
        previousPage,
        currentUrl,
        userAgent,
        browserInfo,
        sessionData,
        testerComments,
        ipAddress,
        screenshot1,
        screenshot2,
        screenshot3
      );

      console.log('Alpha feedback submitted:', {
        id: feedback.id,
        userId: session.userId,
        userName,
        pageTitle,
        commentsLength: testerComments.length,
        screenshotCount: files ? files.length : 0
      });

      res.json({ success: true, id: feedback.id });
    } catch (error) {
      console.error('Error submitting alpha feedback:', error);
      res.status(500).json({ error: 'Failed to submit feedback' });
    }
  });

  // Admin Alpha Feedback Interface
  app.get('/admin/alpha-feedback', async (req: Request, res: Response) => {
    try {
      const session = req.session as SessionData;
      
      if (!session.userId) {
        return res.redirect('/demo');
      }

      const user = await storage.getUser(session.userId);
      if (!user || !user.roles || !user.roles.includes('ROLE_ADMIN')) {
        return res.status(403).send('Access denied - Admin privileges required');
      }

      const feedbacks = await storage.getAllAlphaFeedback();
      
      // Convert UTC timestamps to Atlantic time for display
      const feedbacksWithAtlanticTime = feedbacks.map(feedback => ({
        ...feedback,
        atlanticTimestamp: new Date(feedback.timestamp).toLocaleString('en-US', {
          timeZone: 'America/Halifax',
          year: 'numeric',
          month: 'short',
          day: 'numeric',
          hour: 'numeric',
          minute: '2-digit',
          hour12: true
        })
      }));
      
      const html = await templateEngine.render('admin-alpha-feedback.html.twig', {
        user,
        feedbacks: feedbacksWithAtlanticTime,
        pageTitle: 'Admin - Alpha Testing Feedback'
      });
      
      res.send(html);
    } catch (error) {
      console.error('Error loading admin alpha feedback:', error);
      res.status(500).send('Error loading feedback interface');
    }
  });

  // Get specific alpha feedback details
  app.get('/api/alpha-feedback/:id', async (req: Request, res: Response) => {
    try {
      const session = req.session as SessionData;
      
      if (!session.userId) {
        return res.status(401).json({ error: 'Authentication required' });
      }

      const user = await storage.getUser(session.userId);
      console.log(`Alpha feedback access check for user ${session.userId}:`, { 
        hasUser: !!user, 
        roles: user?.roles,
        isAdmin: user?.roles?.includes('ROLE_ADMIN') || session.userId === 1 
      });
      
      // Allow user ID 1 (Dilly) as admin, or check for ROLE_ADMIN
      const isAdmin = session.userId === 1 || (user?.roles && user.roles.includes('ROLE_ADMIN'));
      if (!user || !isAdmin) {
        return res.status(403).json({ error: 'Admin privileges required' });
      }

      const feedbackId = parseInt(req.params.id);
      const feedback = await storage.getAlphaFeedbackById(feedbackId);
      
      if (!feedback) {
        return res.status(404).json({ error: 'Feedback not found' });
      }

      res.json(feedback);
    } catch (error) {
      console.error('Error getting alpha feedback:', error);
      res.status(500).json({ error: 'Failed to get feedback' });
    }
  });

  // Mark alpha feedback as resolved
  app.post('/api/alpha-feedback/:id/resolve', async (req: Request, res: Response) => {
    try {
      const session = req.session as SessionData;
      
      if (!session.userId) {
        return res.status(401).json({ error: 'Authentication required' });
      }

      const user = await storage.getUser(session.userId);
      // Allow user ID 1 (Dilly) as admin, or check for ROLE_ADMIN
      const isAdmin = session.userId === 1 || (user?.roles && user.roles.includes('ROLE_ADMIN'));
      if (!user || !isAdmin) {
        return res.status(403).json({ error: 'Admin privileges required' });
      }

      const feedbackId = parseInt(req.params.id);
      const updated = await storage.markAlphaFeedbackResolved(feedbackId);
      
      if (!updated) {
        return res.status(404).json({ error: 'Feedback not found' });
      }

      res.json({ success: true });
    } catch (error) {
      console.error('Error marking feedback as resolved:', error);
      res.status(500).json({ error: 'Failed to mark as resolved' });
    }
  });

  // Reply to alpha feedback
  app.post('/api/alpha-feedback/reply', async (req: Request, res: Response) => {
    try {
      const session = req.session as SessionData;
      
      if (!session.userId) {
        return res.status(401).json({ error: 'Authentication required' });
      }

      const user = await storage.getUser(session.userId);
      // Allow user ID 1 (Dilly) as admin, or check for ROLE_ADMIN
      const isAdmin = session.userId === 1 || (user?.roles && user.roles.includes('ROLE_ADMIN'));
      if (!user || !isAdmin) {
        return res.status(403).json({ error: 'Admin privileges required' });
      }

      const { feedbackId, userId, message, markResolved } = req.body;
      
      if (!feedbackId || !userId || !message) {
        return res.status(400).json({ error: 'Missing required fields' });
      }

      // Save the reply to the feedback
      const updated = await storage.saveAlphaFeedbackReply(feedbackId, message, session.userId);
      
      if (!updated) {
        return res.status(404).json({ error: 'Feedback not found' });
      }

      // Mark as resolved if requested
      if (markResolved) {
        await storage.markAlphaFeedbackResolved(feedbackId);
      }

      res.json({ success: true, message: 'Reply saved successfully' });
    } catch (error) {
      console.error('Error sending feedback reply:', error);
      res.status(500).json({ error: 'Failed to send reply' });
    }
  });

  // Admin user simulation for viewing pages as specific users
  app.post('/api/admin/simulate-user', async (req: Request, res: Response) => {
    try {
      const session = req.session as SessionData;
      
      if (!session.userId) {
        return res.status(401).json({ error: 'Authentication required' });
      }

      const adminUser = await storage.getUser(session.userId);
      // Allow user ID 1 (Dilly) as admin, or check for ROLE_ADMIN  
      const isAdmin = session.userId === 1 || (adminUser?.roles && adminUser.roles.includes('ROLE_ADMIN'));
      if (!adminUser || !isAdmin) {
        return res.status(403).json({ error: 'Admin privileges required' });
      }

      const { userId } = req.body;
      const targetUser = await storage.getUser(userId);
      
      if (!targetUser) {
        return res.status(404).json({ error: 'Target user not found' });
      }

      // Store original admin session for restoration
      req.session.originalAdminId = session.userId;
      req.session.originalAdminUsername = session.username;
      req.session.isSimulatingUser = true;
      
      // Temporarily switch to target user
      req.session.userId = targetUser.id;
      req.session.username = targetUser.email;

      console.log('Admin user simulation:', {
        adminId: adminUser.id,
        targetUserId: targetUser.id,
        targetUsername: targetUser.email
      });

      res.json({ success: true, message: 'Now simulating user: ' + targetUser.email });
    } catch (error) {
      console.error('Error simulating user:', error);
      res.status(500).json({ error: 'Failed to simulate user' });
    }
  });

  // Restore admin session
  app.post('/api/admin/restore-session', async (req: Request, res: Response) => {
    try {
      const session = req.session as any;
      
      if (!session.isSimulatingUser || !session.originalAdminId) {
        return res.status(400).json({ error: 'No user simulation active' });
      }

      // Restore original admin session
      session.userId = session.originalAdminId;
      session.username = session.originalAdminUsername;
      delete session.originalAdminId;
      delete session.originalAdminUsername;
      delete session.isSimulatingUser;

      res.json({ success: true, message: 'Admin session restored' });
    } catch (error) {
      console.error('Error restoring admin session:', error);
      res.status(500).json({ error: 'Failed to restore session' });
    }
  });

  // Member Import Routes
  app.get('/admin/member-import', async (req: Request, res: Response) => {
    try {
      const session = req.session as SessionData;
      
      if (!session.userId) {
        return res.redirect('/demo');
      }

      const user = await storage.getUser(session.userId);
      const isAdmin = session.userId === 1 || (user?.roles && user.roles.includes('ROLE_ADMIN'));
      if (!user || !isAdmin) {
        return res.status(403).send('Access denied - Admin privileges required');
      }

      const stats = await storage.getAdminStats();
      const importedCount = await storage.getImportedMemberCount();
      const welcomePending = await storage.getWelcomePendingCount();
      const recentImports = await storage.getRecentImports(5);
      
      const html = await templateEngine.render('admin-member-import.html.twig', {
        user,
        stats,
        importedCount,
        welcomePending,
        lastImportDate: recentImports.length > 0 ? recentImports[0].importDate : 'Never',
        recentImports
      });
      
      res.send(html);
    } catch (error) {
      console.error('Error loading member import page:', error);
      res.status(500).send('Error loading member import interface');
    }
  });

  // Download Excel template
  app.get('/api/admin/member-import/template', async (req: Request, res: Response) => {
    try {
      const session = req.session as SessionData;
      
      if (!session.userId) {
        return res.status(401).json({ error: 'Authentication required' });
      }

      const user = await storage.getUser(session.userId);
      const isAdmin = session.userId === 1 || (user?.roles && user.roles.includes('ROLE_ADMIN'));
      if (!user || !isAdmin) {
        return res.status(403).json({ error: 'Admin privileges required' });
      }

      const { memberImportService } = await import('./member-import');
      const templateBuffer = memberImportService.generateExcelTemplate();
      
      res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
      res.setHeader('Content-Disposition', 'attachment; filename="LocalLendables_Member_Import_Template.xlsx"');
      res.send(templateBuffer);
    } catch (error) {
      console.error('Error generating Excel template:', error);
      res.status(500).json({ error: 'Failed to generate template' });
    }
  });

  // Process member import
  app.post('/api/admin/member-import', upload.single('excelFile'), async (req: Request, res: Response) => {
    try {
      const session = req.session as SessionData;
      
      if (!session.userId) {
        return res.status(401).json({ error: 'Authentication required' });
      }

      const user = await storage.getUser(session.userId);
      const isAdmin = session.userId === 1 || (user?.roles && user.roles.includes('ROLE_ADMIN'));
      if (!user || !isAdmin) {
        return res.status(403).json({ error: 'Admin privileges required' });
      }

      if (!req.file) {
        return res.status(400).json({ error: 'No Excel file uploaded' });
      }

      const { memberImportService } = await import('./member-import');
      const result = await memberImportService.processExcelImport(req.file.buffer, session.userId);
      
      // Log import activity
      if (result.imported > 0) {
        await storage.logImportActivity(session.userId, result.imported);
      }
      
      res.json(result);
    } catch (error) {
      console.error('Error processing member import:', error);
      res.status(500).json({ error: 'Failed to process import' });
    }
  });

  // Welcome sequence progress
  app.get('/api/welcome/progress/:userId', async (req: Request, res: Response) => {
    try {
      const userId = parseInt(req.params.userId);
      const { memberImportService } = await import('./member-import');
      const progress = await memberImportService.getWelcomeProgress(userId);
      
      res.json(progress);
    } catch (error) {
      console.error('Error getting welcome progress:', error);
      res.status(500).json({ error: 'Failed to get progress' });
    }
  });

  // Complete welcome sequence
  app.post('/api/welcome/complete/:userId', async (req: Request, res: Response) => {
    try {
      const userId = parseInt(req.params.userId);
      const session = req.session as SessionData;
      
      if (session.userId !== userId) {
        return res.status(403).json({ error: 'Access denied' });
      }
      
      const { memberImportService } = await import('./member-import');
      const success = await memberImportService.completeWelcomeSequence(userId);
      
      res.json({ success });
    } catch (error) {
      console.error('Error completing welcome sequence:', error);
      res.status(500).json({ error: 'Failed to complete welcome' });
    }
  });

  // Admin conversation cleanup
  app.get('/api/admin/empty-conversations/count', async (req: Request, res: Response) => {
    try {
      const session = req.session as SessionData;
      
      if (!session.userId) {
        return res.status(401).json({ error: 'Authentication required' });
      }

      const user = await storage.getUser(session.userId);
      const isAdmin = session.userId === 1 || (user?.roles && user.roles.includes('ROLE_ADMIN'));
      if (!user || !isAdmin) {
        return res.status(403).json({ error: 'Admin privileges required' });
      }

      const count = await storage.getEmptyConversationCount();
      res.json({ count });
    } catch (error) {
      console.error('Error getting empty conversation count:', error);
      res.status(500).json({ error: 'Failed to get count' });
    }
  });

  app.delete('/api/admin/empty-conversations', async (req: Request, res: Response) => {
    try {
      const session = req.session as SessionData;
      
      if (!session.userId) {
        return res.status(401).json({ error: 'Authentication required' });
      }

      const user = await storage.getUser(session.userId);
      const isAdmin = session.userId === 1 || (user?.roles && user.roles.includes('ROLE_ADMIN'));
      if (!user || !isAdmin) {
        return res.status(403).json({ error: 'Admin privileges required' });
      }

      const deletedCount = await storage.deleteEmptyConversations();
      res.json({ 
        success: true, 
        deletedCount,
        message: `Deleted ${deletedCount} empty conversations` 
      });
    } catch (error) {
      console.error('Error deleting empty conversations:', error);
      res.status(500).json({ error: 'Failed to delete conversations' });
    }
  });

  // Admin duplicate cleanup
  app.get('/api/admin/duplicate-requests', async (req: Request, res: Response) => {
    try {
      const session = req.session as SessionData;
      
      if (!session.userId) {
        return res.status(401).json({ error: 'Authentication required' });
      }

      const user = await storage.getUser(session.userId);
      const isAdmin = session.userId === 1 || (user?.roles && user.roles.includes('ROLE_ADMIN'));
      if (!user || !isAdmin) {
        return res.status(403).json({ error: 'Admin privileges required' });
      }

      const duplicates = await storage.getDuplicateBorrowRequests();
      res.json({ duplicates });
    } catch (error) {
      console.error('Error getting duplicate requests:', error);
      res.status(500).json({ error: 'Failed to get duplicates' });
    }
  });

  app.delete('/api/admin/duplicate-requests', async (req: Request, res: Response) => {
    try {
      const session = req.session as SessionData;
      
      if (!session.userId) {
        return res.status(401).json({ error: 'Authentication required' });
      }

      const user = await storage.getUser(session.userId);
      const isAdmin = session.userId === 1 || (user?.roles && user.roles.includes('ROLE_ADMIN'));
      if (!user || !isAdmin) {
        return res.status(403).json({ error: 'Admin privileges required' });
      }

      const deletedCount = await storage.deleteDuplicateBorrowRequests();
      res.json({ 
        success: true, 
        deletedCount,
        message: `Deleted ${deletedCount} duplicate borrow requests` 
      });
    } catch (error) {
      console.error('Error deleting duplicate requests:', error);
      res.status(500).json({ error: 'Failed to delete duplicates' });
    }
  });

  // Delete borrowed item
  app.delete('/api/borrowed-items/:id', async (req: Request, res: Response) => {
    try {
      const session = req.session as SessionData;
      const borrowId = parseInt(req.params.id);
      
      if (!session.userId) {
        return res.status(401).json({ error: 'Authentication required' });
      }

      // Check if user is admin
      const user = await storage.getUser(session.userId);
      const isAdmin = session.userId === 1 || (user?.roles && user.roles.includes('ROLE_ADMIN'));
      
      let success = false;
      if (isAdmin) {
        // Admin can delete any borrowed item
        success = await storage.deleteBorrowedItemAdmin(borrowId);
      } else {
        // Regular users can only delete their own borrowed items
        success = await storage.deleteBorrowedItem(borrowId, session.userId);
      }
      
      if (success) {
        res.json({ success: true, message: 'Item removed successfully' });
      } else {
        res.status(404).json({ error: 'Item not found or not authorized to delete' });
      }
    } catch (error) {
      console.error('Error deleting borrowed item:', error);
      res.status(500).json({ error: 'Failed to delete item' });
    }
  });

  // CSS Debug Interface
  app.get('/css-debug', async (req: Request, res: Response) => {
    try {
      const session = req.session as SessionData;
      let currentUser = null;
      
      if (session?.userId) {
        currentUser = await storage.getUser(session.userId);
      }
      
      const html = await templateEngine.render('css-debug.html.twig', {
        user: currentUser,
        currentUser: currentUser
      });
      
      res.send(html);
    } catch (error) {
      console.error('Error rendering CSS debug interface:', error);
      res.status(500).send('Internal Server Error');
    }
  });

  // Search API endpoint
  app.get('/api/search', async (req: Request, res: Response) => {
    try {
      const query = req.query.q as string;
      
      if (!query || query.length < 3) {
        return res.json({ results: [] });
      }

      const searchQuery = query.toLowerCase().trim();
      
      // Get all items for searching
      const allItems = await storage.getAllAvailableItems(false);
      const categories = await storage.getAllCategories();
      
      // Search through items
      const itemResults = allItems.filter(item => {
        const titleMatch = item.name?.toLowerCase().includes(searchQuery);
        const descriptionMatch = item.description?.toLowerCase().includes(searchQuery);
        const categoryMatch = item.categoryName?.toLowerCase().includes(searchQuery);
        const ownerMatch = item.ownerFirstName?.toLowerCase().includes(searchQuery) || 
                          item.ownerLastName?.toLowerCase().includes(searchQuery) ||
                          item.ownerShowName?.toLowerCase().includes(searchQuery);
        
        return titleMatch || descriptionMatch || categoryMatch || ownerMatch;
      }).slice(0, 8); // Limit to 8 results

      // Search through categories
      const categoryResults = categories.filter(cat => 
        cat.category?.toLowerCase().includes(searchQuery)
      ).slice(0, 3); // Limit to 3 category results

      // Static page content search
      const staticPages = [
        {
          title: "Browse All Items",
          url: "/browse",
          description: "Find items to borrow or lend in our community",
          keywords: ["browse", "find", "search", "items", "lendables", "borrow", "lend"]
        },
        {
          title: "Create Item",
          url: "/create-item", 
          description: "Add a new item to lend to the community",
          keywords: ["create", "add", "new", "item", "lendable", "offer", "share"]
        },
        {
          title: "Our Manager",
          url: "/our-manager",
          description: "Learn about our community manager and support",
          keywords: ["manager", "support", "help", "community", "contact", "administrator"]
        },
        {
          title: "Send Reminder",
          url: "/send-reminder",
          description: "Send reminders about borrowed items",
          keywords: ["reminder", "return", "borrowed", "notification", "message", "due"]
        }
      ];

      const pageResults = staticPages.filter(page => {
        const titleMatch = page.title.toLowerCase().includes(searchQuery);
        const descMatch = page.description.toLowerCase().includes(searchQuery);
        const keywordMatch = page.keywords.some(keyword => 
          keyword.toLowerCase().includes(searchQuery) || searchQuery.includes(keyword.toLowerCase())
        );
        return titleMatch || descMatch || keywordMatch;
      }).slice(0, 3);

      const results = {
        items: itemResults.map(item => ({
          type: 'item',
          id: item.id,
          title: item.name,
          description: item.description,
          category: item.categoryName,
          owner: item.ownerShowName || `${item.ownerFirstName} ${item.ownerLastName}`,
          image: item.thumbnailLogo || item.primaryLogo, // Always prefer thumbnail for listings per Image Canon
          url: `/item/${item.id}`,
          price: item.pricePerDay
        })),
        categories: categoryResults.map(cat => ({
          type: 'category',
          id: cat.id,
          title: cat.category,
          url: `/browse?category=${encodeURIComponent(cat.category)}`
        })),
        pages: pageResults.map(page => ({
          type: 'page',
          title: page.title,
          description: page.description,
          url: page.url
        }))
      };

      res.json({ results });
    } catch (error) {
      console.error('Search error:', error);
      res.status(500).json({ error: 'Search failed' });
    }
  });

  // Edit item page (owner only)
  app.get('/edit-item/:id', async (req: Request, res: Response) => {
    // Add cache busting headers
    res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
    res.setHeader('Pragma', 'no-cache');
    res.setHeader('Expires', '0');
    const session = req.session as SessionData;
    if (!session?.userId) {
      return res.redirect('/login');
    }

    const itemId = parseInt(req.params.id);
    if (isNaN(itemId)) {
      return res.status(400).send('Invalid item ID');
    }

    try {
      const item = await storage.getItem(itemId);
      if (!item) {
        return res.status(404).send('Item not found');
      }

      // Check if user can edit this item (owner or admin)
      const canEdit = await storage.canUserEditItem(session.userId, itemId);
      if (!canEdit) {
        return res.status(403).send('You can only edit your own items');
      }

      const currentUser = await storage.getUser(session.userId);
      if (!currentUser) {
        return res.redirect('/login');
      }

      // Get all categories for the dropdown
      const categories = await storage.getAllCategories();

      const html = await templateEngine.render('edit-item.html.twig', {
        item,
        currentUser,
        user: currentUser,
        breadcrumbs: generateBreadcrumbs('edit-item', item?.name, currentUser?.id),
        userAvatar: await getUserAvatarPath(currentUser),
        categories
      });
      res.send(html);
    } catch (error) {
      console.error('Error loading edit item page:', error);
      res.status(500).send('Internal Server Error');
    }
  });

  // Update item (PUT)
  app.put('/api/items/:id', upload.array('newImages', 10), async (req: Request, res: Response) => {
    const session = req.session as SessionData;
    if (!session?.userId) {
      return res.status(401).json({ error: 'Not authenticated' });
    }

    const itemId = parseInt(req.params.id);
    if (isNaN(itemId)) {
      return res.status(400).json({ error: 'Invalid item ID' });
    }

    try {
      const item = await storage.getItem(itemId);
      if (!item) {
        return res.status(404).json({ error: 'Item not found' });
      }

      // Check if user can edit this item (owner or admin)
      const canEdit = await storage.canUserEditItem(session.userId, itemId);
      if (!canEdit) {
        return res.status(403).json({ error: 'You can only edit your own items' });
      }

      // Helper function to parse decimal fields (handle empty strings)
      const parseDecimal = (value: any) => {
        if (value === '' || value === null || value === undefined) return null;
        const parsed = parseFloat(value);
        return isNaN(parsed) ? null : parsed.toFixed(2);
      };

      // Debug: Log request body to see what we're receiving
      console.log('Edit item request body keys:', Object.keys(req.body));
      console.log('Security Deposit value:', req.body.deposit);
      console.log('Security Deposit parsed result:', parseDecimal(req.body.deposit));
      console.log('Daily Rate value:', req.body.dailyRate);

      const updatedItem = await storage.updateItem(itemId, {
        name: req.body.title,
        description: req.body.description,
        specialInstructions: req.body.specialInstructions || null,
        condition: req.body.condition,
        category: parseInt(req.body.category),
        pricePerDay: req.body.dailyRate ? parseFloat(req.body.dailyRate) : null,
        deposit: parseDecimal(req.body.deposit)
      });

      // Handle image updates if needed
      const files = req.files as Express.Multer.File[] | undefined;
      const newImageFiles = files?.filter(f => f.fieldname === 'newImages') || [];
      console.log('Image upload debug - Files received:', files?.length || 0, 'New images:', newImageFiles.length);
      
      if (newImageFiles && newImageFiles.length > 0) {
        console.log('Processing new images:', newImageFiles.map(f => ({ name: f.originalname, path: f.path, size: f.size, hasBuffer: !!f.buffer })));
        
        try {
          // Process new images with Image Canon standards (400px width, 85% quality JPG)
          console.log('Applying Image Canon standards: 400px wide JPG at 85% quality with white background');
          
          // Handle both file paths and buffer data for pasted images
          const imagePaths = [];
          const tempFilesToCleanup = []; // Track temp files for cleanup
          
          for (let fileIndex = 0; fileIndex < newImageFiles.length; fileIndex++) {
            const file = newImageFiles[fileIndex];
            if (file.path) {
              imagePaths.push(file.path);
            } else if (file.buffer) {
              // Save buffer to temporary file for processing
              const fs = await import('fs');
              const path = await import('path');
              const tempDir = 'temp/uploads';
              
              // CRITICAL FIX: Enhanced unique temp filename with random component
              const timestamp = Date.now();
              const random = Math.floor(Math.random() * 10000);
              const tempFilename = `temp_${timestamp}_${random}_${fileIndex}_${file.originalname}`;
              const tempPath = path.join(tempDir, tempFilename);
              
              // Ensure temp directory exists
              if (!fs.existsSync(tempDir)) {
                fs.mkdirSync(tempDir, { recursive: true });
              }
              
              fs.writeFileSync(tempPath, file.buffer);
              imagePaths.push(tempPath);
              tempFilesToCleanup.push(tempPath);
              console.log('Saved pasted image to temp file:', tempPath);
            }
          }
          
          if (imagePaths.length > 0) {
            let imageResults;
            try {
              imageResults = await imageProcessor.processItemImages(
                imagePaths,
                session.userId!
              );
              console.log('Image Canon processing results:', imageResults);
              
              // CRITICAL FIX: Clean up temporary files after successful processing
              const fs = await import('fs');
              for (const tempPath of tempFilesToCleanup) {
                try {
                  if (fs.existsSync(tempPath)) {
                    fs.unlinkSync(tempPath);
                    console.log(`✓ Cleaned up temp file: ${tempPath}`);
                  }
                } catch (cleanupError) {
                  console.warn(`Failed to cleanup temp file ${tempPath}:`, cleanupError);
                }
              }
            } catch (error: any) {
              console.warn('Image Canon processing failed, using simple processor fallback:', error?.message || String(error));
              
              // CRITICAL FIX: Clean up temp files even if processing failed
              const fs = await import('fs');
              for (const tempPath of tempFilesToCleanup) {
                try {
                  if (fs.existsSync(tempPath)) {
                    fs.unlinkSync(tempPath);
                    console.log(`✓ Cleaned up temp file after failure: ${tempPath}`);
                  }
                } catch (cleanupError) {
                  console.warn(`Failed to cleanup temp file ${tempPath}:`, cleanupError);
                }
              }
              // Import simple processor for fallback
              const { simpleImageProcessor } = await import('./simple-image-processor.js');
              
              // Process images with simple processor as fallback
              const processedImages: string[] = [];
              for (let i = 0; i < imagePaths.length; i++) {
                try {
                  // Use enhanced filename generation for fallback too
                  const timestamp = Date.now();
                  const random = Math.floor(Math.random() * 10000);
                  const filename = `${session.userId}_${timestamp}_${random}_fallback_${i}`;
                  const processedFilename = await simpleImageProcessor.processImage(imagePaths[i], filename);
                  processedImages.push(processedFilename);
                } catch (fallbackError) {
                  console.error(`Failed to process image ${imagePaths[i]} even with fallback:`, fallbackError);
                }
              }
              
              imageResults = {
                primaryLogo: processedImages[0] || '',
                extraImages: processedImages.slice(1)
              };
              console.log('Fallback processing results:', imageResults);
            }

            // Update item with new standardized images (add to existing, don't replace)
            if (storage.updateItemImages) {
              const currentItem = await storage.getItem(itemId);
              
              // Handle "make primary" from existing images
              if (req.body.newPrimaryImage) {
                const requestedPrimary = req.body.newPrimaryImage;
                const existingExtras = currentItem?.extraImages ? currentItem.extraImages.split(',').filter(img => img.trim()) : [];
                
                // Check if requested primary is in extra images
                if (existingExtras.includes(requestedPrimary)) {
                  // Move current primary to extras (if exists and is not placeholder)
                  if (currentItem?.primaryLogo && 
                      currentItem.primaryLogo !== 'default-placeholder.jpg' && 
                      !currentItem.primaryLogo.includes('placeholder')) {
                    existingExtras.push(currentItem.primaryLogo);
                  }
                  // Remove new primary from extras
                  const filteredExtras = existingExtras.filter(img => img !== requestedPrimary);
                  // Add any new images to extras
                  const allExtraImages = [...filteredExtras, ...imageResults.extraImages];
                  
                  await storage.updateItemImages(itemId, requestedPrimary, allExtraImages.join(','));
                  console.log('Primary image changed to existing image:', requestedPrimary);
                  return;
                }
              }
              
              // Handle "make primary" from new uploaded images
              if (req.body.newImagePrimaryIndex !== undefined) {
                const primaryIndex = parseInt(req.body.newImagePrimaryIndex);
                console.log('New image primary index:', primaryIndex, 'Total new images:', imageResults.extraImages.length + 1);
                
                if (primaryIndex === 0 && imageResults.primaryLogo) {
                  // First new image becomes primary
                  const existingExtras = currentItem?.extraImages ? currentItem.extraImages.split(',').filter(img => img.trim()) : [];
                  let allExtraImages = [...existingExtras];
                  
                  // Add current primary to extras if exists and is not placeholder
                  if (currentItem?.primaryLogo && 
                      currentItem.primaryLogo !== 'default-placeholder.jpg' && 
                      !currentItem.primaryLogo.includes('placeholder')) {
                    allExtraImages.push(currentItem.primaryLogo);
                  }
                  
                  // Add remaining new images to extras
                  allExtraImages = [...allExtraImages, ...imageResults.extraImages];
                  
                  await storage.updateItemImages(itemId, imageResults.primaryLogo, allExtraImages.join(','));
                  console.log('New uploaded image set as primary with Image Canon standards');
                } else if (primaryIndex > 0 && imageResults.extraImages[primaryIndex - 1]) {
                  // One of the extra new images becomes primary
                  const newPrimary = imageResults.extraImages[primaryIndex - 1];
                  const existingExtras = currentItem?.extraImages ? currentItem.extraImages.split(',').filter(img => img.trim()) : [];
                  let allExtraImages = [...existingExtras];
                  
                  // Add current primary to extras if exists and is not placeholder
                  if (currentItem?.primaryLogo && 
                      currentItem.primaryLogo !== 'default-placeholder.jpg' && 
                      !currentItem.primaryLogo.includes('placeholder')) {
                    allExtraImages.push(currentItem.primaryLogo);
                  }
                  
                  // Add first new image to extras
                  if (imageResults.primaryLogo) {
                    allExtraImages.push(imageResults.primaryLogo);
                  }
                  
                  // Add other new images to extras (excluding the chosen primary)
                  imageResults.extraImages.forEach((img, idx) => {
                    if (idx !== primaryIndex - 1) {
                      allExtraImages.push(img);
                    }
                  });
                  
                  await storage.updateItemImages(itemId, newPrimary, allExtraImages.join(','));
                  console.log('New uploaded image at index', primaryIndex, 'set as primary with Image Canon standards');
                }
              } else {
                // Standard add new images behavior - preserve existing primary
                const existingExtras = currentItem?.extraImages ? currentItem.extraImages.split(',').filter(img => img.trim()) : [];
                let allExtraImages = [...existingExtras];
                let finalPrimary = currentItem?.primaryLogo;
                
                // Check if current primary is a placeholder (should be treated as no primary)
                const hasRealPrimary = currentItem?.primaryLogo && 
                  currentItem.primaryLogo !== 'default-placeholder.jpg' && 
                  !currentItem.primaryLogo.includes('placeholder');
                
                if (hasRealPrimary && imageResults.primaryLogo) {
                  // Add new primary image to extras, keep existing real primary
                  allExtraImages = [imageResults.primaryLogo, ...allExtraImages, ...imageResults.extraImages];
                } else if (!hasRealPrimary && imageResults.primaryLogo) {
                  // No real primary (or placeholder), use first new image as primary
                  finalPrimary = imageResults.primaryLogo;
                  allExtraImages = [...allExtraImages, ...imageResults.extraImages];
                } else {
                  // Just add new extra images
                  allExtraImages = [...allExtraImages, ...imageResults.extraImages];
                }
                
                await storage.updateItemImages(itemId, finalPrimary, allExtraImages.join(','));
                console.log('Item images updated with Image Canon standards - added as additional images');
              }
            } else {
              console.warn('updateItemImages method not available on storage');
            }
          } else {
            console.warn('No valid image paths found for processing');
          }
        } catch (imageError) {
          console.error('Image Canon processing failed:', imageError);
          // Continue with item update even if image processing fails
        }
      }

      // Handle removed images
      if (req.body.removedImages) {
        const removedImages = JSON.parse(req.body.removedImages);
        console.log('Images to remove:', removedImages);
        
        if (removedImages.length > 0) {
          const currentItem = await storage.getItem(itemId);
          let updatedPrimaryLogo = currentItem?.primaryLogo;
          let updatedExtraImages = currentItem?.extraImages ? currentItem.extraImages.split(',').filter(img => img.trim()) : [];
          
          // Remove images from primary/extra lists
          removedImages.forEach(imageToRemove => {
            if (updatedPrimaryLogo === imageToRemove) {
              // If removing primary, promote first extra to primary
              if (updatedExtraImages.length > 0) {
                updatedPrimaryLogo = updatedExtraImages.shift();
              } else {
                updatedPrimaryLogo = null;
              }
            }
            // Remove from extras
            updatedExtraImages = updatedExtraImages.filter(img => img !== imageToRemove);
          });
          
          // Update database
          await storage.updateItemImages(itemId, updatedPrimaryLogo, updatedExtraImages.join(','));
          console.log('Updated images after removal - Primary:', updatedPrimaryLogo, 'Extras:', updatedExtraImages);
        }
      }

      res.json({ success: true, item: updatedItem });
    } catch (error) {
      console.error('Error updating item:', error);
      res.status(500).json({ error: 'Failed to update item' });
    }
  });

  // Delete item (DELETE)
  app.delete('/api/items/:id', async (req: Request, res: Response) => {
    const session = req.session as SessionData;
    if (!session?.userId) {
      return res.status(401).json({ error: 'Not authenticated' });
    }

    const itemId = parseInt(req.params.id);
    if (isNaN(itemId)) {
      return res.status(400).json({ error: 'Invalid item ID' });
    }

    try {
      const item = await storage.getItem(itemId);
      if (!item) {
        return res.status(404).json({ error: 'Item not found' });
      }

      // Check if user can edit this item (owner or admin)
      const canEdit = await storage.canUserEditItem(session.userId, itemId);
      if (!canEdit) {
        return res.status(403).json({ error: 'You can only delete your own items' });
      }

      await storage.deleteItem(itemId);
      res.json({ success: true });
    } catch (error) {
      console.error('Error deleting item:', error);
      
      // Check for specific error messages and provide user-friendly responses
      if (error instanceof Error) {
        if (error.message.includes('active loans that must be returned')) {
          res.status(400).json({ 
            error: 'Cannot delete this item because it is currently on loan. Please wait for it to be returned first.' 
          });
        } else if (error.message.includes('pending or approved borrow requests')) {
          res.status(400).json({ 
            error: 'Cannot delete this item because it has pending reservations. Please resolve all borrow requests first.' 
          });
        } else {
          res.status(500).json({ error: 'Failed to delete item' });
        }
      } else {
        res.status(500).json({ error: 'Failed to delete item' });
      }
    }
  });

  // API endpoint to get item loan calendar
  app.get('/api/items/:id/loan-calendar', async (req, res) => {
    try {
      const itemId = parseInt(req.params.id);
      const loans = await storage.getItemLoanCalendar(itemId);
      res.json({ loans });
    } catch (error) {
      console.error('Error fetching loan calendar:', error);
      res.status(500).json({ error: 'Failed to fetch loan calendar' });
    }
  });

  // API endpoint to get item status (available/on loan)
  app.get('/api/items/:id/status', async (req, res) => {
    try {
      const itemId = parseInt(req.params.id);
      const loanStatus = await storage.getItemLoanStatus(itemId);
      res.json(loanStatus);
    } catch (error) {
      console.error('Error fetching item status:', error);
      res.status(500).json({ error: 'Failed to fetch item status' });
    }
  });

  // API endpoint to get loans where user is the owner
  app.get('/api/loans/owner/:userId', async (req, res) => {
    try {
      const userId = parseInt(req.params.userId);
      
      // Get loan agreements for items owned by this user using storage method
      const ownerLoans = await storage.getOwnerLoans(userId);
      
      res.json({ success: true, loans: ownerLoans });
    } catch (error) {
      console.error('Error fetching owner loans:', error);
      res.status(500).json({ error: 'Failed to fetch loans' });
    }
  });

  // Messages Inbox route
  app.get('/messages-inbox/:userId?', async (req: Request, res: Response) => {
    try {
      const userId = parseInt(req.params.userId || '1');
      const currentUser = await storage.getUser(userId);
      
      if (!currentUser) {
        return res.status(404).send('User not found');
      }

      // Get both regular messages and unreturned item messages
      const { received, sent } = await storage.getAllUserMessages(userId);
      const { received: unreturnedReceived, sent: unreturnedSent, threaded } = await storage.getUnreturnedItemMessages(userId);

      // Generate breadcrumbs
      const breadcrumbs = generateBreadcrumbs('messages', 'Messages', userId);

      const html = await templateEngine.render('messages-inbox.html.twig', {
        receivedMessages: received,
        sentMessages: sent,
        unreturnedReceived,
        unreturnedSent,
        threadedMessages: threaded,
        breadcrumbs,
        currentUser,
        user: currentUser
      });

      res.send(html);
    } catch (error) {
      console.error('Error loading messages inbox:', error);
      res.status(500).send('Error loading messages inbox');
    }
  });

  // Chat route - handles both items and hour offerings
  app.get('/chat/:itemId/:ownerId/:borrowerId', async (req: Request, res: Response) => {
    try {
      const itemId = parseInt(req.params.itemId);
      const ownerId = parseInt(req.params.ownerId);
      const borrowerId = parseInt(req.params.borrowerId);
      const session = req.session as SessionData;
      const currentUserId = session?.userId;
      
      if (!currentUserId) {
        return res.status(401).send('Authentication required');
      }
      const isModal = req.query.modal === 'true';
      const isHourOffering = req.query.hourOffering === 'true';

      // Get or create conversation
      let conversation = await storage.getConversation(itemId, ownerId, borrowerId);
      if (!conversation) {
        conversation = await storage.createConversation({
          itemId,
          ownerId,
          borrowerId,
          status: 'active'
        });
      }

      // Get conversation details with users and item info
      const conversationDetails = await storage.getConversationById(conversation.id);
      const messages = await storage.getChatMessages(conversation.id);

      let item;
      let owner;
      let borrower;
      
      if (isHourOffering) {
        // Get hour offering details instead of item
        const hourOfferings = await storage.getAvailableHourOfferings();
        item = hourOfferings.find(offering => offering.id === itemId);
        if (item) {
          // Transform hour offering to look like an item for chat template
          item = {
            id: item.id,
            title: item.title,
            description: item.description,
            name: item.title, // Use title as name for consistency
            owner: item.userId,
            isHourOffering: true
          };
        }
      } else {
        // Get regular item details
        item = await storage.getItem(itemId);
      }
      
      owner = await storage.getUser(ownerId);
      borrower = await storage.getUser(borrowerId);
      const currentUser = await storage.getUser(currentUserId);

      // Use different template for modal vs full page
      const templateName = isModal ? 'chat-modal-content.html.twig' : 'chat.html.twig';
      
      const html = await templateEngine.render(templateName, {
        conversation: conversationDetails,
        messages,
        item,
        owner,
        borrower,
        currentUser,
        currentUserId,
        user: currentUser,  // Add user variable for common header
        isHourOffering: isHourOffering
      });

      res.send(html);
    } catch (error) {
      console.error('Error loading chat:', error);
      res.status(500).send('Error loading chat');
    }
  });

  // API: Start or get conversation
  app.post('/api/chat/start', async (req: Request, res: Response) => {
    try {
      console.log('Chat start request body:', req.body);
      const { itemId, ownerId } = req.body;
      const session = req.session as SessionData;
      const borrowerId = session?.userId;
      
      if (!borrowerId) {
        return res.status(401).json({ error: 'Authentication required' });
      }
      
      if (!itemId || !ownerId) {
        return res.status(400).json({ error: 'Missing itemId or ownerId' });
      }
      
      // Get or create conversation
      let conversation = await storage.getConversation(itemId, ownerId, borrowerId);
      if (!conversation) {
        conversation = await storage.createConversation({
          itemId,
          ownerId,
          borrowerId,
          status: 'active'
        });
      }
      
      console.log('Conversation created/found:', conversation.id);
      res.json({ conversationId: conversation.id, success: true });
    } catch (error) {
      console.error('Error starting conversation:', error);
      res.status(500).json({ error: 'Failed to start conversation' });
    }
  });

  // API: Send audio message - use audioUpload for disk storage
  app.post('/api/chat/:conversationId/audio', audioUpload.single('recording'), async (req: Request, res: Response) => {
    try {
      const conversationId = parseInt(req.params.conversationId);
      const session = req.session as SessionData;
      const senderId = session?.userId;
      
      if (!senderId) {
        return res.status(401).json({ error: 'Authentication required' });
      }
      
      console.log('Audio upload request - conversationId:', conversationId, 'senderId:', senderId);
      
      if (!req.file) {
        console.log('No audio file in request');
        return res.status(400).json({ error: 'No audio file provided' });
      }
      
      console.log('Audio file received:', req.file.originalname, 'size:', req.file.size, 'type:', req.file.mimetype);
      
      // Ensure temp/uploads directory exists
      const uploadDir = 'temp/uploads';
      if (!existsSync(uploadDir)) {
        mkdirSync(uploadDir, { recursive: true });
      }
      
      // Generate proper filename
      const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
      const filename = `chat-user${senderId}-${timestamp}.webm`;
      const audioPath = `/temp/uploads/${filename}`;
      
      // File is already saved by audioUpload disk storage
      const savedFilePath = req.file.path;
      console.log('Audio file saved at:', savedFilePath);
      
      if (!savedFilePath || !existsSync(savedFilePath)) {
        console.error('Audio file not saved properly:', savedFilePath);
        return res.status(400).json({ error: 'Invalid audio file - file not saved properly' });
      }
      console.log('Audio file moved to:', `temp/uploads/${filename}`);
      
      const audioRelativePath = `temp/uploads/${path.basename(savedFilePath)}`;
      const messageData = {
        conversationId,
        senderId,
        messageType: 'audio',
        audioPath: `/${audioRelativePath}`,
        transcription: 'Voicemail saved. They will get your message next time they log in to Local Lendables.'
      };
      
      // Save voicemail message
      const newMessage = await storage.sendChatMessage(messageData);
      console.log('Audio voicemail saved with ID:', newMessage.id);
      
      res.json({ success: true, message: newMessage });
    } catch (error) {
      console.error('Error sending audio message:', error);
      res.status(500).json({ error: 'Failed to send audio message' });
    }
  });

  // Serve audio files from temp/uploads
  app.get('/temp/uploads/:filename', (req: Request, res: Response) => {
    const filename = req.params.filename;
    const audioPath = path.join('temp/uploads', filename);
    
    if (existsSync(audioPath)) {
      res.setHeader('Content-Type', 'audio/webm');
      res.sendFile(path.resolve(audioPath));
    } else {
      res.status(404).json({ error: 'Audio file not found' });
    }
  });

  // API: Get unread messages for user
  app.get('/api/unread-messages/:userId', async (req: Request, res: Response) => {
    try {
      const userId = parseInt(req.params.userId);
      console.log(`Fetching unread messages for user ${userId}`);
      
      // Get regular chat messages
      const messages = await storage.getUserUnreadMessages(userId);
      
      // Debug: Check sender names in messages
      console.log('Unread messages sender names:', messages.map(m => ({ 
        messageId: m.messageId, 
        sender_name: m.sender_name,
        senderShowName: m.senderShowName,
        senderFirstName: m.senderFirstName 
      })));
      
      // Get unread system notifications (must not be dismissed)
      const systemNotifications = await storage.getSystemNotifications(userId);
      const unreadSystemNotifications = systemNotifications.filter(n => !n.isRead && !n.isDismissed && 
        (n.type.startsWith('return_') || n.type.startsWith('owner_') || n.type === 'borrow_request' || n.type === 'agreement_ready'));
      
      // Get item details for system notifications
      const systemMessages = [];
      for (const notification of unreadSystemNotifications) {
        // Extract item name from notification message
        const itemNameMatch = notification.message.match(/"([^"]+)"/);
        const itemName = itemNameMatch ? itemNameMatch[1] : 'Unknown Item';
        
        // Get item details from database
        let itemDetails = null;
        let ownerDetails = null;
        let borrowerDetails = null;
        
        try {
          const items = await storage.getAllAvailableItems();
          itemDetails = items.find(item => item.name === itemName);
          if (itemDetails) {
            ownerDetails = await storage.getUser(itemDetails.owner);
          }
          
          // For borrow requests, get borrower details from related_id
          if (notification.type === 'borrow_request' && notification.relatedId) {
            try {
              const borrowRequest = await storage.getBorrowRequest(notification.relatedId);
              if (borrowRequest) {
                borrowerDetails = await storage.getUser(borrowRequest.borrowerId);
              }
            } catch (error) {
              console.error('Error fetching borrower details:', error);
            }
          }
        } catch (error) {
          console.error('Error fetching item details for notification:', error);
        }
        
        // Extract due date from message (for return notifications)
        const dueDateMatch = notification.message.match(/(\d{1,2}\/\d{1,2}\/\d{4})/);
        const dueDate = dueDateMatch ? dueDateMatch[1] : null;
        
        // Different handling for borrow_request vs return notifications vs agreement notifications
        const systemMessage = {
          messageId: `system_${notification.id}`,
          conversationId: null,
          item_id: notification.itemId || itemDetails?.id || null,
          item_name: itemName,
          item_thumbnail: itemDetails?.primaryLogo || null,
          sender_id: notification.type === 'borrow_request' ? (borrowerDetails?.id || 'system') : 'system',
          sender_name: notification.type === 'borrow_request' 
            ? (borrowerDetails ? (borrowerDetails.showName || borrowerDetails.firstName || 'Unknown Borrower') : 'Unknown Borrower')
            : 'LocalLendables System',
          sender_avatar: notification.type === 'borrow_request' ? (borrowerDetails?.avatar || null) : null,
          preview: notification.message,
          created_at: notification.createdAt,
          owner_id: itemDetails?.owner || null,
          owner_name: ownerDetails ? (ownerDetails.showName || ownerDetails.firstName || 'Unknown Owner') : 'Unknown Owner',
          owner_avatar: ownerDetails?.avatar || null,
          borrower_id: notification.type === 'borrow_request' ? (borrowerDetails?.id || null) : userId,
          borrower_name: notification.type === 'borrow_request' 
            ? (borrowerDetails ? (borrowerDetails.showName || borrowerDetails.firstName || 'Unknown Borrower') : 'Unknown Borrower') 
            : null,
          userId: userId,
          isSystemNotification: true,
          notificationId: notification.id,
          notificationType: notification.type,
          type: notification.type,
          dueDate: dueDate,
          relatedId: notification.relatedId, // Include the borrow request ID or agreement ID for linking
          agreementId: notification.type === 'agreement_ready' ? notification.relatedId : null // For agreement notifications
        };
        
        systemMessages.push(systemMessage);
      }
      
      // Combine and sort by date
      const allMessages = [...messages, ...systemMessages].sort((a, b) => 
        new Date(b.created_at).getTime() - new Date(a.created_at).getTime()
      );
      
      console.log(`Found ${messages.length} chat messages and ${systemMessages.length} system notifications for user ${userId}`);
      
      res.json({ messages: allMessages });
    } catch (error) {
      console.error('Error fetching unread messages:', error);
      res.status(500).json({ error: 'Failed to fetch unread messages' });
    }
  });

  // API: Get chat messages
  app.get('/api/chat/:conversationId/messages', async (req: Request, res: Response) => {
    try {
      const conversationId = parseInt(req.params.conversationId);
      const messages = await storage.getChatMessages(conversationId);
      
      res.json({ messages });
    } catch (error) {
      console.error('Error fetching messages:', error);
      res.status(500).json({ error: 'Failed to fetch messages' });
    }
  });

  // API: Get user favorites
  app.get('/api/favorites/user/:userId', async (req: Request, res: Response) => {
    try {
      const userId = parseInt(req.params.userId);
      if (isNaN(userId)) {
        return res.status(400).json({ error: 'Invalid user ID' });
      }
      const favorites = await storage.getUserFavorites(userId);
      
      res.json({ 
        success: true, 
        favorites: favorites 
      });
    } catch (error) {
      console.error('Error fetching user favorites:', error);
      res.status(500).json({ error: 'Failed to fetch favorites' });
    }
  });

  // API: Send chat message (text only)
  // API: Find existing conversation for item and user
  app.get('/api/chat/find-conversation', async (req: Request, res: Response) => {
    try {
      const itemId = parseInt(req.query.itemId as string);
      const userId = parseInt(req.query.userId as string);
      
      if (!itemId || !userId) {
        return res.status(400).json({ error: 'Missing itemId or userId' });
      }
      
      // Get item to find owner
      const item = await storage.getItem(itemId);
      if (!item) {
        return res.status(404).json({ error: 'Item not found' });
      }
      
      // Find conversation where user is either owner or borrower
      const conversations = await storage.getConversationsForItem(itemId, userId);
      
      if (conversations.length === 0) {
        return res.status(404).json({ error: 'No conversation found for this item' });
      }
      
      // Return the first (most recent) conversation
      res.json({ 
        conversationId: conversations[0].id,
        success: true 
      });
      
    } catch (error) {
      console.error('Error finding conversation:', error);
      res.status(500).json({ error: 'Failed to find conversation' });
    }
  });

  // API: Start or continue chat conversation (used by return process)
  app.post('/api/chat/start', async (req: Request, res: Response) => {
    try {
      const session = req.session as SessionData;
      const { itemId, borrowerId, ownerId, message } = req.body;
      
      // Use provided borrower ID since this is from return process form
      const senderId = borrowerId;
      
      if (!senderId || !itemId || !ownerId || !message) {
        return res.status(400).json({ error: 'Missing required fields' });
      }

      console.log('Chat start request - itemId:', itemId, 'borrowerId:', borrowerId, 'ownerId:', ownerId, 'senderId:', senderId);

      // Find or create conversation
      const conversationId = await storage.findOrCreateConversation(itemId, ownerId, borrowerId);
      console.log('Conversation created/found:', conversationId);
      
      // Send the message
      const messageData = {
        conversationId,
        senderId,
        messageType: 'text',
        textContent: message.trim()
      };

      console.log('Sending message with data:', messageData);
      try {
        const newMessage = await storage.sendChatMessage(messageData);
        console.log('Message sent successfully:', newMessage);
        
        // Create notification for the recipient (owner if borrower is sending, vice versa)
        const recipientId = senderId === ownerId ? borrowerId : ownerId;
        const item = await storage.getItem(itemId);
        const senderUser = await storage.getUser(senderId);
        
        console.log('Creating notification for recipient:', recipientId);
        if (item && senderUser) {
          await storage.createStandardizedNotification({
            userId: recipientId,
            type: 'new_message',
            relatedId: itemId,
            relatedType: 'item',
            templateData: {
              senderName: senderUser.showName || senderUser.firstName,
              itemName: item.name
            }
          });
          console.log('Notification created successfully');
        }
        
        res.json({ success: true, message: newMessage, conversationId });
      } catch (messageError) {
        console.error('Error in chat process:', messageError);
        res.status(500).json({ error: 'Failed to send message' });
      }
    } catch (error) {
      console.error('Error starting chat:', error);
      res.status(500).json({ error: 'Failed to start chat' });
    }
  });

  app.post('/api/chat/:conversationId/message', async (req: Request, res: Response) => {
    try {
      const conversationId = parseInt(req.params.conversationId);
      const session = req.session as SessionData;
      const userParam = parseInt(req.query.user as string);
      const senderId = userParam || session?.userId;
      
      if (!senderId) {
        return res.status(401).json({ error: 'Authentication required' });
      }
      const { content } = req.body;

      console.log('Text message request - conversationId:', conversationId, 'senderId:', senderId, 'content:', content);

      const messageData = {
        conversationId,
        senderId,
        messageType: 'text',
        textContent: content
      };

      const newMessage = await storage.sendChatMessage(messageData);
      
      res.json({ success: true, message: newMessage });
    } catch (error) {
      console.error('Error sending message:', error);
      res.status(500).json({ error: 'Failed to send message' });
    }
  });

  // Print Label route for owners
  app.get('/print-label/:id', async (req: Request, res: Response) => {
    try {
      const session = req.session as SessionData;
      const itemId = parseInt(req.params.id);
      
      if (isNaN(itemId)) {
        return res.status(400).send('Invalid item ID');
      }

      const item = await storage.getItem(itemId);
      if (!item) {
        return res.status(404).send('Item not found');
      }

      // Check if current user is the owner (allow anonymous access for lost items)
      if (session?.userId && item.owner !== session.userId) {
        const canEdit = await storage.canUserEditItem(session.userId, itemId);
        if (!canEdit) {
          return res.status(403).send('You can only print labels for your own items');
        }
      }

      const owner = await storage.getUser(item.owner);
      if (!owner) {
        return res.status(404).send('Owner not found');
      }

      // Generate QR code URL
      const baseUrl = `${req.protocol}://${req.get('host')}`;
      const qrUrl = `${baseUrl}/return/${item.id}`;

      const html = await templateEngine.render('print-label.html.twig', {
        item,
        owner,
        qrUrl,
        baseUrl
      });

      res.send(html);
    } catch (error) {
      console.error('Error rendering print label:', error);
      res.status(500).send('Internal Server Error');
    }
  });

  // Print labels page (Avery 5160 multi-label system)
  app.get('/print-labels/:id', async (req: Request, res: Response) => {
    try {
      const session = req.session as SessionData;
      const itemId = parseInt(req.params.id);
      
      if (isNaN(itemId)) {
        return res.status(400).send('Invalid item ID');
      }

      const item = await storage.getItem(itemId);
      if (!item) {
        return res.status(404).send('Item not found');
      }

      // Check if current user is the owner or has edit permissions
      if (!session?.userId) {
        return res.status(401).send('Authentication required');
      }

      const canEdit = await storage.canUserEditItem(session.userId, itemId);
      if (!canEdit) {
        return res.status(403).send('You can only print labels for your own items');
      }

      const owner = await storage.getUser(item.owner);
      if (!owner) {
        return res.status(404).send('Owner not found');
      }

      // Get or create QR code for this item
      const qrData = await storage.getOrCreateItemQR(itemId);

      // Generate site URL and owner phone
      const siteUrl = `${req.protocol}://${req.get('host')}`;
      const ownerPhone = owner.phone || owner.phoneNumber || '';
      const qrUrl = `${siteUrl}/qr/${qrData.qr_code}`;

      const html = await templateEngine.render('print-labels.html.twig', {
        item,
        owner,
        siteUrl,
        ownerPhone,
        qrUrl,
        qrCode: qrData.qr_code,
        currentUser: await storage.getUser(session.userId)
      });

      res.send(html);
    } catch (error) {
      console.error('Error rendering print labels:', error);
      res.status(500).send('Internal Server Error');
    }
  });

  // Borrow request response page by item ID - finds active pending request
  app.get('/borrow-response/:itemId', async (req: Request, res: Response) => {
    const session = req.session as SessionData;
    if (!session?.userId) {
      return res.redirect('/login');
    }

    try {
      const itemId = parseInt(req.params.itemId);
      const messageId = req.query.messageId ? parseInt(req.query.messageId as string) : null;
      
      console.log(`Finding borrow request for item ID: ${itemId}, owner: ${session.userId}, messageId: ${messageId}`);
      
      // If we have a messageId, try to get the related borrow request ID from the notification
      if (messageId) {
        const notification = await storage.getNotification(messageId);
        if (notification && notification.relatedId) {
          console.log(`Found notification ${messageId} with related borrow request ID: ${notification.relatedId}`);
          return res.redirect(`/borrow-request/${notification.relatedId}/respond`);
        }
      }
      
      // Fallback: Find active pending borrow request for this item and owner
      const borrowRequests = await storage.getBorrowRequestsByOwner(session.userId);

      
      const activeBorrowRequest = borrowRequests.find(req => 
        req.itemId === itemId && 
        (req.status === 'pending' || req.status === 'approved')
      );
      
      if (!activeBorrowRequest) {
        console.log(`No active borrow request found for item ${itemId} and owner ${session.userId}`);
        return res.status(404).send('No active borrow request found for this item');
      }
      
      console.log(`Found active borrow request ID: ${activeBorrowRequest.id} for item ${itemId}`);
      
      // Redirect to the standard borrow request response page
      return res.redirect(`/borrow-request/${activeBorrowRequest.id}/respond`);
    } catch (error) {
      console.error('Error finding borrow request for item:', error);
      res.status(500).send('Server error');
    }
  });

  // Borrow Request Response Page
  app.get('/borrow-request/:id/respond', async (req: Request, res: Response) => {
    const session = req.session as SessionData;
    if (!session?.userId) {
      return res.redirect('/login');
    }

    try {
      const requestId = parseInt(req.params.id);
      console.log(`Loading borrow request response page for request ID: ${requestId}`);
      
      const borrowRequest = await storage.getBorrowRequest(requestId);
      
      if (!borrowRequest) {
        console.log(`Borrow request ${requestId} not found`);
        return res.status(404).send('Request not found');
      }

      if (borrowRequest.ownerId !== session.userId) {
        console.log(`Access denied for user ${session.userId} to request ${requestId}`);
        return res.status(403).send('Access denied');
      }

      const [item, borrower, owner] = await Promise.all([
        storage.getItem(borrowRequest.itemId),
        storage.getUser(borrowRequest.borrowerId),
        storage.getUser(borrowRequest.ownerId)
      ]);

      console.log(`Loaded data for request ${requestId}:`, {
        item: item?.name,
        borrower: borrower?.firstName,
        owner: owner?.firstName,
        pricePerDay: item?.pricePerDay
      });

      const conversationId = await storage.findOrCreateConversation(borrowRequest.itemId, borrowRequest.ownerId, borrowRequest.borrowerId);
      const chatHistory = await storage.getChatMessages(conversationId);

      console.log('Template data for borrow response:', {
        borrower: { 
          firstName: borrower?.firstName, 
          lastName: borrower?.lastName, 
          showName: borrower?.showName, 
          avatar: borrower?.avatar,
          phone: borrower?.phone,
          address: borrower?.address,
          town: borrower?.town,
          email: borrower?.email
        },
        item: { name: item?.name, pricePerDay: item?.pricePerDay },
        owner: { 
          firstName: owner?.firstName, 
          lastName: owner?.lastName, 
          showName: owner?.showName, 
          avatar: owner?.avatar,
          phone: owner?.phone,
          address: owner?.address,
          town: owner?.town,
          email: owner?.email
        }
      });

      const html = await templateEngine.render('borrow-response.html.twig', {
        currentUser: owner,
        user: owner, // Add user variable for header template
        breadcrumbs: generateBreadcrumbs('borrow-request', undefined, owner?.id),
        borrowRequest,
        item,
        borrower,
        owner, // Add owner data for convenience labels
        chatHistory
      });
      res.send(html);
    } catch (error) {
      console.error('Error loading borrow request response page:', error);
      res.status(500).send('Server error');
    }
  });

  // Loan Agreement Page
  app.get('/agreement/:id', async (req: Request, res: Response) => {
    const session = req.session as SessionData;
    if (!session?.userId) {
      return res.redirect('/login');
    }

    try {
      const agreementId = parseInt(req.params.id);
      const agreement = await storage.getLoanAgreement(agreementId);
      
      if (!agreement) {
        return res.status(404).send('Agreement not found');
      }

      if (agreement.borrowerId !== session.userId && agreement.ownerId !== session.userId) {
        return res.status(403).send('Access denied');
      }

      const [item, borrower, owner, category] = await Promise.all([
        storage.getItem(agreement.itemId),
        storage.getUser(agreement.borrowerId),
        storage.getUser(agreement.ownerId),
        storage.getItemCategory(agreement.itemId)
      ]);

      console.log('=== AGREEMENT DEBUG START ===');
      console.log('borrower:', JSON.stringify(borrower, null, 2));
      console.log('owner:', JSON.stringify(owner, null, 2));
      console.log('borrower type:', typeof borrower);
      console.log('owner type:', typeof owner);
      console.log('agreement.borrowerId:', agreement.borrowerId);
      console.log('agreement.ownerId:', agreement.ownerId);
      console.log('agreement.ownerSignedAt:', agreement.ownerSignedAt);
      console.log('agreement.borrowerSignedAt:', agreement.borrowerSignedAt);
      console.log('session.userId:', session.userId);
      console.log('isOwnerUser:', session.userId === agreement.ownerId);
      console.log('=== AGREEMENT DEBUG END ===');

      const currentUserObj = session.userId === agreement.ownerId ? owner : borrower;
      
      const html = await templateEngine.render('loan-agreement.html.twig', {
        currentUser: currentUserObj,
        user: currentUserObj, // Header compatibility
        agreement,
        item,
        borrower,
        owner,
        category
      });
      res.send(html);
    } catch (error) {
      console.error('Error loading agreement page:', error);
      res.status(500).send('Server error');
    }
  });

  // API: Respond to Borrow Request
  app.post('/api/borrow-requests/respond', async (req: Request, res: Response) => {
    const session = req.session as SessionData;
    console.log('Session debug:', { 
      hasSession: !!session, 
      userId: session?.userId, 
      sessionId: req.sessionID
    });
    
    if (!session?.userId) {
      return res.status(401).json({ success: false, error: 'Not authenticated' });
    }

    try {
      console.log('Raw request body:', req.body);
      const { requestId, response, responseMessage, newStartDate, newEndDate, dailyRate } = req.body;
      console.log('Processing borrow request response:', { requestId, response, userId: session.userId, type: typeof requestId });
      
      const borrowRequest = await storage.getBorrowRequest(parseInt(requestId));
      
      if (!borrowRequest) {
        console.log(`Borrow request ${requestId} not found`);
        return res.status(404).json({ success: false, error: 'Request not found' });
      }
      
      if (borrowRequest.ownerId !== session.userId) {
        console.log(`Access denied: user ${session.userId} not owner of request ${requestId} (owner: ${borrowRequest.ownerId})`);
        return res.status(403).json({ success: false, error: 'Access denied' });
      }

      const startDate = newStartDate ? new Date(newStartDate) : undefined;
      const endDate = newEndDate ? new Date(newEndDate) : undefined;
      
      // The requestId might be either a borrow request ID or loan transaction ID
      // First try to get it as a loan transaction directly
      let loanTransaction = await storage.getLoanTransaction(parseInt(requestId));
      
      if (!loanTransaction) {
        // If not found, try to find it by borrow request ID
        loanTransaction = await storage.getLoanTransactionByBorrowRequestId(parseInt(requestId));
      }
      
      if (!loanTransaction) {
        console.error(`No loan transaction found for request ${requestId}`);
        return res.status(500).json({ success: false, error: 'Loan transaction not found' });
      }
      
      console.log(`Using loan transaction ${loanTransaction.id} for request ${requestId}`);
      
      // Update the status in the unified loan_transactions system
      await storage.updateLoanTransactionStatus(
        loanTransaction.id, 
        response === 'approved' ? 'approved' : response === 'counter' ? 'counter' : 'rejected',
        responseMessage,
        startDate,
        endDate
      );

      // Mark related notification as read and dismissed since the owner has dealt with it
      try {
        const { pool } = await import('./db');
        await pool.query(`
          UPDATE notifications 
          SET is_read = true, is_dismissed = true 
          WHERE user_id = $1 
            AND type = 'borrow_request' 
            AND related_type = 'borrow_request' 
            AND related_id = $2
        `, [session.userId, parseInt(requestId)]);
        
        // Also mark any borrow request notifications for this item as dismissed
        await pool.query(`
          UPDATE notifications 
          SET is_read = true, is_dismissed = true 
          WHERE user_id = $1 
            AND type = 'borrow_request'
            AND (item_id = $2 OR message LIKE '%Power Drill Set%')
        `, [session.userId, borrowRequest.itemId]);

        // Mark the original borrow request chat message as read for the owner since they've processed it
        await pool.query(`
          UPDATE chat_messages 
          SET is_read = true 
          WHERE conversation_id IN (
            SELECT id FROM conversations WHERE item_id = $2 AND (owner_id = $1 OR borrower_id = $1)
          )
          AND sender_id != $1
          AND created_at <= NOW()
        `, [session.userId, borrowRequest.itemId]);
        
        console.log(`Marked notifications as read and dismissed for borrow request ${requestId} and item ${borrowRequest.itemId}`);
      } catch (notificationError) {
        console.error('Error marking notification as read:', notificationError);
      }

      if (response === 'approved' || response === 'counter') {
        const finalStartDate = startDate || borrowRequest.startDate;
        const finalEndDate = endDate || borrowRequest.endDate;
        const days = Math.ceil((finalEndDate.getTime() - finalStartDate.getTime()) / (1000 * 60 * 60 * 24));
        const rate = parseFloat(dailyRate) || 0;
        const totalAmount = rate * days;

        const legalContent = await storage.getLegalContent();
        const defaultTerms = legalContent.find(content => content.type === 'loan_agreement')?.content || 
          'Standard lending terms apply. Item must be returned in original condition.';

        const agreement = await storage.createLoanAgreement({
          borrowRequestId: parseInt(requestId),
          itemId: borrowRequest.itemId,
          borrowerId: borrowRequest.borrowerId,
          ownerId: borrowRequest.ownerId,
          startDate: finalStartDate,
          endDate: finalEndDate,
          agreementTerms: defaultTerms,
          dailyRate: rate > 0 ? rate : undefined,
          totalAmount: totalAmount > 0 ? totalAmount : undefined,
          status: 'pending'  // All loans and rentals require proactive signing action
        });

        // Link the agreement to the loan transaction
        await storage.updateLoanTransactionStatus(
          loanTransaction.id,
          'approved',
          responseMessage,
          startDate,
          endDate
        );
        
        // Update the loan transaction to reference the agreement
        await storage.linkLoanTransactionToAgreement(loanTransaction.id, agreement.id);

        const conversationId = await storage.findOrCreateConversation(
          borrowRequest.itemId, 
          borrowRequest.ownerId, 
          borrowRequest.borrowerId
        );

        await storage.sendChatMessage({
          conversationId,
          senderId: session.userId,
          messageType: 'text',
          textContent: response === 'approved' ? 
            `Great news! I've approved your borrow request. Please sign the loan agreement to complete the process.` :
            `I've made a counter-offer with different dates. Please sign the loan agreement to complete the process. ${responseMessage || ''}`
        });

        // Create standardized notification for borrower about the agreement
        await storage.createStandardizedNotification({
          userId: borrowRequest.borrowerId,
          type: 'agreement_ready',
          relatedId: agreement.id,
          relatedType: 'agreement',
          templateData: {
            itemName: borrowRequest.itemName
          }
        });

        return res.json({ 
          success: true, 
          agreementId: agreement.id,
          message: 'Response sent and agreement created',
          showPreSignOption: true
        });
      } else {
        const conversationId = await storage.findOrCreateConversation(
          borrowRequest.itemId, 
          borrowRequest.ownerId, 
          borrowRequest.borrowerId
        );

        await storage.sendChatMessage({
          conversationId,
          senderId: session.userId,
          messageType: 'text',
          textContent: `Sorry, I can't lend this item for the requested dates. ${responseMessage || ''}`
        });

        return res.json({ success: true, message: 'Response sent' });
      }
    } catch (error) {
      console.error('Error responding to borrow request:', error);
      res.status(500).json({ success: false, error: 'Server error' });
    }
  });

  // API: Sign Agreement
  app.post('/api/agreements/:id/sign', async (req: Request, res: Response) => {
    const session = req.session as SessionData;
    if (!session?.userId) {
      return res.status(401).json({ success: false, error: 'Not authenticated' });
    }

    try {
      const agreementId = parseInt(req.params.id);
      const { signerType, signature, timestamp } = req.body;
      
      const agreement = await storage.getLoanAgreement(agreementId);
      if (!agreement) {
        return res.status(404).json({ success: false, error: 'Agreement not found' });
      }

      const isOwner = agreement.ownerId === session.userId;
      const isBorrower = agreement.borrowerId === session.userId;
      
      if (!isOwner && !isBorrower) {
        return res.status(403).json({ success: false, error: 'Access denied' });
      }

      if ((signerType === 'owner' && !isOwner) || (signerType === 'borrower' && !isBorrower)) {
        return res.status(403).json({ success: false, error: 'Invalid signer type' });
      }

      const signingAsOwner = signerType === 'owner';
      const signatureData = `${signature}_${timestamp}`;
      await storage.signAgreement(agreementId, session.userId, signatureData, signingAsOwner);

      const updatedAgreement = await storage.getLoanAgreement(agreementId);
      if (updatedAgreement.borrowerSignedAt && updatedAgreement.ownerSignedAt) {
        const conversationId = await storage.findOrCreateConversation(
          agreement.itemId, 
          agreement.ownerId, 
          agreement.borrowerId
        );

        await storage.sendChatMessage({
          conversationId,
          senderId: session.userId,
          messageType: 'text',
          textContent: 'Agreement fully signed! The loan is now active. Please coordinate pickup details.'
        });
      }

      res.json({ success: true, message: 'Agreement signed successfully' });
    } catch (error) {
      console.error('Error signing agreement:', error);
      res.status(500).json({ success: false, error: 'Server error' });
    }
  });

  // API: Get Borrow Requests by Owner
  app.get('/api/borrow-requests/owner/:ownerId', async (req: Request, res: Response) => {
    const session = req.session as SessionData;
    if (!session?.userId) {
      return res.status(401).json({ success: false, error: 'Not authenticated' });
    }

    try {
      const ownerId = parseInt(req.params.ownerId);
      if (ownerId !== session.userId) {
        return res.status(403).json({ success: false, error: 'Access denied' });
      }

      const requests = await storage.getBorrowRequestsByOwner(ownerId);

      res.json({ success: true, requests });
    } catch (error) {
      console.error('Error fetching borrow requests:', error);
      res.status(500).json({ success: false, error: 'Server error' });
    }
  });

  // API endpoint to mark notification as read
  app.post('/api/notifications/:id/mark-read', async (req: Request, res: Response) => {
    try {
      const notificationId = parseInt(req.params.id);
      await storage.markNotificationAsRead(notificationId);
      res.json({ success: true });
    } catch (error) {
      console.error('Error marking notification as read:', error);
      res.status(500).json({ success: false, error: 'Failed to mark notification as read' });
    }
  });

  // API: Get Active Loans for User
  app.get('/api/loans/active/:userId', async (req: Request, res: Response) => {
    const session = req.session as SessionData;
    if (!session?.userId) {
      return res.status(401).json({ success: false, error: 'Not authenticated' });
    }

    try {
      const userId = parseInt(req.params.userId);
      if (userId !== session.userId) {
        return res.status(403).json({ success: false, error: 'Access denied' });
      }

      const activeLoans = await storage.getActiveLoans(userId);
      res.json({ success: true, loans: activeLoans });
    } catch (error) {
      console.error('Error fetching active loans:', error);
      res.status(500).json({ success: false, error: 'Server error' });
    }
  });

  // API: Initiate Return (Unified System)
  app.post('/api/loans/:loanId/return', async (req: Request, res: Response) => {
    const session = req.session as SessionData;
    if (!session?.userId) {
      return res.status(401).json({ success: false, error: 'Not authenticated' });
    }

    try {
      const loanId = parseInt(req.params.loanId);
      const { thankYouMessage } = req.body;
      
      console.log(`Return initiation request: loanId=${loanId}, userId=${session.userId}`);
      
      await storage.initiateReturn(loanId, session.userId, thankYouMessage);

      res.json({ success: true, message: 'Return initiated successfully' });
    } catch (error) {
      console.error('Error initiating return:', error);
      res.status(500).json({ success: false, error: 'Server error' });
    }
  });

  // Return Item page (accessible without login for found items)
  app.get('/return/:id', async (req: Request, res: Response) => {
    try {
      const itemId = parseInt(req.params.id);
      
      if (isNaN(itemId)) {
        return res.status(400).send('Invalid item ID');
      }

      const item = await storage.getItem(itemId);
      if (!item) {
        return res.status(404).send('Item not found');
      }

      const owner = await storage.getUser(item.owner);
      if (!owner) {
        return res.status(404).send('Owner not found');
      }

      // Get current loan information if any
      let currentLoan = null;
      try {
        const loanStatus = await storage.getItemLoanStatus(itemId);
        if (loanStatus && loanStatus.isOnLoan) {
          currentLoan = {
            id: loanStatus.loanId,
            borrowerName: loanStatus.borrowerName,
            endDate: loanStatus.endDate
          };
        }
      } catch (error) {
        console.log('No active loan found for item:', itemId);
      }

      // Get current user if logged in (from session or URL parameter)
      const session = req.session as SessionData;
      let currentUser = null;
      const userId = session?.userId || parseInt(req.query.userId as string);
      if (userId) {
        currentUser = await storage.getUser(userId);
      }

      // Determine user authorization
      const isOwner = currentUser && currentUser.id === item.owner;
      const isBorrower = currentUser && currentLoan && currentLoan.borrowerName; // Check if current user is the borrower
      
      const html = await templateEngine.render('return-item.html.twig', {
        item,
        owner,
        currentLoan,
        currentUser,
        user: currentUser,
        isOwner,
        isBorrower
      });

      res.send(html);
    } catch (error) {
      console.error('Error rendering return item page:', error);
      res.status(500).send('Internal Server Error');
    }
  });



  // API: Get user notifications
  app.get('/api/notifications/:userId', async (req: Request, res: Response) => {
    try {
      const userId = parseInt(req.params.userId);
      const notifications = await storage.getUserNotifications(userId);
      
      res.json({ success: true, notifications });
    } catch (error) {
      console.error('Error fetching notifications:', error);
      res.status(500).json({ error: 'Failed to fetch notifications' });
    }
  });

  // API: Mark notification as read
  app.post('/api/notifications/:id/read', async (req: Request, res: Response) => {
    try {
      const notificationId = parseInt(req.params.id);
      await storage.markNotificationAsRead(notificationId);
      
      res.json({ success: true });
    } catch (error) {
      console.error('Error marking notification as read:', error);
      res.status(500).json({ error: 'Failed to mark notification as read' });
    }
  });

  // API: Dismiss notification permanently
  app.post('/api/notifications/:id/dismiss', async (req: Request, res: Response) => {
    try {
      const notificationId = parseInt(req.params.id);
      await storage.dismissNotification(notificationId);
      
      res.json({ success: true });
    } catch (error) {
      console.error('Error dismissing notification:', error);
      res.status(500).json({ error: 'Failed to dismiss notification' });
    }
  });

  // API: Mark chat message as read
  app.post('/api/messages/:id/mark-read', async (req: Request, res: Response) => {
    try {
      const messageId = parseInt(req.params.id);
      await storage.markMessageAsRead(messageId);
      
      res.json({ success: true });
    } catch (error) {
      console.error('Error marking message as read:', error);
      res.status(500).json({ error: 'Failed to mark message as read' });
    }
  });

  // API: Clean up old notifications
  app.post('/api/notifications/cleanup', async (req: Request, res: Response) => {
    try {
      await storage.cleanupOldNotifications();
      res.json({ success: true, message: 'Old notifications cleaned up' });
    } catch (error) {
      console.error('Error cleaning up notifications:', error);
      res.status(500).json({ error: 'Failed to clean up notifications' });
    }
  });

  // PERFORMANCE OPTIMIZATION: Consolidated Dashboard API
  app.get('/api/dashboard/:userId', async (req: Request, res: Response) => {
    const session = req.session as SessionData;
    if (!session?.userId) {
      return res.status(401).json({ success: false, error: 'Not authenticated' });
    }

    try {
      const userId = parseInt(req.params.userId);
      if (userId !== session.userId) {
        return res.status(403).json({ success: false, error: 'Access denied' });
      }

      console.log(`Consolidated dashboard API called for user ${userId}`);

      // Execute all dashboard queries in parallel for optimal performance
      const [activeLoans, ownerLoans, borrowRequests, unreadData] = await Promise.all([
        storage.getActiveLoans(userId),
        storage.getOwnerLoans(userId),
        storage.getBorrowRequestsByOwner(userId),
        storage.getUserUnreadChatData(userId)
      ]);

      res.json({
        success: true,
        data: {
          activeLoans: activeLoans || [],
          ownerLoans: ownerLoans || [],
          borrowRequests: borrowRequests || [],
          unreadMessages: unreadData || { messages: [], hasUnreadChats: false, unreadChatCount: 0 }
        }
      });
    } catch (error) {
      console.error('Error fetching consolidated dashboard data:', error);
      res.status(500).json({ success: false, error: 'Server error' });
    }
  });

  // API: Owner confirms return completion
  app.post('/api/loans/:trackingId/confirm-return', async (req: Request, res: Response) => {
    try {
      const trackingId = parseInt(req.params.trackingId);
      const { condition, notes } = req.body;
      const session = req.session as SessionData;
      const ownerId = session?.userId;
      
      if (!ownerId) {
        return res.status(401).json({ error: 'Authentication required' });
      }
      
      const result = await storage.completeReturn(trackingId, ownerId, condition, notes);
      
      if (result) {
        res.json({ success: true, message: 'Return completed successfully' });
      } else {
        res.status(400).json({ error: 'Failed to complete return' });
      }
    } catch (error) {
      console.error('Error confirming return:', error);
      res.status(500).json({ error: 'Failed to confirm return' });
    }
  });

  // API: Search items by name
  app.get('/api/items/search', async (req: Request, res: Response) => {
    try {
      const { name } = req.query;
      if (!name || typeof name !== 'string') {
        return res.status(400).json({ error: 'Item name is required' });
      }
      
      const result = await pool.query(`
        SELECT id, name, description, owner
        FROM lendable_items 
        WHERE name ILIKE $1 
        LIMIT 1
      `, [`%${name}%`]);
      
      if (result.rows.length > 0) {
        res.json({ success: true, item: result.rows[0] });
      } else {
        res.json({ success: false, error: 'Item not found' });
      }
    } catch (error) {
      console.error('Error searching for item:', error);
      res.status(500).json({ error: 'Failed to search for item' });
    }
  });

  // API: Contact owner about found item
  app.post('/api/contact-owner', async (req: Request, res: Response) => {
    try {
      const { senderName, senderEmail, message, itemId } = req.body;
      
      if (!senderName || !senderEmail || !message || !itemId) {
        return res.status(400).json({ error: 'Missing required fields' });
      }

      const item = await storage.getItem(itemId);
      if (!item) {
        return res.status(404).json({ error: 'Item not found' });
      }

      const owner = await storage.getUser(item.owner);
      if (!owner) {
        return res.status(404).json({ error: 'Owner not found' });
      }

      // Create standardized notification for the owner
      await storage.createStandardizedNotification({
        userId: item.owner,
        type: 'found_item',
        relatedId: itemId,
        relatedType: 'item',
        templateData: {
          senderName: `${senderName} (${senderEmail})`,
          details: message
        }
      });

      res.json({ success: true });
    } catch (error) {
      console.error('Error sending contact message:', error);
      res.status(500).json({ error: 'Failed to send message' });
    }
  });

  // API: Report issue with item
  app.post('/api/report-issue', async (req: Request, res: Response) => {
    try {
      const { itemId, issueType, description, reporterName } = req.body;
      
      if (!itemId || !issueType || !description || !reporterName) {
        return res.status(400).json({ error: 'Missing required fields' });
      }

      const item = await storage.getItem(itemId);
      if (!item) {
        return res.status(404).json({ error: 'Item not found' });
      }

      // Create standardized notification for the owner
      await storage.createStandardizedNotification({
        userId: item.owner,
        type: 'item_issue',
        relatedId: itemId,
        relatedType: 'item',
        templateData: {
          senderName: reporterName,
          itemName: item.name,
          issueType,
          details: description
        }
      });

      res.json({ success: true });
    } catch (error) {
      console.error('Error reporting issue:', error);
      res.status(500).json({ error: 'Failed to report issue' });
    }
  });

  // Edit Profile Route
  app.get('/edit-profile/:id', async (req: Request, res: Response) => {
    try {
      const userId = parseInt(req.params.id);
      
      // For demo purposes, allow editing any profile
      // In production, you would check authentication
      
      const user = await storage.getUser(userId);
      if (!user) {
        return res.status(404).send('User not found');
      }
      
      // Fetch admin tooltips
      const tooltips = await storage.getAdminTooltips();
      
      const html = await templateEngine.render('edit-profile.html.twig', {
        user,
        currentUser: user,
        breadcrumbs: generateBreadcrumbs('edit-profile', undefined, user?.id),
        tooltips
      });
      
      res.send(html);
    } catch (error) {
      console.error('Error rendering edit profile page:', error);
      res.status(500).send('Internal Server Error');
    }
  });

  // Admin tooltip management API
  app.get('/api/admin/tooltips', async (req: Request, res: Response) => {
    try {
      const tooltips = await storage.getAdminTooltips();
      res.json({ success: true, tooltips });
    } catch (error) {
      console.error('Error fetching admin tooltips:', error);
      res.status(500).json({ error: 'Failed to fetch tooltips' });
    }
  });

  app.put('/api/admin/tooltips/:key', async (req: Request, res: Response) => {
    try {
      const { key } = req.params;
      const { text } = req.body;
      
      if (!text || typeof text !== 'string') {
        return res.status(400).json({ error: 'Invalid tooltip text' });
      }
      
      const success = await storage.updateAdminTooltip(key, text);
      
      if (success) {
        res.json({ success: true, message: 'Tooltip updated successfully' });
      } else {
        res.status(500).json({ error: 'Failed to update tooltip' });
      }
    } catch (error) {
      console.error('Error updating admin tooltip:', error);
      res.status(500).json({ error: 'Failed to update tooltip' });
    }
  });

  // API: Check membership status for profile editing
  app.get('/api/users/:id/membership-status', async (req: Request, res: Response) => {
    try {
      const userId = parseInt(req.params.id);
      
      const user = await storage.getUser(userId);
      if (!user) {
        return res.status(404).json({ error: 'User not found' });
      }
      
      // Check if user has any active loans (items they own that are currently borrowed)
      const activeLoans = await storage.getActiveLoansByOwner(userId);
      const activeLoansCount = activeLoans.length;
      
      // User can go inactive only if they have no active loans
      const canGoInactive = activeLoansCount === 0;
      
      res.json({
        success: true,
        isActive: user.active,
        canGoInactive,
        activeLoansCount
      });
    } catch (error) {
      console.error('Error checking membership status:', error);
      res.status(500).json({ error: 'Failed to check membership status' });
    }
  });

  // API: Get user preferences for profile editing
  app.get('/api/users/:id/preferences', async (req: Request, res: Response) => {
    try {
      const userId = parseInt(req.params.id);
      
      const preferences = await storage.getUserPreferences(userId);
      
      res.json({
        success: true,
        preferences: preferences || {
          smsNotifications: false,
          instantEmailNotifications: false
        }
      });
    } catch (error) {
      console.error('Error getting user preferences:', error);
      res.status(500).json({ error: 'Failed to get user preferences' });
    }
  });

  // Update Profile API Route
  app.post('/api/profile/:id/update', upload.single('avatar'), async (req: Request, res: Response) => {
    try {
      const userId = parseInt(req.params.id);
      
      const user = await storage.getUser(userId);
      if (!user) {
        return res.status(404).json({ success: false, message: 'User not found' });
      }
      
      // Prepare update data
      const updateData: any = {
        firstName: req.body.firstName?.trim(),
        lastName: req.body.lastName?.trim(),
        showName: req.body.showName?.trim() || null,
        email: req.body.email?.trim(),
        phone: req.body.phone?.trim() || null,
        address: req.body.address?.trim() || null,
        town: req.body.town?.trim() || null,
        zipcode: req.body.zipcode ? parseInt(req.body.zipcode) : null,
        gender: req.body.gender || null,
        age: req.body.age || null,
        bio: req.body.bio?.trim() || null
      };

      // Handle membership status change
      if (req.body.active !== undefined) {
        const isActive = req.body.active === 'on' || req.body.active === true || req.body.active === 'true';
        
        // If user wants to go inactive, check they have no active loans
        if (!isActive) {
          const activeLoans = await storage.getActiveLoansByOwner(userId);
          if (activeLoans.length > 0) {
            return res.status(400).json({ 
              success: false,
              message: `Cannot set status to inactive. You have ${activeLoans.length} item(s) currently on loan.` 
            });
          }
        }
        
        updateData.active = isActive;
      }

      // Handle notification preferences
      const notificationPreferences = {
        smsNotifications: req.body.smsNotifications === 'on',
        instantEmailNotifications: req.body.instantEmailNotifications === 'on'
      };

      // Handle privacy preferences
      const privacyPreferences = {
        displayPhone: req.body.displayPhone === 'on',
        displayEmail: req.body.displayEmail === 'on',
        displayAddress: req.body.displayAddress === 'on',
        displayGender: req.body.displayGender === 'on',
        displayAge: req.body.displayAge === 'on'
      };

      // Combine all preferences
      const allPreferences = {
        ...notificationPreferences,
        ...privacyPreferences
      };

      // Update user preferences for notifications and privacy
      await storage.updateUserPreferences(userId, allPreferences);
      
      // Debug logging for upload debugging
      console.log('Profile update - selectedAvatar:', req.body.selectedAvatar);
      console.log('Profile update - req.file:', req.file ? `File: ${req.file.originalname}, Size: ${req.file.size}, Buffer: ${req.file.buffer ? 'present' : 'missing'}` : 'No file');
      if (req.file) {
        console.log('File object keys:', Object.keys(req.file));
      }
      
      // Handle avatar selection - either uploaded file or predefined avatar
      if (req.body.selectedAvatar && req.body.selectedAvatar.trim() !== '') {
        // User selected a predefined avatar - store reference to existing file
        updateData.avatar = req.body.selectedAvatar;
        console.log('Using predefined avatar:', req.body.selectedAvatar);
      } else if (req.file) {
        // User uploaded a custom avatar (including selfie)
        const fileExtension = req.file.originalname ? 
          req.file.originalname.split('.').pop() : 
          (req.file.mimetype === 'image/jpeg' ? 'jpg' : 'png');
        
        // Follow Image Canon for avatar processing
        const baseFilename = `user_${userId}`;
        const tempInputPath = path.join('temp/uploads', `temp_${Date.now()}.${fileExtension}`);
        const avatarPath = path.join('public/ll_images', `${baseFilename}.jpg`);
        
        // Ensure directories exist
        await fs.mkdir('temp/uploads', { recursive: true });
        await fs.mkdir(path.dirname(avatarPath), { recursive: true });
        
        try {
          // Get file data - handle both buffer and file path cases
          let fileData: Buffer;
          if (req.file.buffer) {
            fileData = req.file.buffer;
          } else if (req.file.path) {
            fileData = await fs.readFile(req.file.path);
          } else {
            throw new Error('No file data available in req.file');
          }
          
          // Save temp file for ImageMagick processing
          await fs.writeFile(tempInputPath, fileData);
          console.log(`Temp file saved: ${fileData.length} bytes`);
          
          // Image Canon: Process with ImageMagick via spawn
          const { spawn } = await import('child_process');
          
          // Main avatar (400px) - square crop for profile pics
          await new Promise((resolve, reject) => {
            const convert = spawn('convert', [
              tempInputPath,
              '-resize', '400x400^',
              '-gravity', 'center',
              '-crop', '400x400+0+0',
              '-quality', '85',
              '-strip',
              '-auto-orient',
              '-background', 'white',
              '-flatten',
              '-unsharp', '0.25x0.25+8+0.065',
              avatarPath
            ]);
            convert.on('close', (code) => code === 0 ? resolve(null) : reject(new Error(`Convert failed: ${code}`)));
            convert.on('error', reject);
          });
          
          console.log(`Avatar processed: ${baseFilename}.jpg (400px)`);
          
          // Clean up temp file
          await fs.unlink(tempInputPath).catch(() => {});
          
        } catch (error) {
          console.error('Avatar processing failed:', error);
          // Clean up temp file on error
          await fs.unlink(tempInputPath).catch(() => {});
          throw error;
        }
        
        // Store only the base filename in database (following Image Canon)
        updateData.avatar = `${baseFilename}.jpg`;
        
        console.log(`Avatar saved to filesystem: ${avatarPath}`);
      } else {
        console.log('No avatar update - neither file nor predefined avatar selected');
      }
      
      // Update user in database
      await storage.updateUser(userId, updateData);
      
      res.json({ success: true, message: 'Profile updated successfully' });
    } catch (error) {
      console.error('Error updating profile:', error);
      res.status(500).json({ success: false, message: 'Failed to update profile' });
    }
  });

  // Static Pages Management Routes (before other admin routes)
  
  // List all static pages (Admin)
  app.get('/admin/static-pages', async (req: Request, res: Response) => {
    try {
      console.log('Admin static pages route accessed');
      const session = req.session as SessionData;
      
      if (!session.userId) {
        session.userId = 1; // Set admin user for demo
        session.username = 'dilly.admin';
      }

      const user = await storage.getUser(session.userId);
      if (!user || !await storage.isUserAdmin(session.userId)) {
        return res.status(403).send('Access denied - Admin privileges required');
      }

      const staticPages = await storage.getAllStaticPages();
      console.log('Found static pages:', staticPages.length);
      
      const html = await templateEngine.render('admin-static-pages.html.twig', {
        currentUser: user,
        user,
        staticPages,
        pageTitle: 'Manage Static Pages'
      });
      
      res.send(html);
    } catch (error) {
      console.error('Error loading static pages admin:', error);
      res.status(500).send('Error loading static pages interface');
    }
  });

  // Create new static page form (Admin)
  app.get('/admin/static-pages/new', async (req: Request, res: Response) => {
    try {
      const session = req.session as SessionData;
      
      if (!session.userId) {
        session.userId = 1; // Set admin user for demo
        session.username = 'dilly.admin';
      }

      const user = await storage.getUser(session.userId);
      if (!user || !await storage.isUserAdmin(session.userId)) {
        return res.status(403).send('Access denied - Admin privileges required');
      }
      
      const html = await templateEngine.render('admin-static-page-edit.html.twig', {
        currentUser: user,
        user,
        isNew: true,
        pageTitle: 'Create New Static Page',
        site_url: 'https://locallendables.example.com'
      });
      
      res.send(html);
    } catch (error) {
      console.error('Error loading new static page form:', error);
      res.status(500).send('Error loading page creation interface');
    }
  });

  // Edit static page form (Admin)
  app.get('/admin/static-pages/:slug/edit', async (req: Request, res: Response) => {
    try {
      const session = req.session as SessionData;
      
      if (!session.userId) {
        session.userId = 1; // Set admin user for demo
        session.username = 'dilly.admin';
      }

      const user = await storage.getUser(session.userId);
      if (!user || !await storage.isUserAdmin(session.userId)) {
        return res.status(403).send('Access denied - Admin privileges required');
      }

      const page = await storage.getStaticPageBySlug(req.params.slug);
      if (!page) {
        return res.status(404).send('Static page not found');
      }
      
      const html = await templateEngine.render('admin-static-page-edit.html.twig', {
        currentUser: user,
        user,
        page,
        isNew: false,
        pageTitle: `Edit ${page.title}`,
        site_url: 'https://locallendables.example.com'
      });
      
      res.send(html);
    } catch (error) {
      console.error('Error loading static page edit form:', error);
      res.status(500).send('Error loading page edit interface');
    }
  });

  // Create static page (POST)
  app.post('/admin/static-pages/create', async (req: Request, res: Response) => {
    try {
      const session = req.session as SessionData;
      
      if (!session.userId) {
        session.userId = 1; // Set admin user for demo
        session.username = 'dilly.admin';
      }

      const user = await storage.getUser(session.userId);
      if (!user || !await storage.isUserAdmin(session.userId)) {
        return res.status(403).send('Access denied - Admin privileges required');
      }

      const pageData = {
        slug: req.body.slug,
        title: req.body.title,
        content: req.body.content,
        isActive: req.body.isActive === 'on',
        sortOrder: parseInt(req.body.sortOrder) || 0
      };

      const createdPage = await storage.createStaticPage(pageData, session.userId);
      
      if (createdPage) {
        res.redirect('/admin/static-pages');
      } else {
        res.status(500).send('Failed to create static page');
      }
    } catch (error) {
      console.error('Error creating static page:', error);
      res.status(500).send('Error creating static page');
    }
  });

  // Update static page (POST)
  app.post('/admin/static-pages/:slug/update', async (req: Request, res: Response) => {
    try {
      const session = req.session as SessionData;
      
      if (!session.userId) {
        session.userId = 1; // Set admin user for demo
        session.username = 'dilly.admin';
      }

      const user = await storage.getUser(session.userId);
      if (!user || !await storage.isUserAdmin(session.userId)) {
        return res.status(403).send('Access denied - Admin privileges required');
      }

      const pageData = {
        title: req.body.title,
        content: req.body.content,
        isActive: req.body.isActive === 'on',
        sortOrder: parseInt(req.body.sortOrder) || 0
      };

      const updatedPage = await storage.updateStaticPage(req.params.slug, pageData, session.userId);
      
      if (updatedPage) {
        res.redirect('/admin/static-pages');
      } else {
        res.status(500).send('Failed to update static page');
      }
    } catch (error) {
      console.error('Error updating static page:', error);
      res.status(500).send('Error updating static page');
    }
  });

  // Delete static page
  app.get('/admin/static-pages/:slug/delete', async (req: Request, res: Response) => {
    try {
      const session = req.session as SessionData;
      
      if (!session.userId) {
        session.userId = 1; // Set admin user for demo
        session.username = 'dilly.admin';
      }

      const user = await storage.getUser(session.userId);
      if (!user || !await storage.isUserAdmin(session.userId)) {
        return res.status(403).send('Access denied - Admin privileges required');
      }

      const success = await storage.deleteStaticPage(req.params.slug);
      
      if (success) {
        res.redirect('/admin/static-pages');
      } else {
        res.status(500).send('Failed to delete static page');
      }
    } catch (error) {
      console.error('Error deleting static page:', error);
      res.status(500).send('Error deleting static page');
    }
  });

  // Admin Email Dashboard Route
  app.get('/admin/email', async (req: Request, res: Response) => {
    try {
      const userId = req.session?.userId;
      if (!userId) {
        return res.redirect('/login?redirect=/admin/email');
      }

      const user = await storage.getUser(userId);
      if (!user || !user.roles?.includes('ROLE_ADMIN')) {
        return res.status(403).render('error.html.twig', {
          error: 'Access denied. Admin privileges required.',
          user: user,
          breadcrumbs: generateBreadcrumbs('admin-email', '/admin/email', 'Email Management')
        });
      }

      const html = await templateEngine.render('admin-email-dashboard.html.twig', {
        user: user,
        currentUser: user,
        breadcrumbs: generateBreadcrumbs('admin-email', '/admin/email', 'Email Management'),
        pageTitle: 'Email System Management'
      });
      
      res.send(html);
    } catch (error) {
      console.error('Error rendering admin email dashboard:', error);
      res.status(500).render('error.html.twig', {
        error: 'Failed to load admin email dashboard',
        user: req.session?.userId ? await storage.getUser(req.session.userId) : null
      });
    }
  });

  // Admin Dashboard Route
  app.get('/admin/dashboard', async (req: Request, res: Response) => {
    try {
      // Ensure admin session is set
      const session = req.session as SessionData;
      if (!session.userId) {
        session.userId = 1; // Set admin user
        session.username = 'dilly.admin';
      }
      
      const conversations = await storage.getConversationsForAdmin();
      const stats = await storage.getAdminStats();
      
      // Calculate derived stats
      const activeConversations = conversations.filter(c => c.isActive).length;
      const totalMessages = conversations.reduce((sum, c) => sum + (parseInt(c.messageCount) || 0), 0);
      const uniqueItems = new Set(conversations.map(c => c.itemId)).size;
      
      // Get items and members for filters
      const items = await storage.getAllAvailableItems(false);
      const members = await storage.getAllUsers();
      
      const html = await templateEngine.render('admin-dashboard-new.html.twig', {
        conversations,
        stats,
        activeConversations: activeConversations || 0,
        totalMessages: totalMessages || 0,
        uniqueItems: uniqueItems || 0,
        items: items,
        members
      });
      
      res.send(html);
    } catch (error) {
      console.error('Error rendering admin dashboard:', error);
      res.status(500).send('Internal Server Error');
    }
  });

  // API: Delete chat message (user can only delete their own messages)
  app.delete('/api/chat/message/:messageId/delete', async (req: Request, res: Response) => {
    try {
      const messageId = parseInt(req.params.messageId);
      const session = req.session as SessionData;
      
      if (!session?.userId) {
        return res.status(401).json({ error: 'Authentication required' });
      }
      
      if (isNaN(messageId)) {
        return res.status(400).json({ error: 'Invalid message ID' });
      }

      // Check if user owns the message
      const message = await storage.getChatMessage(messageId);
      if (!message) {
        return res.status(404).json({ error: 'Message not found' });
      }
      
      if (message.sender_id !== session.userId) {
        return res.status(403).json({ error: 'You can only delete your own messages' });
      }

      // Delete the message from the database
      await storage.deleteMessage(messageId);
      
      res.json({ success: true, message: 'Message deleted successfully' });
    } catch (error) {
      console.error('Error deleting message:', error);
      res.status(500).json({ error: 'Failed to delete message' });
    }
  });

  // Admin API for message deletion
  app.delete('/admin/api/messages/:messageId', async (req: Request, res: Response) => {
    try {
      const messageId = parseInt(req.params.messageId);
      if (isNaN(messageId)) {
        return res.status(400).json({ error: 'Invalid message ID' });
      }

      // Delete the message from the database
      await storage.deleteMessage(messageId);
      
      res.json({ success: true, message: 'Message deleted successfully' });
    } catch (error) {
      console.error('Error deleting message:', error);
      res.status(500).json({ error: 'Failed to delete message' });
    }
  });

  // Admin API Routes for conversation management
  app.post('/admin/api/conversations/:id/deactivate', async (req: Request, res: Response) => {
    try {
      const conversationId = parseInt(req.params.id);
      const success = await storage.deactivateMessage(conversationId);
      
      if (success) {
        res.json({ success: true });
      } else {
        res.status(500).json({ success: false, error: 'Failed to deactivate conversation' });
      }
    } catch (error) {
      console.error('Error deactivating conversation:', error);
      res.status(500).json({ success: false, error: 'Internal server error' });
    }
  });

  app.post('/admin/api/conversations/:id/reactivate', async (req: Request, res: Response) => {
    try {
      const conversationId = parseInt(req.params.id);
      const success = await storage.reactivateMessage(conversationId);
      
      if (success) {
        res.json({ success: true });
      } else {
        res.status(500).json({ success: false, error: 'Failed to reactivate conversation' });
      }
    } catch (error) {
      console.error('Error reactivating conversation:', error);
      res.status(500).json({ success: false, error: 'Internal server error' });
    }
  });

  // Admin API Routes for activity management
  app.post('/admin/api/activity/:id/deactivate', async (req: Request, res: Response) => {
    try {
      const activityId = parseInt(req.params.id);
      const success = await storage.deactivateActivity(activityId);
      
      if (success) {
        res.json({ success: true });
      } else {
        res.status(500).json({ success: false, error: 'Failed to deactivate activity' });
      }
    } catch (error) {
      console.error('Error deactivating activity:', error);
      res.status(500).json({ success: false, error: 'Internal server error' });
    }
  });

  app.post('/admin/api/activity/:id/reactivate', async (req: Request, res: Response) => {
    try {
      const activityId = parseInt(req.params.id);
      const success = await storage.reactivateActivity(activityId);
      
      if (success) {
        res.json({ success: true });
      } else {
        res.status(500).json({ success: false, error: 'Failed to reactivate activity' });
      }
    } catch (error) {
      console.error('Error reactivating activity:', error);
      res.status(500).json({ success: false, error: 'Internal server error' });
    }
  });

  // Admin Site Configuration
  app.get('/admin/site-config', async (req: Request, res: Response) => {
    try {
      const session = req.session as SessionData;
      
      if (!session.userId) {
        return res.redirect('/demo');
      }

      const user = await storage.getUser(session.userId);
      if (!user || !user.roles || !user.roles.includes('ROLE_ADMIN')) {
        return res.status(403).send('Access denied - Admin privileges required');
      }

      // Get platform statistics
      const stats = await storage.getAdminStats();
      
      const html = await templateEngine.render('admin-site-config.html.twig', {
        user,
        stats,
        platformStats: stats,
        pageTitle: 'Admin - Site Configuration'
      });
      
      res.send(html);
    } catch (error) {
      console.error('Error loading admin site config:', error);
      res.status(500).send('Error loading configuration interface');
    }
  });





  app.get('*', async (req: Request, res: Response) => {
    // Skip catch-all for static assets but NOT API routes (they should be handled above)
    if (req.path.includes('.') || 
        req.path.startsWith('/avatars/') || req.path.startsWith('/lendable_items/') ||
        req.path.startsWith('/ll_images/') || req.path.startsWith('/manifest.json') ||
        req.path.startsWith('/sw.js')) {
      return res.status(404).send('Not found');
    }
    
    // For unmatched API routes, return JSON error
    if (req.path.startsWith('/api/')) {
      return res.status(404).json({ error: 'API endpoint not found' });
    }
    
    console.log(`CATCH-ALL TRIGGERED: ${req.path}`);
    console.log(`  Referrer: ${req.headers.referer || 'None'}`);
    console.log(`  User-Agent: ${req.headers['user-agent']?.substring(0, 50) || 'Unknown'}`);
    console.log(`  Method: ${req.method}`);
    console.log(`  Session ID: None (session disabled)`);
    console.log(`  Back navigation: ${req.headers.referer && req.headers.referer.includes('replit.dev') ? 'Likely' : 'No'}`);
    
    // Return a proper 404 error page with redirect to demo
    res.status(404).send(`
      <!DOCTYPE html>
      <html>
      <head>
        <title>Page Not Found - LocalLendables</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <style>
          body { 
            font-family: Arial, sans-serif; 
            text-align: center; 
            padding: 50px; 
            background-color: #f8f9fa;
          }
          .error { 
            background: #f8d7da; 
            color: #721c24; 
            padding: 20px; 
            border-radius: 5px; 
            margin: 20px auto; 
            max-width: 500px;
          }
          .btn {
            background: #28a745; 
            color: white; 
            padding: 15px 30px; 
            text-decoration: none; 
            border-radius: 5px;
            display: inline-block;
            margin: 10px;
          }
        </style>
      </head>
      <body>
        <h1>Page Not Found</h1>
        <div class="error">
          <p><strong>Error:</strong> The page "${req.path}" doesn't exist.</p>
          <p>This might be caused by browser back navigation or an invalid link.</p>
        </div>
        <a href="/demo" class="btn">Return to Demo Homepage</a>
        <script>
          // Auto-redirect after 5 seconds to prevent user confusion
          setTimeout(() => {
            window.location.href = '/demo';
          }, 5000);
        </script>
      </body>
      </html>
    `);
  });

  // New Admin Dashboard with Content Management
  app.get('/admin-dashboard-new', async (req: Request, res: Response) => {
    try {
      const conversations = await storage.getConversationsForAdmin();
      const stats = await storage.getAdminStats();
      
      // Calculate derived stats
      const activeConversations = conversations.filter(c => c.isActive).length;
      const totalMessages = conversations.reduce((sum, c) => sum + (parseInt(c.messageCount) || 0), 0);
      const uniqueItems = new Set(conversations.map(c => c.itemId)).size;
      
      // Get items and members for filters
      const items = await storage.getAllAvailableItems(false);
      const members = await storage.getAllUsers();
      
      // Set demo admin user
      const currentUser = await storage.getUser(1);
      
      const html = await templateEngine.render('admin-dashboard-new.html.twig', {
        conversations,
        stats,
        activeConversations: activeConversations || 0,
        totalMessages: totalMessages || 0,
        uniqueItems: uniqueItems || 0,
        items: items,
        members,
        currentUser: currentUser,
        user: currentUser,
        isAdmin: true
      });
      
      res.send(html);
    } catch (error) {
      console.error('Error rendering new admin dashboard:', error);
      res.status(500).send('Internal Server Error');
    }
  });



  // Display static page to public (MUST BE LAST - catch-all route)
  app.get('/:slug', async (req: Request, res: Response) => {
    try {
      // Skip static files (CSS, JS, images, etc.)
      if (req.params.slug.includes('.') || 
          req.params.slug.startsWith('api/') || 
          req.params.slug.startsWith('admin/') ||
          req.params.slug === 'admin' ||
          req.path.startsWith('/admin/') ||
          req.path.startsWith('/css/') ||
          req.path.startsWith('/js/') ||
          req.path.startsWith('/images/') ||
          req.path.startsWith('/public/')) {
        return res.status(404).send('Not found');
      }

      const page = await storage.getStaticPageBySlug(req.params.slug);
      
      if (!page || !page.isActive) {
        return res.status(404).send('Page not found');
      }

      const session = req.session as SessionData;
      let currentUser = null;
      let isAdmin = false;
      
      if (session?.userId) {
        currentUser = await storage.getUser(session.userId);
        isAdmin = await storage.isUserAdmin(session.userId);
      }

      // Get dynamic variables for this page
      const stats = await storage.getAdminStats();
      const variables = {
        site_name: 'LocalLendables',
        total_members: String(stats.totalUsers || 0),
        total_items: String(stats.totalItems || 0),
        active_loans: String(stats.activeLoans || 0)
      };

      const processedContent = storage.processStaticPageContent(page.content, variables);
      
      const html = await templateEngine.render('static-page.html.twig', {
        pageTitle: page.title,
        pageContent: page.content,
        processedContent: processedContent,
        pageSlug: page.slug,
        currentUser,
        user: currentUser,
        isAdmin
      });
      
      res.send(html);
    } catch (error) {
      console.error('Error displaying static page:', error);
      res.status(500).send('Error loading page');
    }
  });

  // Send reminder API endpoint
  app.post('/api/send-reminder', upload.none(), async (req: Request, res: Response) => {
    try {
      const session = req.session as SessionData;
      const userId = session?.userId;
      
      if (!userId) {
        return res.status(401).json({ 
          success: false, 
          message: 'User authentication required for sending reminders' 
        });
      }

      const { notificationId, itemId, message, sendSMS, markUrgent } = req.body;
      
      if (!notificationId || !itemId || !message) {
        return res.status(400).json({ 
          success: false, 
          message: 'Missing required fields' 
        });
      }
      
      console.log('Processing reminder request:', { notificationId, itemId, message, sendSMS, markUrgent });
      
      // Get item details and loan information
      const item = await storage.getItem(parseInt(itemId));
      if (!item) {
        return res.status(404).json({ 
          success: false, 
          message: 'Item not found' 
        });
      }
      
      // Verify user is the item owner
      if (item.owner !== userId) {
        return res.status(403).json({ 
          success: false, 
          message: 'Only the item owner can send reminders' 
        });
      }
      
      // Get loan status to find borrower
      const loanStatus = await storage.getItemLoanStatus(parseInt(itemId));
      if (!loanStatus?.borrowerId) {
        return res.status(400).json({ 
          success: false, 
          message: 'No active loan found for this item' 
        });
      }
      
      // Get borrower details
      const borrower = await storage.getUserById(loanStatus.borrowerId);
      if (!borrower) {
        return res.status(404).json({ 
          success: false, 
          message: 'Borrower not found' 
        });
      }
      
      // Get owner details for the reminder
      const owner = await storage.getUserById(userId);
      if (!owner) {
        return res.status(404).json({ 
          success: false, 
          message: 'Owner not found' 
        });
      }
      
      // Create standardized reminder notification for borrower
      await storage.createStandardizedNotification({
        userId: borrower.id,
        type: 'reminder_sent',
        relatedId: parseInt(itemId),
        relatedType: 'item',
        templateData: {
          ownerName: owner.showName || owner.firstName + ' ' + owner.lastName,
          itemName: item.name,
          reminderMessage: message
        }
      });
      
      // Handle SMS if requested and borrower has phone and opted in
      let smsResult = null;
      if (sendSMS === 'on' && borrower.phone) {
        // Check if borrower has SMS notifications enabled
        const borrowerPrefs = await storage.getUserPreferences(borrower.id);
        
        if (borrowerPrefs?.smsNotifications) {
          try {
            // For now, log SMS attempt (would integrate with Twilio or similar service)
            console.log('SMS reminder would be sent to:', borrower.phone, 'Message:', message);
            smsResult = { success: true, message: 'SMS sent successfully' };
            
            // TODO: Integrate with actual SMS service
            // const smsService = new SMSService();
            // smsResult = await smsService.sendSMS(borrower.phone, `Reminder about ${item.name}: ${message}`);
            
          } catch (smsError) {
            console.error('Error sending SMS reminder:', smsError);
            smsResult = { success: false, message: 'SMS failed to send' };
          }
        } else {
          smsResult = { success: false, message: 'Borrower has not opted in to SMS notifications' };
        }
      }
      
      // Mark original notification as dismissed since we've acted on it
      await storage.dismissNotification(parseInt(notificationId));
      
      console.log('Reminder sent successfully:', {
        borrower: borrower.showName || borrower.firstName,
        item: item.name,
        smsResult: smsResult
      });
      
      res.json({ 
        success: true, 
        message: 'Reminder sent successfully',
        smsResult: smsResult
      });
      
    } catch (error) {
      console.error('Error sending reminder:', error);
      res.status(500).json({ 
        success: false, 
        message: 'Failed to send reminder: ' + (error as Error).message 
      });
    }
  });

  // Get item loan status API (needed for reminder form)
  app.get('/api/items/:id/loan-status', async (req: Request, res: Response) => {
    try {
      const itemId = parseInt(req.params.id);
      const loanStatus = await storage.getItemLoanStatus(itemId);
      
      if (!loanStatus) {
        return res.json({ success: false, message: 'No loan found' });
      }
      
      // Get borrower details if there's an active loan
      let borrower = null;
      if (loanStatus.borrowerId) {
        borrower = await storage.getUserById(loanStatus.borrowerId);
      }
      
      res.json({ 
        success: true, 
        borrower: borrower ? {
          id: borrower.id,
          showName: borrower.showName,
          firstName: borrower.firstName,
          lastName: borrower.lastName
        } : null,
        dueDate: loanStatus.expectedReturnDate,
        status: loanStatus.status
      });
      
    } catch (error) {
      console.error('Error getting loan status:', error);
      res.status(500).json({ 
        success: false, 
        message: 'Failed to get loan status' 
      });
    }
  });

}