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

// Helper to determine follow type
const getFollowType = async (followerId: string, followingId: string): Promise<string> => {
  const [followerResult, followingResult] = await Promise.all([
    database.query(`SELECT account_type FROM accounts WHERE id = $1`, [followerId]),
    database.query(`SELECT account_type FROM accounts WHERE id = $1`, [followingId])
  ]);
  
  const followerType = followerResult.rows[0]?.account_type;
  const followingType = followingResult.rows[0]?.account_type;
  
  return `${followerType}_to_${followingType}`;
};

// controller: follow user/company
export const followAccount = catchAsyncError(async (req: Request, res: Response, next: NextFunction) => {
  const follower = (req as any).user;
  const { following_id, message } = req.body;
  
  if (!following_id) {
    return next(new ErrorHandler("Following account ID is required.", 400));
  }
  
  if (follower.id === following_id) {
    return next(new ErrorHandler("You cannot follow yourself.", 400));
  }
  
  // Check if following account exists
  const followingResult = await database.query(
    `SELECT * FROM accounts WHERE id = $1 AND is_active = true`,
    [following_id]
  );
  
  if (followingResult.rows.length === 0) {
    return next(new ErrorHandler("Account not found.", 404));
  }
  
  const followingAccount = followingResult.rows[0];
  
  // Check if already following
  const existingFollow = await database.query(
    `SELECT * FROM follows WHERE follower_id = $1 AND following_id = $2`,
    [follower.id, following_id]
  );
  
  if (existingFollow.rows.length > 0) {
    const follow = existingFollow.rows[0];
    
    if (follow.status === 'pending') {
      return next(new ErrorHandler("Follow request is already pending.", 400));
    } else if (follow.status === 'accepted') {
      return next(new ErrorHandler("You are already following this account.", 400));
    } else if (follow.status === 'blocked') {
      return next(new ErrorHandler("You cannot follow this account.", 403));
    }
  }
  
  // Determine follow type
  const followType = await getFollowType(follower.id, following_id);
  
  // Check privacy settings
  let status = 'pending';
  if (followingAccount.account_type === 'company' || followingAccount.privacy_settings?.profile_visibility === 'public') {
    // Companies and public profiles auto-accept follows
    status = 'accepted';
  }
  
  // Create follow
  const result = await database.query(
    `INSERT INTO follows (
      follower_id,
      following_id,
      follow_type,
      status,
      follow_request_message,
      created_at
    ) VALUES ($1, $2, $3, $4, $5, CURRENT_TIMESTAMP)
    RETURNING *`,
    [follower.id, following_id, followType, status, message || null]
  );
  
  const newFollow = result.rows[0];
  
  res.status(201).json({
    success: true,
    message: status === 'accepted' 
      ? "You are now following this account." 
      : "Follow request sent.",
    data: {
      follow: newFollow,
    },
  });
});

// controller: unfollow account
export const unfollowAccount = catchAsyncError(async (req: Request, res: Response, next: NextFunction) => {
  const follower = (req as any).user;
  const { following_id } = req.params;
  
  if (!following_id) {
    return next(new ErrorHandler("Following account ID is required.", 400));
  }
  
  // Check if follow exists
  const followResult = await database.query(
    `SELECT * FROM follows WHERE follower_id = $1 AND following_id = $2`,
    [follower.id, following_id]
  );
  
  if (followResult.rows.length === 0) {
    return next(new ErrorHandler("You are not following this account.", 404));
  }
  
  const follow = followResult.rows[0];
  
  // Delete the follow
  await database.query(
    `DELETE FROM follows WHERE id = $1`,
    [follow.id]
  );
  
  res.status(200).json({
    success: true,
    message: "Unfollowed successfully.",
  });
});

