import express from 'express';
import path from 'node:path';
import { fileURLToPath } from 'node:url';

// ESM polyfill — Phase 9 §9.1. Handlers below use __dirname to resolve the
// db-convert/db/files/{uploads,avatars} static asset paths.
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

const router = express.Router();

// Serve uploaded files with proper mapping from database paths to actual file locations
router.get('/files/uploads/*', async (req, res) => {
  try {
    // Extract the path from the URL (everything after /files/uploads/)
    const requestPath = req.params[0];
    
    // The URL path will be like: ufs/AmazonS3:Uploads/274Mf9CyHNG72oF86/filename.jpg
    // We need to extract the mongo_id (274Mf9CyHNG72oF86) from this path
    const pathParts = requestPath.split('/');
    let mongoId = null;
    
    // Find the mongo_id in the path structure
    for (let i = 0; i < pathParts.length; i++) {
      if (pathParts[i].includes('AmazonS3:Uploads') && i + 1 < pathParts.length) {
        mongoId = pathParts[i + 1];
        break;
      }
      // Sometimes the mongo_id might be the last part of ufs/AmazonS3:Uploads/mongoId
      if (pathParts[i] === 'AmazonS3:Uploads' && i + 1 < pathParts.length) {
        mongoId = pathParts[i + 1];
        break;
      }
    }
    
    if (!mongoId) {
      // Try to get mongo_id from database by matching the full path
      const result = await global.pool.query(`
        SELECT mongo_id, name, type 
        FROM uploads 
        WHERE path = $1 OR url = $1
        LIMIT 1
      `, [`/ufs/AmazonS3:Uploads/${requestPath}`, `/ufs/AmazonS3:Uploads/${requestPath}`]);
      
      if (result.rows.length > 0) {
        mongoId = result.rows[0].mongo_id;
      }
    }
    
    if (!mongoId) {
      return res.status(404).json({ error: 'File not found' });
    }
    
    // The actual file is stored with just the mongo_id as filename
    const filePath = path.join(__dirname, 'db-convert/db/files/uploads', mongoId);
    
    // Get file info from database for proper content-type
    const fileInfo = await global.pool.query(`
      SELECT name, type 
      FROM uploads 
      WHERE mongo_id = $1
      LIMIT 1
    `, [mongoId]);
    
    if (fileInfo.rows.length === 0) {
      return res.status(404).json({ error: 'File metadata not found' });
    }
    
    const { name, type } = fileInfo.rows[0];
    
    // Set proper content type
    if (type) {
      res.set('Content-Type', type);
    }
    
    // Set content disposition with original filename
    if (name) {
      res.set('Content-Disposition', `inline; filename="${name}"`);
    }
    
    // Send the file
    res.sendFile(filePath, (err) => {
      if (err) {
        console.error('Error serving file:', err);
        if (!res.headersSent) {
          res.status(404).json({ error: 'File not found on disk' });
        }
      }
    });
    
  } catch (error) {
    console.error('Error serving upload:', error);
    res.status(500).json({ error: 'Server error' });
  }
});

// Also serve files directly by mongo_id for simpler access
router.get('/files/by-id/:mongoId', async (req, res) => {
  try {
    const { mongoId } = req.params;
    
    // Get file info from database
    const fileInfo = await global.pool.query(`
      SELECT name, type 
      FROM uploads 
      WHERE mongo_id = $1
      LIMIT 1
    `, [mongoId]);
    
    if (fileInfo.rows.length === 0) {
      return res.status(404).json({ error: 'File not found' });
    }
    
    const { name, type } = fileInfo.rows[0];
    const filePath = path.join(__dirname, 'db-convert/db/files/uploads', mongoId);
    
    // Set proper content type and filename
    if (type) {
      res.set('Content-Type', type);
    }
    
    if (name) {
      res.set('Content-Disposition', `inline; filename="${name}"`);
    }
    
    // Send the file
    res.sendFile(filePath, (err) => {
      if (err) {
        console.error('Error serving file:', err);
        if (!res.headersSent) {
          res.status(404).json({ error: 'File not found on disk' });
        }
      }
    });
    
  } catch (error) {
    console.error('Error serving upload by ID:', error);
    res.status(500).json({ error: 'Server error' });
  }
});

