// oms/src/controllers/artController.ts
import { Request, Response } from "express";
import artService from "../services/artService"; // Import ArtService
import { IArt, PageStatus, SpecialThumbType } from "../models/artModel"; // Import IArt interface and enums
import mongoose, { FilterQuery, SortOrder } from "mongoose"; // Import Mongoose types

// Define valid keys for Art model for runtime query parameter validation
const ART_MODEL_QUERY_KEYS: (keyof IArt)[] = [
  "status",
  "pageId",
  "user",
  "areaCount",
  "areaCountFloor",
  "colorCount",
  "hasSpecial",
  "mystery",
  "ai",
  "width",
  "height",
  "name",
  "desc",
  "use",
  "tags",
  "epgs",
  "date",
  "lastMod",
  "timeSubmit",
  "lock",
  "publishTime",
  "drop",
  "totalStartCount",
  "totalDoneCount",
  "completionRate",
];

class ArtController {
  /**
   * Handles requests to update art information.
   * PUT /api/arts/:id
   * @param req Express request object
   * @param res Express response object
   * @returns Promise<void>
   */
  public async updateArt(req: Request, res: Response): Promise<void> {
    try {
      const { id } = req.params; // Get art ID from URL parameters
      const updateData = req.body; // Get update data from request body

      // Validate if ID is a valid MongoDB ObjectId
      if (!mongoose.Types.ObjectId.isValid(id)) {
        res.status(400).json({ message: "Invalid Art ID format." });
        return;
      }

      const updatedArt = await artService.updateArt(id, updateData);
      if (updatedArt) {
        res.status(200).json(updatedArt); // Return the updated art document
      } else {
        res.status(404).json({ message: `Art with ID: ${id} not found.` }); // Art not found
      }
    } catch (error: any) {
      console.error("Failed to update art:", error);
      res.status(500).json({ message: "Failed to update art", error: error.message }); // Internal server error
    }
  }

