import { Request, Response, NextFunction } from "express";
import { catchAsyncError } from "../../middlewares/catchAsyncError";
import ErrorHandler from "../../middlewares/error";
import database from "../../config/db";

// Helper to format notification response
const formatNotification = (notification: any) => {
  const { 
    delivery_status, 
    delivery_method, 
    scheduled_for, 
    expires_at, 
    ...formatted 
  } = notification;
  return formatted;
};

// controller: get notifications
export const getNotifications = catchAsyncError(async (req: Request, res: Response, next: NextFunction) => {
  const account = (req as any).user;
  const { 
    type, 
    is_read, 
    priority,
    page = 1, 
    limit = 20 
  } = req.query;
  
  const offset = (Number(page) - 1) * Number(limit);
  
  // Build where conditions
  const whereConditions: string[] = ["recipient_id = $1"];
  const values: any[] = [account.id];
  let paramCounter = 2;
  
  if (type) {
    whereConditions.push(`type = $${paramCounter}`);
    values.push(type);
    paramCounter++;
  }
  
  if (is_read !== undefined) {
    whereConditions.push(`is_read = $${paramCounter}`);
    values.push(is_read === 'true');
    paramCounter++;
  }
  
  if (priority) {
    whereConditions.push(`priority = $${paramCounter}`);
    values.push(priority);
    paramCounter++;
  }
  
  // Add conditions for scheduled and expired notifications
  whereConditions.push(`(scheduled_for IS NULL OR scheduled_for <= CURRENT_TIMESTAMP)`);
  whereConditions.push(`(expires_at IS NULL OR expires_at > CURRENT_TIMESTAMP)`);
  
  // Get count
  const countQuery = `
    SELECT COUNT(*) as total 
    FROM notifications 
    WHERE ${whereConditions.join(' AND ')}
  `;
  
  const countResult = await database.query(countQuery, values);
  const total = parseInt(countResult.rows[0]?.total || 0);
  
  // Get notifications with sender details
  const notificationsQuery = `
    SELECT 
      n.*,
      s.username as sender_username,
      s.display_name as sender_display_name,
      s.avatar as sender_avatar,
      s.account_type as sender_account_type
    FROM notifications n
    LEFT JOIN accounts s ON n.sender_id = s.id
    WHERE ${whereConditions.join(' AND ')}
    ORDER BY 
      CASE priority
        WHEN 'urgent' THEN 1
        WHEN 'high' THEN 2
        WHEN 'normal' THEN 3
        WHEN 'low' THEN 4
      END,
      n.created_at DESC
    LIMIT $${paramCounter} OFFSET $${paramCounter + 1}
  `;
  
  values.push(Number(limit));
  values.push(offset);
  
  const notificationsResult = await database.query(notificationsQuery, values);
  
  const notifications = notificationsResult.rows.map(formatNotification);
  
  // Get unread count
  const unreadResult = await database.query(
    `SELECT get_unread_notification_count($1) as unread_count`,
    [account.id]
  );
  
  const unreadCount = parseInt(unreadResult.rows[0]?.unread_count || 0);
  
  res.status(200).json({
    success: true,
    data: {
      notifications,
      meta: {
        unread_count: unreadCount,
        total,
      },
      pagination: {
        page: Number(page),
        limit: Number(limit),
        total,
        pages: Math.ceil(total / Number(limit)),
      },
    },
  });
});