// Serve user avatars by mongo_id
router.get('/avatar/:mongoId', async (req, res) => {
  try {
    const { mongoId } = req.params;
    
    console.log(`[Avatar Debug] Looking up avatar for user mongo_id: ${mongoId}`);
    
    // First try to find avatar by user's avataretag
    const userResult = await global.pool.query(`
      SELECT avataretag, username FROM users WHERE mongo_id = $1
    `, [mongoId]);
    
    let avatarPath = null;
    
    if (userResult.rows.length > 0) {
      const username = userResult.rows[0].username;
      const avataretag = userResult.rows[0].avataretag;
      
      // Try method 1: Look up by avataretag -> etag (for users with avataretag set)
      if (avataretag) {
        console.log(`[Avatar Debug] Found user ${username} with avataretag: ${avataretag}`);
        
        const avatarResult = await global.pool.query(`
          SELECT url, path FROM avatars WHERE etag = $1
        `, [avataretag]);
        
        if (avatarResult.rows.length > 0) {
          const dbPath = avatarResult.rows[0].path || avatarResult.rows[0].url;
          console.log(`[Avatar Debug] Found avatar record with path: ${dbPath}`);
          
          if (dbPath) {
            const pathParts = dbPath.split('/');
            for (let i = 0; i < pathParts.length; i++) {
              if (pathParts[i].includes('AmazonS3:Avatars') && i + 1 < pathParts.length) {
                const avatarMongoId = pathParts[i + 1];
                avatarPath = path.join(__dirname, 'db-convert/db/files/avatars', avatarMongoId);
                console.log(`[Avatar Debug] Extracted avatar mongo_id: ${avatarMongoId}, full path: ${avatarPath}`);
                break;
              }
            }
          }
        } else {
          console.log(`[Avatar Debug] No avatar record found for etag: ${avataretag}`);
        }
      }
      
      // Try method 2: Look up by userid directly (for users without avataretag)
      if (!avatarPath) {
        console.log(`[Avatar Debug] Trying direct userid lookup for user ${username} (${mongoId})`);
        
        const avatarResult = await global.pool.query(`
          SELECT url, path FROM avatars WHERE userid = $1
        `, [mongoId]);
        
        if (avatarResult.rows.length > 0) {
          const dbPath = avatarResult.rows[0].path || avatarResult.rows[0].url;
          console.log(`[Avatar Debug] Found avatar record by userid with path: ${dbPath}`);
          
          if (dbPath) {
            const pathParts = dbPath.split('/');
            for (let i = 0; i < pathParts.length; i++) {
              if (pathParts[i].includes('AmazonS3:Avatars') && i + 1 < pathParts.length) {
                const avatarMongoId = pathParts[i + 1];
                avatarPath = path.join(__dirname, 'db-convert/db/files/avatars', avatarMongoId);
                console.log(`[Avatar Debug] Extracted avatar mongo_id: ${avatarMongoId}, full path: ${avatarPath}`);
                break;
              }
            }
          }
        } else {
          console.log(`[Avatar Debug] No avatar record found for userid: ${mongoId}`);
        }
      }
    } else {
      console.log(`[Avatar Debug] No user found for mongo_id: ${mongoId}`);
    }
    
    // Fallback: try direct lookup by user mongo_id
    if (!avatarPath) {
      avatarPath = path.join(__dirname, 'db-convert/db/files/avatars', mongoId);
      console.log(`[Avatar Debug] Using fallback path: ${avatarPath}`);
    }
    
    // Set proper content type for images
    res.set('Content-Type', 'image/jpeg'); // Most avatars are likely JPEG
    
    // Send the file
    res.sendFile(avatarPath, (err) => {
      if (err) {
        // If avatar doesn't exist, send a default 404 or generate initials
        console.log(`[Avatar Debug] Avatar file not found at path: ${avatarPath}, error:`, err.message);
        if (!res.headersSent) {
          res.status(404).json({ error: 'Avatar not found' });
        }
      } else {
        console.log(`[Avatar Debug] Successfully served avatar from: ${avatarPath}`);
      }
    });
    
  } catch (error) {
    console.error('Error serving avatar:', error);
    res.status(500).json({ error: 'Server error' });
  }
});

