import { Server } from "socket.io";
import { createServer } from "http";
import app from "../app";
import database from "./db";
import http from "http";

// // HTTP server Create
// const server = http.createServer(app);

// // socket.io Server creation
// const io = new Server(server, {
//   cors: {
//     origin: [process.env.PORTFOLIO_URL, process.env.DASHBOARD_URL].filter(
//       (s): s is string => !!s
//     ),
//     methods: ["GET", "POST", "PUT", "DELETE"],
//     credentials: true,
//   },
// });

// //Middleware: You can use all sockets with req.io
// app.set("io", io);

// export { io, app, server };



// HTTP server Create
const server = createServer(app);

// socket.io Server creation
const io = new Server(server, {
  cors: {
    origin: [process.env.PORTFOLIO_URL, process.env.DASHBOARD_URL].filter(
      (s): s is string => !!s,
    ),
    methods: ["GET", "POST", "PUT", "DELETE"],
    credentials: true,
  },
  connectionStateRecovery: {},
});

//Middleware: You can use all sockets with req.io
app.set("io", io);

// Socket.io authentication middleware for verifying JWT tokens
io.use(async (socket, next) => {
  try {
    const token =
      socket.handshake.auth.access_token ||
      socket.handshake.headers.access_token;

    if (!token) {
      return next(new Error("Authentication error: Token required"));
    }

    // Verify token (use your JWT verification logic)
    const jwt = require("jsonwebtoken");
    const decoded = jwt.verify(token, process.env.JWT_SECRET!);

    // Fetch user from database
    const result = await database.query(
      `SELECT * FROM accounts WHERE id = $1 AND is_active = true`,
      [decoded.id],
    );

    if (result.rows.length === 0) {
      return next(new Error("Authentication error: User not found"));
    }

    socket.data.user = result.rows[0];
    next();
  } catch (error) {
    next(new Error("Authentication error: Invalid token"));
  }
});

// Store connected users
const connectedUsers = new Map();

io.on("connection", (socket) => {
  const user = socket.data.user;

  if (!user) {
    socket.disconnect();
    return;
  }

  console.log(`✅ User connected: ${user.username} (${socket.id})`);

  // Store socket by user ID
  connectedUsers.set(user.id, socket.id);

  // Join user to their personal room
  socket.join(`user:${user.id}`);

  // Join user to notification room
  socket.join(`notifications:${user.id}`);

  // Send online status to followers
  socket.broadcast.emit("user:online", {
    user_id: user.id,
    username: user.username,
    timestamp: new Date().toISOString(),
  });

  // Notification events
  socket.on("notification:read", async (data) => {
    try {
      const { notification_id } = data;

      // Mark notification as read in database
      await database.query(
        `UPDATE notifications 
         SET is_read = true, read_at = CURRENT_TIMESTAMP
         WHERE id = $1 AND recipient_id = $2`,
        [notification_id, user.id],
      );

      // Emit update to user
      socket.emit("notification:updated", {
        notification_id,
        is_read: true,
      });

      // Update unread count
      const unreadResult = await database.query(
        `SELECT get_unread_notification_count($1) as unread_count`,
        [user.id],
      );

      const unreadCount = parseInt(unreadResult.rows[0]?.unread_count || 0);

      socket.emit("notification:unread_count", {
        count: unreadCount,
      });
    } catch (error) {
      console.error("Error marking notification as read:", error);
      socket.emit("notification:error", {
        message: "Failed to mark notification as read",
      });
    }
  });

  // Follow events
  socket.on("follow:request", async (data) => {
    try {
      const { target_id, message } = data;

      // Emit to target user if online
      const targetSocketId = connectedUsers.get(target_id);
      if (targetSocketId) {
        io.to(targetSocketId).emit("follow:request_received", {
          from_user_id: user.id,
          from_username: user.username,
          message,
          timestamp: new Date().toISOString(),
        });
      }
    } catch (error) {
      console.error("Error sending follow request:", error);
    }
  });

  // Activity events
  socket.on("activity:create", (data) => {
    // Broadcast to followers (except sender)
    socket.broadcast.emit("activity:new", {
      ...data,
      actor: {
        id: user.id,
        username: user.username,
        display_name: user.display_name,
      },
      timestamp: new Date().toISOString(),
    });
  });

  // Message events (if you implement messaging later)
  socket.on("message:send", (data) => {
    const { recipient_id, message } = data;

    // Emit to recipient if online
    const recipientSocketId = connectedUsers.get(recipient_id);
    if (recipientSocketId) {
      io.to(recipientSocketId).emit("message:received", {
        from_user_id: user.id,
        from_username: user.username,
        message,
        timestamp: new Date().toISOString(),
      });
    }
  });

  // Typing indicators
  socket.on("typing:start", (data) => {
    const { recipient_id } = data;
    const recipientSocketId = connectedUsers.get(recipient_id);
    if (recipientSocketId) {
      io.to(recipientSocketId).emit("typing:started", {
        user_id: user.id,
        username: user.username,
      });
    }
  });

  socket.on("typing:stop", (data) => {
    const { recipient_id } = data;
    const recipientSocketId = connectedUsers.get(recipient_id);
    if (recipientSocketId) {
      io.to(recipientSocketId).emit("typing:stopped", {
        user_id: user.id,
      });
    }
  });

  // Disconnect
  socket.on("disconnect", () => {
    console.log(`❌ User disconnected: ${user.username} (${socket.id})`);

    // Remove from connected users
    connectedUsers.delete(user.id);

    // Send offline status to followers
    socket.broadcast.emit("user:offline", {
      user_id: user.id,
      username: user.username,
      timestamp: new Date().toISOString(),
    });
  });
});

