/**
 * Batch Match Refresh API
 * POST - Refresh all matches for the org
 */

import { NextRequest } from "next/server";
import { auth } from "@/lib/auth";
import { prisma } from "@/lib/db";
import { getAuthenticatedEmail } from "@/lib/utils/auth-helpers.util";
import { getEffectiveOwnerId } from "@/lib/utils/organization-data";
import { logger } from "@/lib/logger";
import { createId } from "@paralleldrive/cuid2";
import { ApiResponses } from "@/lib/utils/api-response.util";
import { withRateLimit, rateLimiters } from "@/lib/rate-limiter";
import { getErrorMessage } from "@/lib/errors";
import { withCSRF } from "@/lib/csrf";
import { getDataSource } from "@/lib/data-source";

export const dynamic = "force-dynamic";

/**
 * POST /api/real-estate/matches/refresh
 * Batch refresh all matches for the org.
 * Loops through active ClientRequirements, runs matching for each.
 */
export const POST = withRateLimit(
  withCSRF(async (request: NextRequest) => {
    try {
      const userEmail = await getAuthenticatedEmail(request, auth);
      if (!userEmail) {
        return ApiResponses.unauthorized();
      }

      const user = await prisma.users.findUnique({
        where: { email: userEmail },
        select: { id: true },
      });
      if (!user) {
        return ApiResponses.notFound("User not found");
      }

      const effectiveUserId = await getEffectiveOwnerId(user.id);

      // Convex branch
      if (getDataSource("real_estate") === "convex") {
        try {
          const { convexMutation } = await import("@/lib/convex-client");
          const convexResult = await convexMutation(
            "real_estate/mutations:refreshMatches",
            { userId: effectiveUserId },
          );
          if (convexResult) {
            const r = convexResult as {
              clientsProcessed: number;
              propertiesEvaluated: number;
              matchesCreated: number;
            };
            return ApiResponses.success({
              refreshed: r.clientsProcessed,
              propertiesEvaluated: r.propertiesEvaluated,
              totalMatchesCreated: r.matchesCreated,
              totalMatchesUpdated: 0,
              summary: [],
            });
          }
        } catch (convexErr) {
          console.error(
            "[Convex fallback] real_estate matches refresh:",
            convexErr,
          );
        }
      }

      // Fetch all active client requirements
      const activeClients = await prisma.clientRequirements.findMany({
        where: {
          userId: effectiveUserId,
          isActive: true,
        },
      });

      if (activeClients.length === 0) {
        return ApiResponses.success({
          refreshed: 0,
          message: "No active client requirements found",
        });
      }

      // Fetch all available properties once (shared across all clients)
      const allProperties = await prisma.property.findMany({
        where: {
          userId: effectiveUserId,
          status: "AVAILABLE",
        },
        select: {
          id: true,
          propertyType: true,
          listingType: true,
          price: true,
          currency: true,
          bedrooms: true,
          builtUpArea: true,
          city: true,
          district: true,
          amenities: true,
        },
      });

      let totalMatchesCreated = 0;
      let totalMatchesUpdated = 0;
      const summary: Array<{
        clientId: string;
        clientName: string;
        matchCount: number;
      }> = [];

      for (const requirements of activeClients) {
        // Filter properties by listing type if specified
        const filteredProps = requirements.listingType
          ? allProperties.filter(
              (p) => p.listingType === requirements.listingType,
            )
          : allProperties;

        const reqTypes = (requirements.propertyTypes as string[]) || [];
        const reqAreas = (requirements.preferredAreas as string[]) || [];
        const reqMustHaves = (requirements.mustHaves as string[]) || [];

        const scored: Array<{
          propertyId: string;
          score: number;
          reasons: string[];
        }> = [];

        for (const prop of filteredProps) {
          let score = 0;
          const reasons: string[] = [];

          // Price (30%)
          if (
            requirements.minBudget !== null ||
            requirements.maxBudget !== null
          ) {
            const inRange =
              (requirements.minBudget === null ||
                prop.price >= requirements.minBudget) &&
              (requirements.maxBudget === null ||
                prop.price <= requirements.maxBudget);
            if (inRange) {
              score += 30;
              reasons.push("Price in budget");
            } else if (
              requirements.maxBudget &&
              prop.price <= requirements.maxBudget * 1.1
            ) {
              score += 15;
              reasons.push("Price slightly over budget");
            }
          } else {
            score += 15;
          }

          // Bedrooms (20%)
          if (
            prop.bedrooms !== null &&
            (requirements.minBedrooms !== null ||
              requirements.maxBedrooms !== null)
          ) {
            const ok =
              (requirements.minBedrooms === null ||
                prop.bedrooms >= requirements.minBedrooms) &&
              (requirements.maxBedrooms === null ||
                prop.bedrooms <= requirements.maxBedrooms);
            if (ok) {
              score += 20;
              reasons.push("Bedrooms match");
            }
          } else {
            score += 10;
          }

          // Area (25%)
          if (reqAreas.length > 0) {
            if (prop.district && reqAreas.includes(prop.district)) {
              score += 25;
              reasons.push(`District: ${prop.district}`);
            } else if (prop.city && reqAreas.includes(prop.city)) {
              score += 15;
              reasons.push(`City: ${prop.city}`);
            }
          } else {
            score += 12;
          }

          // Amenities (15%)
          if (reqMustHaves.length > 0) {
            const propAmenities = (prop.amenities as string[]) || [];
            const matched = reqMustHaves.filter((a: string) =>
              propAmenities.some((pa: string) =>
                pa.toLowerCase().includes(a.toLowerCase()),
              ),
            );
            score += Math.min(
              15,
              Math.round((matched.length / reqMustHaves.length) * 15),
            );
            if (matched.length > 0) {
              reasons.push(
                `${matched.length}/${reqMustHaves.length} must-haves`,
              );
            }
          } else {
            score += 7;
          }

          // Type (10%)
          if (reqTypes.length > 0) {
            if (reqTypes.includes(prop.propertyType)) {
              score += 10;
              reasons.push(`Type: ${prop.propertyType}`);
            }
          } else {
            score += 5;
          }

          if (score > 0) {
            scored.push({ propertyId: prop.id, score, reasons });
          }
        }

        // Sort and upsert top 50
        scored.sort((a, b) => b.score - a.score);
        const top = scored.slice(0, 50);

        for (const match of top) {
          try {
            const existing = await prisma.propertyMatch.findUnique({
              where: {
                propertyId_requirementId: {
                  propertyId: match.propertyId,
                  requirementId: requirements.id,
                },
              },
            });

            if (existing) {
              await prisma.propertyMatch.update({
                where: { id: existing.id },
                data: {
                  matchScore: match.score,
                  matchReasons: match.reasons,
                },
              });
              totalMatchesUpdated++;
            } else {
              await prisma.propertyMatch.create({
                data: {
                  id: createId(),
                  propertyId: match.propertyId,
                  requirementId: requirements.id,
                  matchScore: match.score,
                  matchReasons: match.reasons,
                },
              });
              totalMatchesCreated++;
            }
          } catch {
            // Non-fatal -- continue
          }
        }

        summary.push({
          clientId: requirements.id,
          clientName: requirements.clientName,
          matchCount: top.length,
        });
      }

      logger.info("[Match Refresh API] Batch refresh completed", {
        clientsProcessed: activeClients.length,
        propertiesEvaluated: allProperties.length,
        totalMatchesCreated,
        totalMatchesUpdated,
      });

      return ApiResponses.success({
        refreshed: activeClients.length,
        propertiesEvaluated: allProperties.length,
        totalMatchesCreated,
        totalMatchesUpdated,
        summary,
      });
    } catch (error) {
      logger.error("[Match Refresh API] POST error", {
        error: getErrorMessage(error),
      });
      return ApiResponses.serverError("Failed to refresh matches");
    }
  }),
  rateLimiters.authGeneral,
);