// Serve avatars statically as fallback
router.use('/files/avatars', express.static(path.join(__dirname, 'db-convert/db/files/avatars')));

// Get all users for the "view as" dropdown (active and inactive)
router.get('/users', async (req, res) => {
  try {
    const result = await global.pool.query(`
      SELECT id, username, name, type, active, status, lastlogin, 
             statustext, utcoffset, statusconnection, mongo_id, avataretag
      FROM users 
      WHERE type = 'user'
      ORDER BY 
        active DESC,  -- Active users first
        CASE 
          WHEN status = 'online' THEN 1
          WHEN status = 'away' THEN 2  
          WHEN status = 'busy' THEN 3
          ELSE 4
        END,
        name ASC
    `);
    
    res.json({
      status: 'success',
      users: result.rows
    });
  } catch (error) {
    console.error('Error fetching users:', error);
    res.status(500).json({ 
      status: 'error',
      error: 'Failed to fetch users',
      details: error.message
    });
  }
});

// Get rooms for a specific user with enhanced room names for direct messages
router.get('/users/:userId/rooms', async (req, res) => {
  const { userId } = req.params;
  
  try {
    // Get the current user's mongo_id for filtering
    const userResult = await global.pool.query(`
      SELECT mongo_id, username FROM users WHERE id = $1
    `, [userId]);
    
    if (userResult.rows.length === 0) {
      return res.status(404).json({
        status: 'error',
        error: 'User not found'
      });
    }
    
    const currentUserMongoId = userResult.rows[0].mongo_id;
    const currentUsername = userResult.rows[0].username;
    
    // Get rooms where the user is a member with proper naming from subscription table
    // Include archived and closed rooms but sort them at the bottom
    const result = await global.pool.query(`
      SELECT DISTINCT 
        r.id, 
        r.mongo_id as room_mongo_id, 
        r.name, 
        r.fname, 
        r.t as type, 
        r.msgs, 
        r.lm as last_message_date, 
        r.usernames, 
        r.uids,
        r.userscount,
        r.description,
        r.teamid,
        r.archived,
        s.open,
        -- Use the subscription's name for direct messages (excludes current user)
        -- For channels/groups, use room's fname or name
        CASE 
          WHEN r.t = 'd' THEN COALESCE(s.fname, s.name, 'Unknown User')
          ELSE COALESCE(r.fname, r.name, 'Unnamed Room')
        END as display_name
      FROM room r
      JOIN subscription s ON s.rid = r.mongo_id 
      WHERE s.u->>'_id' = $1
      ORDER BY 
        s.open DESC NULLS LAST,  -- Open rooms first
        r.archived NULLS FIRST,  -- Non-archived first (nulls treated as false)
        r.lm DESC NULLS LAST
      LIMIT 50
    `, [currentUserMongoId]);

    // Enhance rooms with participant information for direct messages
    const enhancedRooms = await Promise.all(result.rows.map(async (room) => {
      if (room.type === 'd' && room.uids) {
        // Get participant info (excluding current user) for direct messages
        const participantResult = await global.pool.query(`
          SELECT u.username, u.name, u.mongo_id, u.avataretag
          FROM users u 
          WHERE u.mongo_id = ANY($1::text[])
          AND u.mongo_id != $2
        `, [room.uids, currentUserMongoId]);
        
        room.participants = participantResult.rows;
      }
      return room;
    }));
    
    res.json({
      status: 'success',
      rooms: enhancedRooms
    });
  } catch (error) {
    console.error('Error fetching user rooms:', error);
    res.status(500).json({ 
      status: 'error',
      error: 'Failed to fetch user rooms',
      details: error.message
    });
  }
});