// controller: accept follow request
export const acceptFollowRequest = catchAsyncError(async (req: Request, res: Response, next: NextFunction) => {
  const account = (req as any).user;
  const { follow_id } = req.params;
  
  // Check if follow request exists
  const followResult = await database.query(
    `SELECT * FROM follows 
     WHERE id = $1 AND following_id = $2 AND status = 'pending'`,
    [follow_id, account.id]
  );
  
  if (followResult.rows.length === 0) {
    return next(new ErrorHandler("Follow request not found.", 404));
  }
  
  const follow = followResult.rows[0];
  
  // Update follow status
  const result = await database.query(
    `UPDATE follows 
     SET status = 'accepted', accepted_at = CURRENT_TIMESTAMP, updated_at = CURRENT_TIMESTAMP
     WHERE id = $1
     RETURNING *`,
    [follow_id]
  );
  
  const updatedFollow = result.rows[0];
  
  res.status(200).json({
    success: true,
    message: "Follow request accepted.",
    data: {
      follow: updatedFollow,
    },
  });
});

// controller: reject follow request
export const rejectFollowRequest = catchAsyncError(async (req: Request, res: Response, next: NextFunction) => {
  const account = (req as any).user;
  const { follow_id } = req.params;
  const { reason } = req.body;
  
  // Check if follow request exists
  const followResult = await database.query(
    `SELECT * FROM follows 
     WHERE id = $1 AND following_id = $2 AND status = 'pending'`,
    [follow_id, account.id]
  );
  
  if (followResult.rows.length === 0) {
    return next(new ErrorHandler("Follow request not found.", 404));
  }
  
  const follow = followResult.rows[0];
  
  // Update follow status
  const result = await database.query(
    `UPDATE follows 
     SET status = 'rejected', updated_at = CURRENT_TIMESTAMP
     WHERE id = $1
     RETURNING *`,
    [follow_id]
  );
  
  const updatedFollow = result.rows[0];
  
  res.status(200).json({
    success: true,
    message: "Follow request rejected.",
    data: {
      follow: updatedFollow,
    },
  });
});

// controller: block follower
export const blockFollower = catchAsyncError(async (req: Request, res: Response, next: NextFunction) => {
  const account = (req as any).user;
  const { follower_id } = req.params;
  const { reason } = req.body;
  
  if (!follower_id) {
    return next(new ErrorHandler("Follower ID is required.", 400));
  }
  
  if (account.id === follower_id) {
    return next(new ErrorHandler("You cannot block yourself.", 400));
  }
  
  // Check if follow relationship exists
  const followResult = await database.query(
    `SELECT * FROM follows 
     WHERE following_id = $1 AND follower_id = $2`,
    [account.id, follower_id]
  );
  
  let follow;
  
  if (followResult.rows.length > 0) {
    // Update existing follow
    const result = await database.query(
      `UPDATE follows 
       SET status = 'blocked', updated_at = CURRENT_TIMESTAMP
       WHERE following_id = $1 AND follower_id = $2
       RETURNING *`,
      [account.id, follower_id]
    );
    follow = result.rows[0];
  } else {
    // Create new block record
    const followType = await getFollowType(follower_id, account.id);
    
    const result = await database.query(
      `INSERT INTO follows (
        follower_id,
        following_id,
        follow_type,
        status,
        created_at
      ) VALUES ($1, $2, $3, 'blocked', CURRENT_TIMESTAMP)
      RETURNING *`,
      [follower_id, account.id, followType]
    );
    follow = result.rows[0];
  }
  
  res.status(200).json({
    success: true,
    message: "User blocked successfully.",
    data: {
      follow,
    },
  });
});

// controller: unblock follower
export const unblockFollower = catchAsyncError(async (req: Request, res: Response, next: NextFunction) => {
  const account = (req as any).user;
  const { follower_id } = req.params;
  
  // Check if block exists
  const followResult = await database.query(
    `SELECT * FROM follows 
     WHERE following_id = $1 AND follower_id = $2 AND status = 'blocked'`,
    [account.id, follower_id]
  );
  
  if (followResult.rows.length === 0) {
    return next(new ErrorHandler("User is not blocked.", 404));
  }
  
  const follow = followResult.rows[0];
  
  // Delete the block record
  await database.query(
    `DELETE FROM follows WHERE id = $1`,
    [follow.id]
  );
  
  res.status(200).json({
    success: true,
    message: "User unblocked successfully.",
  });
});

