import { Router } from "express";
import { z } from "zod";
import { Favorite, Property, City } from "@/db/models";
import { AppError, asyncHandler } from "@/lib/errors";
import { requireAuth, requireRoles } from "@/middleware/auth";
import { serializeProperty } from "@/lib/serialize";
import { isFavoritesMysql, isPropertiesMysql, isCitiesMysql } from "@/db/activeDatabase";
import {
  createFavoriteMysql,
  deleteFavoriteMysql,
  findFavoriteMysql,
  listFavoritesMysql,
} from "@/db/mysql/favorites";
import { findPropertiesByIdsMysql, findPropertyByIdMysql, propertyToApi } from "@/db/mysql/properties";
import { findCitiesByIdsMysql, cityRowToApi } from "@/db/mysql/cities";

export const favoritesRouter = Router();

favoritesRouter.get(
  "/",
  requireAuth,
  asyncHandler(async (req, res) => {
    if (isFavoritesMysql()) {
      const favorites = await listFavoritesMysql(req.user!.id);
      const propertyIds = favorites.map((f) => f.propertyId);

      if (isPropertiesMysql()) {
        const properties = await findPropertiesByIdsMysql(propertyIds);
        const cityIds = [...new Set(properties.map((p) => p.cityId))];
        const cityMap = new Map<string, any>();
        if (isCitiesMysql()) {
          for (const c of await findCitiesByIdsMysql(cityIds)) {
            cityMap.set(c.id, cityRowToApi(c));
          }
        } else {
          const cities = await City.find({ _id: { $in: cityIds } }).lean();
          for (const c of cities) cityMap.set(c._id, c);
        }
        const propertyMap = new Map(
          properties.map((p) => {
            const api = propertyToApi(p);
            return [
              p.id,
              {
                ...api,
                city: cityMap.get(p.cityId),
                images: (api.images || []).slice(0, 1),
              },
            ];
          }),
        );
        res.json({
          favorites: favorites.map((f) => ({
            id: f.id,
            createdAt: f.createdAt,
            property: serializeProperty(propertyMap.get(f.propertyId)),
          })),
          _db: "mysql",
        });
        return;
      }

      const properties = await Property.find({ _id: { $in: propertyIds } }).lean();
      const cityIds = [...new Set(properties.map((p) => p.cityId))];
      const cities = await City.find({ _id: { $in: cityIds } }).lean();
      const cityMap = new Map(cities.map((c) => [c._id, c]));
      const propertyMap = new Map(
        properties.map((p) => [
          p._id,
          {
            ...p,
            city: cityMap.get(p.cityId),
            images: [...(p.images || [])]
              .sort((a: any, b: any) => (a.sortOrder ?? 0) - (b.sortOrder ?? 0))
              .slice(0, 1),
          },
        ]),
      );

      res.json({
        favorites: favorites.map((f) => ({
          id: f.id,
          createdAt: f.createdAt,
          property: serializeProperty(propertyMap.get(f.propertyId)),
        })),
        _db: "mysql",
      });
      return;
    }

    const favorites = await Favorite.find({ userId: req.user!.id }).sort({ createdAt: -1 }).lean();
    const propertyIds = favorites.map((f) => f.propertyId);
    const properties = await Property.find({ _id: { $in: propertyIds } }).lean();
    const cityIds = [...new Set(properties.map((p) => p.cityId))];
    const cities = await City.find({ _id: { $in: cityIds } }).lean();
    const cityMap = new Map(cities.map((c) => [c._id, c]));
    const propertyMap = new Map(
      properties.map((p) => [
        p._id,
        {
          ...p,
          city: cityMap.get(p.cityId),
          images: [...(p.images || [])]
            .sort((a: any, b: any) => (a.sortOrder ?? 0) - (b.sortOrder ?? 0))
            .slice(0, 1),
        },
      ]),
    );

    res.json({
      favorites: favorites.map((f) => ({
        id: f._id,
        createdAt: f.createdAt,
        property: serializeProperty(propertyMap.get(f.propertyId)),
      })),
      _db: "mongodb",
    });
  }),
);

favoritesRouter.post(
  "/toggle",
  requireAuth,
  requireRoles("CUSTOMER"),
  asyncHandler(async (req, res) => {
    const propertyId = z.string().min(1).parse(req.body.propertyId);
    let propertyOk = false;
    if (isPropertiesMysql()) {
      const p = await findPropertyByIdMysql(propertyId);
      propertyOk = !!p && p.status === "PUBLISHED";
    } else {
      const property = await Property.findOne({
        _id: propertyId,
        deletedAt: null,
        status: "PUBLISHED",
      }).lean();
      propertyOk = !!property;
    }
    if (!propertyOk) throw new AppError(404, "Property not found");

    if (isFavoritesMysql()) {
      const existing = await findFavoriteMysql(req.user!.id, propertyId);
      if (existing) {
        await deleteFavoriteMysql(existing.id);
        return res.json({ favorited: false, _db: "mysql" });
      }
      await createFavoriteMysql(req.user!.id, propertyId);
      return res.json({ favorited: true, _db: "mysql" });
    }

    const existing = await Favorite.findOne({ userId: req.user!.id, propertyId });
    if (existing) {
      await Favorite.deleteOne({ _id: existing._id });
      return res.json({ favorited: false, _db: "mongodb" });
    }

    await Favorite.create({ userId: req.user!.id, propertyId });
    res.json({ favorited: true, _db: "mongodb" });
  }),
);