// Get room details including participants
router.get('/rooms/:roomId', async (req, res) => {
  const { roomId } = req.params;
  const { userId } = req.query; // Accept current user ID as query parameter
  
  try {
    const result = await global.pool.query(`
      SELECT r.id, r.name, r.fname, r.t as type, r.msgs, r.description,
             r.lm as last_message_date, r.usernames, r.uids, r.userscount, r.teamid
      FROM room r
      WHERE r.id = $1
    `, [roomId]);
    
    if (result.rows.length === 0) {
      return res.status(404).json({
        status: 'error',
        error: 'Room not found'
      });
    }
    
    const room = result.rows[0];
    
    // For direct messages, get the proper display name based on current user
    if (room.type === 'd' && room.uids && userId) {
      // Get current user's mongo_id
      const userResult = await global.pool.query(`
        SELECT mongo_id FROM users WHERE id = $1
      `, [userId]);
      
      if (userResult.rows.length > 0) {
        const currentUserMongoId = userResult.rows[0].mongo_id;
        
        // Get display name from subscription table for this user
        // Use room mongo_id to match with subscription.rid
        const roomMongoResult = await global.pool.query(`
          SELECT mongo_id FROM room WHERE id = $1
        `, [roomId]);
        
        if (roomMongoResult.rows.length > 0) {
          const roomMongoId = roomMongoResult.rows[0].mongo_id;
          
          const subscriptionResult = await global.pool.query(`
            SELECT fname, name FROM subscription 
            WHERE rid = $1 AND u->>'_id' = $2
          `, [roomMongoId, currentUserMongoId]);
          
          if (subscriptionResult.rows.length > 0) {
            const sub = subscriptionResult.rows[0];
            room.display_name = sub.fname || sub.name || 'Unknown User';
          }
        }
      }
      
      // Get all participants for additional info
      const participantResult = await global.pool.query(`
        SELECT username, name
        FROM users 
        WHERE mongo_id = ANY($1::text[])
      `, [room.uids]);
      
      room.participants = participantResult.rows;
    } else {
      // For channels/groups, use room's fname or name
      room.display_name = room.fname || room.name || 'Unnamed Room';
    }
    
    res.json({
      status: 'success',
      room: room
    });
  } catch (error) {
    console.error('Error fetching room details:', error);
    res.status(500).json({ 
      status: 'error',
      error: 'Failed to fetch room details',
      details: error.message
    });
  }
});

// Get messages for a specific room (fast, without attachments)
router.get('/rooms/:roomId/messages', async (req, res) => {
  const { roomId } = req.params;
  const { limit = 50, offset = 0, before } = req.query;
  
  try {
    // Fast query - just get messages without expensive attachment joins
    let query = `
      SELECT m.id, m.msg, m.ts, m.u, m._updatedat, m.urls, m.mentions, m.md
      FROM message m
      JOIN room r ON m.rid = r.mongo_id
      WHERE r.id = $1
    `;
    
    const params = [roomId];
    
    if (before) {
      query += ` AND m.ts < $${params.length + 1}`;
      params.push(before);
    }
    
    query += ` ORDER BY m.ts DESC LIMIT $${params.length + 1} OFFSET $${params.length + 2}`;
    params.push(limit, offset);
    
    const result = await global.pool.query(query, params);
    
    // Add empty attachments array for now - attachments will be loaded separately if needed
    const messages = result.rows.map(msg => ({
      ...msg,
      attachments: []
    }));
    
    res.json({
      status: 'success',
      messages: messages.reverse() // Reverse to show oldest first
    });
  } catch (error) {
    console.error('Error fetching messages:', error);
    res.status(500).json({ 
      status: 'error',
      error: 'Failed to fetch messages',
      details: error.message
    });
  }
});