// controller: get followers list
export const getFollowers = catchAsyncError(async (req: Request, res: Response, next: NextFunction) => {
  const { account_id } = req.params;
  const { status = 'accepted', page = 1, limit = 20 } = req.query;
  
  const offset = (Number(page) - 1) * Number(limit);
  
  // Validate account exists
  const accountResult = await database.query(
    `SELECT * FROM accounts WHERE id = $1 AND is_active = true`,
    [account_id]
  );
  
  if (accountResult.rows.length === 0) {
    return next(new ErrorHandler("Account not found.", 404));
  }
  
  const account = accountResult.rows[0];
  
  // Check privacy
  if (!account.is_public && (req as any).user?.id !== account_id) {
    return next(new ErrorHandler("This account is private.", 403));
  }
  
  // Get followers count
  const countResult = await database.query(
    `SELECT COUNT(*) as total FROM follows 
     WHERE following_id = $1 AND status = $2`,
    [account_id, status]
  );
  
  const total = parseInt(countResult.rows[0]?.total || 0);
  
  // Get followers with account details
  const followersResult = await database.query(
    `SELECT 
      f.*,
      a.username,
      a.display_name,
      a.avatar,
      a.account_type,
      a.bio,
      a.profile_completion_percentage
    FROM follows f
    INNER JOIN accounts a ON f.follower_id = a.id
    WHERE f.following_id = $1 AND f.status = $2
    ORDER BY f.created_at DESC
    LIMIT $3 OFFSET $4`,
    [account_id, status, limit, offset]
  );
  
  const followers = followersResult.rows;
  
  res.status(200).json({
    success: true,
    data: {
      followers,
      pagination: {
        page: Number(page),
        limit: Number(limit),
        total,
        pages: Math.ceil(total / Number(limit)),
      },
      account: {
        id: account.id,
        username: account.username,
        display_name: account.display_name,
        account_type: account.account_type,
      },
    },
  });
});

// controller: get following list
export const getFollowing = catchAsyncError(async (req: Request, res: Response, next: NextFunction) => {
  const { account_id } = req.params;
  const { status = 'accepted', page = 1, limit = 20 } = req.query;
  
  const offset = (Number(page) - 1) * Number(limit);
  
  // Validate account exists
  const accountResult = await database.query(
    `SELECT * FROM accounts WHERE id = $1 AND is_active = true`,
    [account_id]
  );
  
  if (accountResult.rows.length === 0) {
    return next(new ErrorHandler("Account not found.", 404));
  }
  
  const account = accountResult.rows[0];
  
  // Check privacy
  if (!account.is_public && (req as any).user?.id !== account_id) {
    return next(new ErrorHandler("This account is private.", 403));
  }
  
  // Get following count
  const countResult = await database.query(
    `SELECT COUNT(*) as total FROM follows 
     WHERE follower_id = $1 AND status = $2`,
    [account_id, status]
  );
  
  const total = parseInt(countResult.rows[0]?.total || 0);
  
  // Get following with account details
  const followingResult = await database.query(
    `SELECT 
      f.*,
      a.username,
      a.display_name,
      a.avatar,
      a.account_type,
      a.bio,
      a.profile_completion_percentage
    FROM follows f
    INNER JOIN accounts a ON f.following_id = a.id
    WHERE f.follower_id = $1 AND f.status = $2
    ORDER BY f.created_at DESC
    LIMIT $3 OFFSET $4`,
    [account_id, status, limit, offset]
  );
  
  const following = followingResult.rows;
  
  res.status(200).json({
    success: true,
    data: {
      following,
      pagination: {
        page: Number(page),
        limit: Number(limit),
        total,
        pages: Math.ceil(total / Number(limit)),
      },
      account: {
        id: account.id,
        username: account.username,
        display_name: account.display_name,
        account_type: account.account_type,
      },
    },
  });
});