// controller: mark notification as read
export const markNotificationAsRead = catchAsyncError(async (req: Request, res: Response, next: NextFunction) => {
  const account = (req as any).user;
  const { notification_id } = req.params;
  
  // Check if notification exists and belongs to user
  const notificationResult = await database.query(
    `SELECT * FROM notifications 
     WHERE id = $1 AND recipient_id = $2`,
    [notification_id, account.id]
  );
  
  if (notificationResult.rows.length === 0) {
    return next(new ErrorHandler("Notification not found.", 404));
  }
  
  const notification = notificationResult.rows[0];
  
  if (notification.is_read) {
    return res.status(200).json({
      success: true,
      message: "Notification is already read.",
      data: {
        notification: formatNotification(notification),
      },
    });
  }
  
  // Mark as read
  const result = await database.query(
    `UPDATE notifications 
     SET is_read = true, read_at = CURRENT_TIMESTAMP, updated_at = CURRENT_TIMESTAMP
     WHERE id = $1
     RETURNING *`,
    [notification_id]
  );
  
  const updatedNotification = result.rows[0];
  
  res.status(200).json({
    success: true,
    message: "Notification marked as read.",
    data: {
      notification: formatNotification(updatedNotification),
    },
  });
});

// controller: mark all notifications as read
export const markAllNotificationsAsRead = catchAsyncError(async (req: Request, res: Response, next: NextFunction) => {
  const account = (req as any).user;
  
  // Mark all as read
  await database.query(
    `UPDATE notifications 
     SET is_read = true, read_at = CURRENT_TIMESTAMP, updated_at = CURRENT_TIMESTAMP
     WHERE recipient_id = $1 AND is_read = false
     AND (scheduled_for IS NULL OR scheduled_for <= CURRENT_TIMESTAMP)
     AND (expires_at IS NULL OR expires_at > CURRENT_TIMESTAMP)`,
    [account.id]
  );
  
  // Get updated unread count
  const unreadResult = await database.query(
    `SELECT get_unread_notification_count($1) as unread_count`,
    [account.id]
  );
  
  const unreadCount = parseInt(unreadResult.rows[0]?.unread_count || 0);
  
  res.status(200).json({
    success: true,
    message: "All notifications marked as read.",
    data: {
      unread_count: unreadCount,
    },
  });
});

// controller: delete notification
export const deleteNotification = catchAsyncError(async (req: Request, res: Response, next: NextFunction) => {
  const account = (req as any).user;
  const { notification_id } = req.params;
  
  // Check if notification exists and belongs to user
  const notificationResult = await database.query(
    `SELECT * FROM notifications 
     WHERE id = $1 AND recipient_id = $2`,
    [notification_id, account.id]
  );
  
  if (notificationResult.rows.length === 0) {
    return next(new ErrorHandler("Notification not found.", 404));
  }
  
  // Delete notification
  await database.query(
    `DELETE FROM notifications WHERE id = $1`,
    [notification_id]
  );
  
  // Get updated unread count
  const unreadResult = await database.query(
    `SELECT get_unread_notification_count($1) as unread_count`,
    [account.id]
  );
  
  const unreadCount = parseInt(unreadResult.rows[0]?.unread_count || 0);
  
  res.status(200).json({
    success: true,
    message: "Notification deleted successfully.",
    data: {
      unread_count: unreadCount,
    },
  });
});

// controller: clear all notifications
export const clearAllNotifications = catchAsyncError(async (req: Request, res: Response, next: NextFunction) => {
  const account = (req as any).user;
  
  // Delete all notifications for user
  await database.query(
    `DELETE FROM notifications WHERE recipient_id = $1`,
    [account.id]
  );
  
  res.status(200).json({
    success: true,
    message: "All notifications cleared.",
  });
});