// Get attachments for specific messages (called separately for performance)
router.post('/messages/attachments', async (req, res) => {
  const { messageIds } = req.body;
  
  if (!messageIds || !Array.isArray(messageIds) || messageIds.length === 0) {
    return res.json({ status: 'success', attachments: {} });
  }
  
  try {
    // Get room mongo_id from first message to limit search scope
    const roomQuery = await global.pool.query(`
      SELECT r.mongo_id as room_mongo_id
      FROM message m
      JOIN room r ON m.rid = r.mongo_id
      WHERE m.id = $1
      LIMIT 1
    `, [messageIds[0]]);
    
    if (roomQuery.rows.length === 0) {
      return res.json({ status: 'success', attachments: {} });
    }
    
    const roomMongoId = roomQuery.rows[0].room_mongo_id;
    
    // Get messages and their upload timestamps
    const messagesQuery = await global.pool.query(`
      SELECT m.id, m.ts, m.u->>'_id' as user_id
      FROM message m
      WHERE m.id = ANY($1::int[])
    `, [messageIds]);
    
    if (messagesQuery.rows.length === 0) {
      return res.json({ status: 'success', attachments: {} });
    }
    
    // Build a map of user_id -> array of message timestamps for efficient lookup
    const userTimeMap = {};
    const messageMap = {};
    messagesQuery.rows.forEach(msg => {
      if (!userTimeMap[msg.user_id]) {
        userTimeMap[msg.user_id] = [];
      }
      userTimeMap[msg.user_id].push(msg.ts);
      messageMap[msg.id] = { ts: msg.ts, user_id: msg.user_id };
    });
    
    // Get attachments for this room and these users
    const uploadsQuery = await global.pool.query(`
      SELECT mongo_id, name, size, type, url, path, typegroup, identify, 
             userid, uploadedat
      FROM uploads 
      WHERE rid = $1 
      AND userid = ANY($2::text[])
      ORDER BY uploadedat
    `, [roomMongoId, Object.keys(userTimeMap)]);
    
    // Match attachments to messages based on timestamp proximity (within 5 minutes)
    const attachmentsByMessage = {};
    
    uploadsQuery.rows.forEach(upload => {
      const uploadTime = new Date(upload.uploadedat).getTime();
      
      // Find the closest message from this user within 5 minutes
      let closestMessageId = null;
      let closestTimeDiff = Infinity;
      
      Object.entries(messageMap).forEach(([msgId, msgData]) => {
        if (msgData.user_id === upload.userid) {
          const msgTime = new Date(msgData.ts).getTime();
          const timeDiff = Math.abs(uploadTime - msgTime);
          
          if (timeDiff < 300000 && timeDiff < closestTimeDiff) { // 5 minutes = 300000ms
            closestMessageId = msgId;
            closestTimeDiff = timeDiff;
          }
        }
      });
      
      if (closestMessageId) {
        if (!attachmentsByMessage[closestMessageId]) {
          attachmentsByMessage[closestMessageId] = [];
        }
        
        attachmentsByMessage[closestMessageId].push({
          id: upload.id,
          mongo_id: upload.mongo_id,
          name: upload.name,
          size: upload.size,
          type: upload.type,
          url: upload.url,
          path: upload.path,
          typegroup: upload.typegroup,
          identify: upload.identify
        });
      }
    });
    
    res.json({
      status: 'success',
      attachments: attachmentsByMessage
    });
    
  } catch (error) {
    console.error('Error fetching message attachments:', error);
    res.status(500).json({ 
      status: 'error',
      error: 'Failed to fetch attachments',
      details: error.message
    });
  }
});

// Search messages in accessible rooms for a user
router.get('/users/:userId/search', async (req, res) => {
  const { userId } = req.params;
  const { q, limit = 20 } = req.query;
  
  if (!q || q.length < 2) {
    return res.status(400).json({
      status: 'error',
      error: 'Search query must be at least 2 characters'
    });
  }
  
  try {
    const userResult = await global.pool.query(`
      SELECT mongo_id FROM users WHERE id = $1
    `, [userId]);
    
    if (userResult.rows.length === 0) {
      return res.status(404).json({
        status: 'error',
        error: 'User not found'
      });
    }
    
    const currentUserMongoId = userResult.rows[0].mongo_id;
    
    const result = await global.pool.query(`
      SELECT m.id, m.msg, m.ts, m.u, r.id as room_id, r.name as room_name, r.fname as room_fname, r.t as room_type
      FROM message m
      JOIN room r ON m.rid = r.mongo_id
      JOIN subscription s ON s.rid = r.mongo_id AND s.u->>'_id' = $1
      WHERE m.msg ILIKE $2
      AND r.archived IS NOT TRUE
      ORDER BY m.ts DESC
      LIMIT $3
    `, [currentUserMongoId, `%${q}%`, limit]);
    
    res.json({
      status: 'success',
      results: result.rows
    });
  } catch (error) {
    console.error('Error searching messages:', error);
    res.status(500).json({ 
      status: 'error',
      error: 'Failed to search messages',
      details: error.message
    });
  }
});

export default router;