// controller: get follow statistics
export const getFollowStats = catchAsyncError(async (req: Request, res: Response, next: NextFunction) => {
  const { account_id } = req.params;
  
  // Validate account exists
  const accountResult = await database.query(
    `SELECT * FROM accounts WHERE id = $1 AND is_active = true`,
    [account_id]
  );
  
  if (accountResult.rows.length === 0) {
    return next(new ErrorHandler("Account not found.", 404));
  }
  
  const account = accountResult.rows[0];
  
  // Check privacy
  if (!account.is_public && (req as any).user?.id !== account_id) {
    return next(new ErrorHandler("This account is private.", 403));
  }
  
  // Get stats using database function
  const statsResult = await database.query(
    `SELECT * FROM get_follow_stats($1)`,
    [account_id]
  );
  
  const stats = statsResult.rows[0]?.get_follow_stats || {};
  
  res.status(200).json({
    success: true,
    data: {
      stats,
      account: {
        id: account.id,
        username: account.username,
        display_name: account.display_name,
        account_type: account.account_type,
      },
    },
  });
});

// controller: check follow relationship
export const checkFollowRelationship = catchAsyncError(async (req: Request, res: Response, next: NextFunction) => {
  const account = (req as any).user;
  const { target_id } = req.params;
  
  if (!target_id) {
    return next(new ErrorHandler("Target account ID is required.", 400));
  }
  
  // Check if target exists
  const targetResult = await database.query(
    `SELECT * FROM accounts WHERE id = $1 AND is_active = true`,
    [target_id]
  );
  
  if (targetResult.rows.length === 0) {
    return next(new ErrorHandler("Target account not found.", 404));
  }
  
  // Check relationship using database function
  const relationshipResult = await database.query(
    `SELECT * FROM check_follow_relationship($1, $2)`,
    [account.id, target_id]
  );
  
  const relationship = relationshipResult.rows[0]?.check_follow_relationship || {};
  
  // Also check if target is following the user
  const reverseResult = await database.query(
    `SELECT * FROM check_follow_relationship($1, $2)`,
    [target_id, account.id]
  );
  
  const reverseRelationship = reverseResult.rows[0]?.check_follow_relationship || {};
  
  res.status(200).json({
    success: true,
    data: {
      relationship: {
        ...relationship,
        is_followed_by: reverseRelationship.is_following === true,
        is_mutual: relationship.is_following === true && reverseRelationship.is_following === true,
      },
      accounts: {
        current_user: {
          id: account.id,
          username: account.username,
        },
        target: {
          id: target_id,
          username: targetResult.rows[0].username,
        },
      },
    },
  });
});

// controller: get pending follow requests
export const getPendingFollowRequests = catchAsyncError(async (req: Request, res: Response, next: NextFunction) => {
  const account = (req as any).user;
  const { page = 1, limit = 20 } = req.query;
  
  const offset = (Number(page) - 1) * Number(limit);
  
  // Get count
  const countResult = await database.query(
    `SELECT COUNT(*) as total FROM follows 
     WHERE following_id = $1 AND status = 'pending'`,
    [account.id]
  );
  
  const total = parseInt(countResult.rows[0]?.total || 0);
  
  // Get pending requests
  const requestsResult = await database.query(
    `SELECT 
      f.*,
      a.username,
      a.display_name,
      a.avatar,
      a.account_type,
      a.bio,
      a.profile_completion_percentage
    FROM follows f
    INNER JOIN accounts a ON f.follower_id = a.id
    WHERE f.following_id = $1 AND f.status = 'pending'
    ORDER BY f.created_at DESC
    LIMIT $2 OFFSET $3`,
    [account.id, limit, offset]
  );
  
  const requests = requestsResult.rows;
  
  res.status(200).json({
    success: true,
    data: {
      requests,
      pagination: {
        page: Number(page),
        limit: Number(limit),
        total,
        pages: Math.ceil(total / Number(limit)),
      },
    },
  });
});