// controllers/productAlternativeController.ts
import { Request, Response, NextFunction } from "express";
import {
  AlternativeCreateData,
  AlternativeFilters,
  AlternativeUpdateData,
  productAlternativeService,
} from "./product.alternative.service";
import { catchAsyncError } from "../../middlewares/catchAsyncError";
import ErrorHandler from "../../middlewares/error";
import { AuthRequest } from "../../middlewares/auth";
import database from "../../config/db";

export const productAlternativeController = {
  // Create new alternative relationship
  createAlternative: catchAsyncError(
    async (req: AuthRequest, res: Response, next: NextFunction) => {
      const userId = req.user?.id;

      if (!userId) {
        return next(new ErrorHandler("Authentication required", 401));
      }

      const data: AlternativeCreateData = {
        ...req.body,
        creator_id: userId,
        relationship_type: "manual",
      };

      // Validation
      if (!data.product_id) {
        return next(new ErrorHandler("Product ID is required", 400));
      }

      if (!data.alternative_product_id) {
        return next(
          new ErrorHandler("Alternative product ID is required", 400),
        );
      }

      if (data.product_id === data.alternative_product_id) {
        return next(
          new ErrorHandler("A product cannot be an alternative to itself", 400),
        );
      }

      // Validate relationship type
      const validTypes = [
        "auto-suggested",
        "manual",
        "competitor",
        "similar",
        "better-alternative",
        "cheaper-alternative",
      ];
      if (
        data.relationship_type &&
        !validTypes.includes(data.relationship_type)
      ) {
        return next(
          new ErrorHandler(
            `Invalid relationship type. Must be one of: ${validTypes.join(", ")}`,
            400,
          ),
        );
      }

      try {
        const alternative =
          await productAlternativeService.createAlternative(data);

        // Emit socket event
        if (req.app.get("io")) {
          const io = req.app.get("io");
          io.to(`product:${data.product_id}`).emit("alternative:added", {
            alternative,
            product_id: data.product_id,
          });
        }

        // Log activity
        try {
          await database.query(
            `INSERT INTO activities (
            account_id, activity_type, entity_type, entity_id, 
            details, is_public
          ) VALUES ($1, $2, $3, $4, $5, $6)`,
            [
              userId,
              "product_alternative_added",
              "product_alternative",
              alternative.id,
              JSON.stringify({
                product_id: data.product_id,
                alternative_product_id: data.alternative_product_id,
                relationship_type: data.relationship_type,
              }),
              true,
            ],
          );
        } catch (activityError) {
          console.error("Failed to log activity:", activityError);
        }

        res.status(201).json({
          success: true,
          message: "Alternative added successfully",
          data: alternative,
        });
      } catch (error: any) {
        return next(new ErrorHandler(error.message, 400));
      }
    },
  ),

  // Get alternative by ID
  getAlternativeById: catchAsyncError(
    async (req: Request, res: Response, next: NextFunction) => {
      const { id } = req.params;

      const alternative =
        await productAlternativeService.getAlternativeById(id);

      if (!alternative) {
        return next(new ErrorHandler("Alternative not found", 404));
      }

      res.status(200).json({
        success: true,
        data: alternative,
      });
    },
  ),

  // Get all alternatives
  getAllAlternatives: catchAsyncError(
    async (req: Request, res: Response, next: NextFunction) => {
      const {
        page = 1,
        limit = 20,
        product_id,
        alternative_product_id,
        creator_id,
        relationship_type,
        status = "active",
        is_user_confirmed,
        is_user_rejected,
        is_featured,
        is_sponsored,
        min_match_score,
        price_comparison,
        sort_by = "match_score",
        order = "desc",
      } = req.query;

      const filters: AlternativeFilters = {
        page: parseInt(page as string),
        limit: parseInt(limit as string),
        product_id: product_id as string,
        alternative_product_id: alternative_product_id as string,
        creator_id: creator_id as string,
        relationship_type: relationship_type as string,
        status: status as string,
        is_user_confirmed: is_user_confirmed
          ? is_user_confirmed === "true"
          : undefined,
        is_user_rejected: is_user_rejected
          ? is_user_rejected === "true"
          : undefined,
        is_featured: is_featured ? is_featured === "true" : undefined,
        is_sponsored: is_sponsored ? is_sponsored === "true" : undefined,
        min_match_score: min_match_score
          ? parseFloat(min_match_score as string)
          : undefined,
        price_comparison: price_comparison as string,
        sort_by: sort_by as string,
        order: order as "asc" | "desc",
      };

      const result =
        await productAlternativeService.getAllAlternatives(filters);

      res.status(200).json({
        success: true,
        ...result,
      });
    },
  ),

  // Update alternative
  updateAlternative: catchAsyncError(
    async (req: AuthRequest, res: Response, next: NextFunction) => {
      const { id } = req.params;
      const userId = req.user?.id;
      const data: AlternativeUpdateData = req.body;

      if (!userId) {
        return next(new ErrorHandler("Authentication required", 401));
      }

      // Validate if trying to update sensitive fields
      if (
        data.relationship_type ||
        data.creator_id ||
        data.product_id ||
        data.alternative_product_id
      ) {
        return next(
          new ErrorHandler(
            "Cannot update relationship type, creator, or product references",
            400,
          ),
        );
      }

      try {
        const updatedAlternative =
          await productAlternativeService.updateAlternative(id, data, userId);

        // Emit socket event
        if (req.app.get("io")) {
          const io = req.app.get("io");
          io.to(`product:${updatedAlternative.product_id}`).emit(
            "alternative:updated",
            {
              alternative: updatedAlternative,
              product_id: updatedAlternative.product_id,
            },
          );
        }

        res.status(200).json({
          success: true,
          message: "Alternative updated successfully",
          data: updatedAlternative,
        });
      } catch (error: any) {
        return next(
          new ErrorHandler(
            error.message,
            error.message.includes("permission") ? 403 : 400,
          ),
        );
      }
    },
  ),

  // Delete alternative
  deleteAlternative: catchAsyncError(
    async (req: AuthRequest, res: Response, next: NextFunction) => {
      const { id } = req.params;
      const userId = req.user?.id;

      if (!userId) {
        return next(new ErrorHandler("Authentication required", 401));
      }

      try {
        // Get alternative info before deletion for socket event
        const alternative =
          await productAlternativeService.getAlternativeById(id);

        const deletedAlternative =
          await productAlternativeService.deleteAlternative(id, userId);

        // Emit socket event
        if (req.app.get("io")) {
          const io = req.app.get("io");
          io.to(`product:${alternative.product_id}`).emit(
            "alternative:removed",
            {
              alternative_id: id,
              product_id: alternative.product_id,
            },
          );
        }

        res.status(200).json({
          success: true,
          message: "Alternative deleted successfully",
          data: deletedAlternative,
        });
      } catch (error: any) {
        return next(
          new ErrorHandler(
            error.message,
            error.message.includes("permission") ? 403 : 400,
          ),
        );
      }
    },
  ),

  // Vote on alternative
  voteOnAlternative: catchAsyncError(
    async (req: AuthRequest, res: Response, next: NextFunction) => {
      const { id } = req.params;
      const userId = req.user?.id;
      const { vote_type } = req.body;

      if (!userId) {
        return next(new ErrorHandler("Authentication required", 401));
      }

      if (!vote_type || !["upvote", "downvote"].includes(vote_type)) {
        return next(
          new ErrorHandler(
            "Invalid vote type. Must be 'upvote' or 'downvote'",
            400,
          ),
        );
      }

      try {
        const result = await productAlternativeService.voteOnAlternative(
          id,
          userId,
          vote_type as "upvote" | "downvote",
        );

        res.status(200).json({
          success: true,
          message: `Alternative ${vote_type}d successfully`,
          data: {
            upvotes: result.upvotes,
            downvotes: result.downvotes,
          },
        });
      } catch (error: any) {
        return next(new ErrorHandler(error.message, 400));
      }
    },
  ),

  // Confirm alternative
  confirmAlternative: catchAsyncError(
    async (req: AuthRequest, res: Response, next: NextFunction) => {
      const { id } = req.params;
      const userId = req.user?.id;

      if (!userId) {
        return next(new ErrorHandler("Authentication required", 401));
      }

      try {
        const alternative = await productAlternativeService.confirmAlternative(
          id,
          userId,
        );

        res.status(200).json({
          success: true,
          message: "Alternative confirmed successfully",
          data: alternative,
        });
      } catch (error: any) {
        return next(new ErrorHandler(error.message, 400));
      }
    },
  ),

  // Reject alternative
  rejectAlternative: catchAsyncError(
    async (req: AuthRequest, res: Response, next: NextFunction) => {
      const { id } = req.params;
      const userId = req.user?.id;

      if (!userId) {
        return next(new ErrorHandler("Authentication required", 401));
      }

      try {
        const alternative = await productAlternativeService.rejectAlternative(
          id,
          userId,
        );

        res.status(200).json({
          success: true,
          message: "Alternative rejected successfully",
          data: alternative,
        });
      } catch (error: any) {
        return next(new ErrorHandler(error.message, 400));
      }
    },
  ),

  // Get alternative statistics
  getAlternativeStats: catchAsyncError(
    async (req: Request, res: Response, next: NextFunction) => {
      const { productId } = req.params;

      if (!productId) {
        return next(new ErrorHandler("Product ID is required", 400));
      }

      const stats =
        await productAlternativeService.getAlternativeStats(productId);

      res.status(200).json({
        success: true,
        data: stats,
      });
    },
  ),

  // Auto-suggest alternatives for a product
  autoSuggestAlternatives: catchAsyncError(
    async (req: AuthRequest, res: Response, next: NextFunction) => {
      const { productId } = req.params;
      const userId = req.user?.id;

      if (!userId) {
        return next(new ErrorHandler("Authentication required", 401));
      }

      try {
        const result =
          await productAlternativeService.autoSuggestAlternatives(productId);

        res.status(200).json({
          success: true,
          message: `${result.suggested_count} alternatives suggested`,
          data: result,
        });
      } catch (error: any) {
        return next(new ErrorHandler(error.message, 400));
      }
    },
  ),

  // Get alternatives for user view
  getAlternativesForUser: catchAsyncError(
    async (req: AuthRequest, res: Response, next: NextFunction) => {
      const { productId } = req.params;
      const userId = req.user?.id;

      const alternatives =
        await productAlternativeService.getAlternativesForUser(
          productId,
          userId,
        );

      res.status(200).json({
        success: true,
        data: alternatives,
      });
    },
  ),

  // Add manual alternative (using database function)
  addManualAlternative: catchAsyncError(
    async (req: AuthRequest, res: Response, next: NextFunction) => {
      const userId = req.user?.id;

      if (!userId) {
        return next(new ErrorHandler("Authentication required", 401));
      }

      const data: AlternativeCreateData = {
        ...req.body,
        creator_id: userId,
      };

      // Validation
      if (!data.product_id) {
        return next(new ErrorHandler("Product ID is required", 400));
      }

      if (!data.alternative_product_id) {
        return next(
          new ErrorHandler("Alternative product ID is required", 400),
        );
      }

      if (data.product_id === data.alternative_product_id) {
        return next(
          new ErrorHandler("A product cannot be an alternative to itself", 400),
        );
      }

      try {
        const result =
          await productAlternativeService.addManualAlternative(data);

        res.status(201).json({
          success: true,
          message: "Manual alternative added successfully",
          data: result,
        });
      } catch (error: any) {
        return next(new ErrorHandler(error.message, 400));
      }
    },
  ),

  // Reorder alternatives
  reorderAlternatives: catchAsyncError(
    async (req: AuthRequest, res: Response, next: NextFunction) => {
      const { productId } = req.params;
      const userId = req.user?.id;
      const { alternative_ids } = req.body;

      if (!userId) {
        return next(new ErrorHandler("Authentication required", 401));
      }

      if (!alternative_ids || !Array.isArray(alternative_ids)) {
        return next(new ErrorHandler("Alternative IDs array is required", 400));
      }

      try {
        const updates = await productAlternativeService.reorderAlternatives(
          productId,
          alternative_ids,
        );

        res.status(200).json({
          success: true,
          message: "Alternatives reordered successfully",
          data: updates,
        });
      } catch (error: any) {
        return next(new ErrorHandler(error.message, 400));
      }
    },
  ),

  // Get featured alternatives
  getFeaturedAlternatives: catchAsyncError(
    async (req: Request, res: Response, next: NextFunction) => {
      const { productId } = req.params;
      const limit = parseInt(req.query.limit as string) || 3;

      const alternatives =
        await productAlternativeService.getFeaturedAlternatives(
          productId,
          limit,
        );

      res.status(200).json({
        success: true,
        data: alternatives,
      });
    },
  ),

  // Get alternatives by price comparison
  getAlternativesByPrice: catchAsyncError(
    async (req: Request, res: Response, next: NextFunction) => {
      const { productId, priceComparison } = req.params;

      if (!priceComparison) {
        return next(
          new ErrorHandler("Price comparison parameter is required", 400),
        );
      }

      const alternatives =
        await productAlternativeService.getAlternativesByPrice(
          productId,
          priceComparison,
        );

      res.status(200).json({
        success: true,
        data: alternatives,
      });
    },
  ),

  // Get comparison between two products
  getProductComparison: catchAsyncError(
    async (req: Request, res: Response, next: NextFunction) => {
      const { productId, alternativeProductId } = req.params;

      if (!productId || !alternativeProductId) {
        return next(new ErrorHandler("Both product IDs are required", 400));
      }

      if (productId === alternativeProductId) {
        return next(
          new ErrorHandler("Cannot compare a product with itself", 400),
        );
      }

      const comparison = await productAlternativeService.getProductComparison(
        productId,
        alternativeProductId,
      );

      res.status(200).json({
        success: true,
        data: comparison,
      });
    },
  ),

  // Bulk update alternatives (admin only)
  bulkUpdateAlternatives: catchAsyncError(
    async (req: AuthRequest, res: Response, next: NextFunction) => {
      const userId = req.user?.id;
      const { alternative_ids, update_data } = req.body;

      if (!userId) {
        return next(new ErrorHandler("Authentication required", 401));
      }

      if (!alternative_ids || !Array.isArray(alternative_ids)) {
        return next(new ErrorHandler("Alternative IDs array is required", 400));
      }

      if (!update_data || typeof update_data !== "object") {
        return next(new ErrorHandler("Update data object is required", 400));
      }

      // Check admin permissions
      const adminCheck = await database.query(
        `SELECT EXISTS(
        SELECT 1 FROM role_assignments ra
        INNER JOIN roles r ON ra.role_id = r.id
        WHERE ra.account_id = $1 
          AND ra.is_active = true
          AND (r.name = 'admin' OR r.name = 'super_admin')
      ) as is_admin`,
        [userId],
      );

      if (!adminCheck.rows[0]?.is_admin) {
        return next(new ErrorHandler("Admin access required", 403));
      }

      try {
        const updates = await productAlternativeService.bulkUpdateAlternatives(
          alternative_ids,
          update_data,
        );

        res.status(200).json({
          success: true,
          message: "Bulk update completed",
          data: updates,
        });
      } catch (error: any) {
        return next(new ErrorHandler(error.message, 400));
      }
    },
  ),

  // Check permission for alternative
  checkAlternativePermission: catchAsyncError(
    async (req: AuthRequest, res: Response, next: NextFunction) => {
      const { id } = req.params;
      const userId = req.user?.id;

      if (!userId) {
        return next(new ErrorHandler("Authentication required", 401));
      }

      const hasPermission =
        await productAlternativeService.checkAlternativePermission(id, userId);

      res.status(200).json({
        success: true,
        data: {
          has_permission: hasPermission,
          alternative_id: id,
        },
      });
    },
  ),
};