// controller: update notification settings
export const updateNotificationSettings = catchAsyncError(async (req: Request, res: Response, next: NextFunction) => {
  const account = (req as any).user;
  const { 
    follow_id, 
    receive_notifications, 
    notifications_settings 
  } = req.body;
  
  if (!follow_id) {
    return next(new ErrorHandler("Follow ID is required.", 400));
  }
  
  // Check if follow exists and user is the follower
  const followResult = await database.query(
    `SELECT * FROM follows 
     WHERE id = $1 AND follower_id = $2`,
    [follow_id, account.id]
  );
  
  if (followResult.rows.length === 0) {
    return next(new ErrorHandler("Follow relationship not found.", 404));
  }
  
  const updates: any = {};
  if (receive_notifications !== undefined) updates.receive_notifications = receive_notifications;
  if (notifications_settings !== undefined) updates.notifications_settings = notifications_settings;
  
  if (Object.keys(updates).length === 0) {
    return next(new ErrorHandler("No settings provided to update.", 400));
  }
  
  // Prepare update query
  const setClauses: string[] = [];
  const values: any[] = [];
  let paramCounter = 1;
  
  Object.entries(updates).forEach(([key, value]) => {
    setClauses.push(`${key} = $${paramCounter}`);
    
    if (typeof value === 'object') {
      values.push(JSON.stringify(value));
    } else {
      values.push(value);
    }
    
    paramCounter++;
  });
  
  setClauses.push(`updated_at = CURRENT_TIMESTAMP`);
  
  const query = `
    UPDATE follows 
    SET ${setClauses.join(', ')}
    WHERE id = $${paramCounter}
    RETURNING *
  `;
  
  values.push(follow_id);
  
  const result = await database.query(query, values);
  
  const updatedFollow = result.rows[0];
  
  res.status(200).json({
    success: true,
    message: "Notification settings updated.",
    data: {
      follow: updatedFollow,
    },
  });
});

// controller: get notification preferences
export const getNotificationPreferences = catchAsyncError(async (req: Request, res: Response, next: NextFunction) => {
  const account = (req as any).user;
  
  // Get user's email notification preferences from accounts table
  const accountResult = await database.query(
    `SELECT email_notifications, push_notifications FROM accounts WHERE id = $1`,
    [account.id]
  );
  
  const emailPreferences = accountResult.rows[0]?.email_notifications || {};
  const pushPreferences = accountResult.rows[0]?.push_notifications || {};
  
  // Get follow-specific notification settings
  const followSettingsResult = await database.query(
    `SELECT 
      f.id as follow_id,
      f.following_id,
      f.receive_notifications,
      f.notifications_settings,
      a.username as following_username,
      a.display_name as following_display_name,
      a.account_type as following_account_type
    FROM follows f
    INNER JOIN accounts a ON f.following_id = a.id
    WHERE f.follower_id = $1 AND f.status = 'accepted'
    ORDER BY a.display_name`,
    [account.id]
  );
  
  const followSettings = followSettingsResult.rows;
  
  res.status(200).json({
    success: true,
    data: {
      email_notifications: emailPreferences,
      push_notifications: pushPreferences,
      follow_notifications: followSettings,
    },
  });
});

// controller: create test notification
export const createTestNotification = catchAsyncError(async (req: Request, res: Response, next: NextFunction) => {
  const account = (req as any).user;
  const { type, title, message } = req.body;
  
  const notificationTypes = [
    'follow_request',
    'follow_accepted',
    'follow_rejected',
    'new_message',
    'new_comment',
    'post_like',
    'post_share',
    'company_update',
    'product_launch',
    'achievement_unlocked',
    'system_alert',
    'security_alert'
  ];
  
  const validType = type || 'system_alert';
  
  if (!notificationTypes.includes(validType)) {
    return next(new ErrorHandler("Invalid notification type.", 400));
  }
  
  // Create test notification
  const result = await database.query(
    `INSERT INTO notifications (
      recipient_id,
      type,
      title,
      message,
      metadata,
      priority,
      action_url,
      action_label
    ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
    RETURNING *`,
    [
      account.id,
      validType,
      title || 'Test Notification',
      message || 'This is a test notification to verify the system is working.',
      JSON.stringify({ test: true, timestamp: new Date().toISOString() }),
      'normal',
      '/dashboard',
      'Go to Dashboard'
    ]
  );
  
  const notification = result.rows[0];
  
  res.status(201).json({
    success: true,
    message: "Test notification created.",
    data: {
      notification: formatNotification(notification),
    },
  });
});