  /**
   * Handles requests to retrieve a paginated list of artworks based on conditions.
   * GET /api/arts?page=1&limit=30&status=9000&name=example&tags=tag1,tag2&dateStart=2023-01-01&dateEnd=2023-12-31&sortBy=date&sortOrder=desc
   * @param req Express request object
   * @param res Express response object
   * @returns Promise<void>
   */
  public async getArts(req: Request, res: Response): Promise<void> {
    try {
      const page = parseInt(req.query.page as string) || 1; // Parse page number, default to 1
      const limit = parseInt(req.query.limit as string) || 30; // Parse limit per page, default to 30

      const filters: FilterQuery<IArt> = {}; // Initialize MongoDB filter object
      const sort: { [key: string]: SortOrder } = {}; // Initialize sort object

      // Build filters
      for (const key of ART_MODEL_QUERY_KEYS) {
        const queryValue = req.query[key as string];
        const startValue = req.query[`${key}Start`] as string; // For date range start
        const endValue = req.query[`${key}End`] as string; // For date range end

        if (key === "status") {
          const statusNum = parseInt(queryValue as string, 10);
          // Validate if status is a valid PageStatus enum value
          if (!isNaN(statusNum) && Object.values(PageStatus).includes(statusNum)) {
            filters.status = statusNum;
          }
        } else if (key === "name") {
          // Fuzzy search for art name (case-insensitive)
          filters.name = { $regex: queryValue as string, $options: "i" } as FilterQuery<IArt>["name"];
        } else if (key === "tags" || key === "epgs") {
          // For tags or EPGs array, allow multiple values separated by commas
          if (queryValue) {
            filters[key] = { $in: (queryValue as string).split(",").map((s) => s.trim()) } as any;
          }
        } else if (key === "hasSpecial" || key === "mystery" || key === "ai" || key === "lock" || key === "drop") {
          // Boolean fields
          if (queryValue !== undefined) {
            filters[key] = (queryValue as string).toLowerCase() === "true";
          }
        } else if (key === "user" || key === "work" || key === "publishBy" || key === "pageId") {
          // ObjectId fields
          if (queryValue && mongoose.Types.ObjectId.isValid(queryValue as string)) {
            filters[key] = new mongoose.Types.ObjectId(queryValue as string) as any;
          }
        } else if (["date", "lastMod", "timeSubmit", "timeReady", "publishTime"].includes(key)) {
          // Date range query for date fields
          const dateFilter: { $gte?: Date; $lte?: Date } = {};
          let isValidDateQuery = false;

          if (startValue) {
            try {
              const startDate = new Date(startValue);
              if (!isNaN(startDate.getTime())) {
                dateFilter.$gte = startDate;
                isValidDateQuery = true;
              } else {
                console.warn(`Invalid start date format for '${key}Start': ${startValue}`);
              }
            } catch (e) {
              console.warn(`Error parsing start date for '${key}Start': ${startValue}, Error: ${e}`);
            }
          }

          if (endValue) {
            try {
              const endDate = new Date(endValue);
              if (!isNaN(endDate.getTime())) {
                dateFilter.$lte = endDate;
                isValidDateQuery = true;
              } else {
                console.warn(`Invalid end date format for '${key}End': ${endValue}`);
              }
            } catch (e) {
              console.warn(`Error parsing end date for '${key}End': ${endValue}, Error: ${e}`);
            }
          }

          if (isValidDateQuery) {
            filters[key] = dateFilter as any;
          }
        } else if (
          // Numeric fields
          typeof (filters as any)[key] === "number" ||
          [
            "areaCount",
            "areaCountFloor",
            "coloredAreaCount",
            "colorCount",
            "orderedColorCount",
            "colorOrderStatus",
            "mapVersion",
            "workVersion",
            "centersVersion",
            "specialVersion",
            "specialPkmVersion",
            "useSpecialThumb",
            "width",
            "height",
            "publishVersion",
            "order",
            "score",
            "totalStartCount",
            "totalDoneCount",
            "completionRate",
          ].includes(key)
        ) {
          if (queryValue !== undefined) {
            const numValue = parseFloat(queryValue as string);
            if (!isNaN(numValue)) {
              (filters as any)[key] = numValue;
            }
          }
        } else if (queryValue !== undefined) {
          // Other string type fields, assign directly
          (filters as any)[key] = queryValue;
        }
      }

      // Build sort conditions
      if (req.query.sortBy) {
        const sortByField = req.query.sortBy as string;
        // Ensure sort field is a valid key from IArt and exclude non-sortable fields like _id
        if (ART_MODEL_QUERY_KEYS.includes(sortByField as keyof IArt) && sortByField !== "_id") {
          const sortOrder = req.query.sortOrder === "asc" || parseInt(req.query.sortOrder as string) === 1 ? 1 : -1;
          sort[sortByField] = sortOrder;
        }
      } else {
        sort.lastMod = -1; // Default sort by last modification time, descending
      }

      const { arts, total } = await artService.getPaginatedArts(page, limit, filters, sort);
      res.status(200).json({ page, limit, total, arts }); // Return paginated results
    } catch (error: any) {
      console.error("Failed to retrieve art list:", error);
      res.status(500).json({ message: "Failed to retrieve art list", error: error.message }); // Internal server error
    }
  }

  /**
   * Handles requests to retrieve a single artwork by ID.
   * GET /api/arts/:id
   * @param req Express request object
   * @param res Express response object
   * @returns Promise<void>
   */
  public async getArtById(req: Request, res: Response): Promise<void> {
    try {
      const { id } = req.params; // Get art ID from URL parameters

      // Validate if ID is a valid MongoDB ObjectId
      if (!mongoose.Types.ObjectId.isValid(id)) {
        res.status(400).json({ message: "Invalid Art ID format." });
        return;
      }

      const art = await artService.getArtById(id);
      if (art) {
        res.status(200).json(art); // Return art document
      } else {
        res.status(404).json({ message: `Art with ID: ${id} not found.` }); // Art not found
      }
    } catch (error: any) {
      console.error("Failed to retrieve art:", error);
      res.status(500).json({ message: "Failed to retrieve art", error: error.message }); // Internal server error
    }
  }
}

const artController = new ArtController();
export default artController;