// Helper function to send notification via socket
export const sendSocketNotification = (
  recipientId: string,
  notification: any,
) => {
  const socketId = connectedUsers.get(recipientId);
  if (socketId) {
    io.to(socketId).emit("notification:new", notification);
  }
};

// Helper function to broadcast activity to followers
export const broadcastToFollowers = async (userId: string, activity: any) => {
  try {
    // Get followers
    const followersResult = await database.query(
      `SELECT follower_id FROM follows 
       WHERE following_id = $1 AND status = 'accepted'`,
      [userId],
    );

    // Broadcast to each follower
    followersResult.rows.forEach((follower: any) => {
      const socketId = connectedUsers.get(follower.follower_id);
      if (socketId) {
        io.to(socketId).emit("activity:new", activity);
      }
    });
  } catch (error) {
    console.error("Error broadcasting to followers:", error);
  }
};

// ========================== ****** middle of file ****** ========================== //

// Socket.io events for
io.on("connection", (socket) => {
  // Join product room for review updates
  socket.on("join:product:reviews", (productId) => {
    socket.join(`product:${productId}:reviews`);
  });

  // Leave product reviews room
  socket.on("leave:product:reviews", (productId) => {
    socket.leave(`product:${productId}:reviews`);
  });

  // New review notification
  socket.on("review:new", (data) => {
    const { product_id, review } = data;
    socket.to(`product:${product_id}:reviews`).emit("review:created", {
      review,
      timestamp: new Date().toISOString(),
    });
  });

  // Review moderation notification
  socket.on("review:moderated", (data) => {
    const { review_id, status, moderated_by } = data;
    // Notify review owner
    io.to(`user:${data.review_owner_id}`).emit("review:status_changed", {
      review_id,
      status,
      moderated_by,
      timestamp: new Date().toISOString(),
    });
  });

  // Join product room for update notifications
  socket.on("join:product:updates", (productId) => {
    socket.join(`product:${productId}:updates`);
  });

  // Leave product updates room
  socket.on("leave:product:updates", (productId) => {
    socket.leave(`product:${productId}:updates`);
  });

  // Subscribe to update notifications
  socket.on("subscribe:updates", (productIds) => {
    if (Array.isArray(productIds)) {
      productIds.forEach((productId) => {
        socket.join(`product:${productId}:updates`);
      });
    }
  });

  // Unsubscribe from update notifications
  socket.on("unsubscribe:updates", (productIds) => {
    if (Array.isArray(productIds)) {
      productIds.forEach((productId) => {
        socket.leave(`product:${productId}:updates`);
      });
    }
  });

  // Join suggestion room for updates
  socket.on("join:suggestion", (suggestionId) => {
    socket.join(`suggestion:${suggestionId}`);
  });

  // Leave suggestion room
  socket.on("leave:suggestion", (suggestionId) => {
    socket.leave(`suggestion:${suggestionId}`);
  });

  // Join product suggestions room
  socket.on("join:product:suggestions", (productId) => {
    socket.join(`product:${productId}:suggestions`);
  });

  // Leave product suggestions room
  socket.on("leave:product:suggestions", (productId) => {
    socket.leave(`product:${productId}:suggestions`);
  });

  // Team events
  socket.on("team:join_request", async (data) => {
    try {
      const { product_id, message } = data;

      // Emit to product admins
      const admins = await database.query(
        `SELECT creator_id FROM products_teams 
         WHERE product_id = $1 
           AND role IN ('founder', 'co-founder', 'admin')
           AND is_active = true`,
        [product_id],
      );

      admins.rows.forEach((admin: any) => {
        const adminSocketId = connectedUsers.get(admin.creator_id);
        if (adminSocketId) {
          io.to(adminSocketId).emit("team:join_request_received", {
            from_user_id: socket.data.user.id,
            from_username: socket.data.user.username,
            product_id,
            message,
            timestamp: new Date().toISOString(),
          });
        }
      });
    } catch (error) {
      console.error("Error handling join request:", error);
    }
  });
});

// Helper function to notify about new updates
export const notifyNewUpdate = (productId: string, update: any) => {
  io.to(`product:${productId}:updates`).emit("update:published", {
    update,
    product_id: productId,
    timestamp: new Date().toISOString(),
  });

  // Also notify product followers
  io.to(`product:${productId}:followers`).emit("update:new", {
    update_id: update.id,
    version: update.version,
    title: update.title,
    product_id: productId,
    timestamp: new Date().toISOString(),
  });
};

// Helper function to notify about new suggestions
export const notifyNewSuggestion = (productId: string, suggestion: any) => {
  io.to(`product:${productId}:suggestions`).emit("feature:suggested", {
    suggestion,
    product_id: productId,
    timestamp: new Date().toISOString(),
  });

  // Notify product owners/admins
  io.to(`product:${productId}:admins`).emit("feature:suggestion:new", {
    suggestion_id: suggestion.id,
    title: suggestion.title,
    suggestor_id: suggestion.suggestor_id,
    product_id: productId,
    timestamp: new Date().toISOString(),
  });
};

// Helper function to notify about votes
export const notifySuggestionVote = (suggestionId: string, data: any) => {
  io.to(`suggestion:${suggestionId}`).emit("feature:voted", data);
};

export { io, app, server };
