/**
 * Real Estate Client Detail API
 * GET    - Client detail with requirements and top matches
 * PUT    - Update requirements and re-run matching
 * DELETE - Soft-delete (deactivate) client requirements
 */

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 { 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 { createId } from "@paralleldrive/cuid2";
import { getDataSource } from "@/lib/data-source";

export const dynamic = "force-dynamic";

/**
 * GET /api/real-estate/clients/[id]
 * Client detail with requirements and top matches.
 */
export const GET = withRateLimit(
  async (
    request: NextRequest,
    { params }: { params: Promise<{ id: string }> },
  ) => {
    try {
      const { id } = await params;

      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 { convexQuery } = await import("@/lib/convex-client");
          const convexClient = await convexQuery(
            "real_estate/queries:getClientById",
            { id, userId: effectiveUserId },
          );
          if (convexClient) {
            return ApiResponses.success({ client: convexClient });
          }
          return ApiResponses.notFound("Client requirements not found");
        } catch (error) {
          console.error("[Convex fallback] real_estate clients GET:", error);
        }
      }

      const client = await prisma.clientRequirements.findFirst({
        where: { id, userId: effectiveUserId },
        include: {
          matches: {
            orderBy: { matchScore: "desc" },
            take: 10,
            include: {
              property: {
                select: {
                  id: true,
                  titleEn: true,
                  titleAr: true,
                  propertyType: true,
                  listingType: true,
                  status: true,
                  price: true,
                  currency: true,
                  bedrooms: true,
                  bathrooms: true,
                  builtUpArea: true,
                  city: true,
                  district: true,
                  images: {
                    where: { isPrimary: true },
                    take: 1,
                    select: { url: true },
                  },
                },
              },
            },
          },
        },
      });

      if (!client) {
        return ApiResponses.notFound("Client requirements not found");
      }

      return ApiResponses.success({ client });
    } catch (error) {
      logger.error("[Client Detail API] GET error", {
        error: getErrorMessage(error),
      });
      return ApiResponses.serverError("Failed to fetch client details");
    }
  },
  rateLimiters.authGeneral,
);

/**
 * PUT /api/real-estate/clients/[id]
 * Update requirements. Re-run matching after update.
 */
export const PUT = withRateLimit(
  withCSRF(
    async (
      request: NextRequest,
      { params }: { params: Promise<{ id: string }> },
    ) => {
      try {
        const { id } = await params;

        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 convexBody = await request.json();
            const { convexMutation } = await import("@/lib/convex-client");
            const convexResult = await convexMutation(
              "real_estate/mutations:updateClient",
              { id, userId: effectiveUserId, ...convexBody },
            );
            return ApiResponses.success({
              client: convexResult,
              matchesRefreshed: 0,
              topMatches: [],
            });
          } catch (convexErr) {
            console.error(
              "[Convex fallback] real_estate clients PUT:",
              convexErr,
            );
          }
        }

        const existing = await prisma.clientRequirements.findFirst({
          where: { id, userId: effectiveUserId },
        });
        if (!existing) {
          return ApiResponses.notFound("Client requirements not found");
        }

        const body = await request.json();

        // Build update data
        const updateData: Record<string, unknown> = {};
        const allowedFields = [
          "clientName",
          "clientPhone",
          "clientEmail",
          "listingType",
          "propertyTypes",
          "minBedrooms",
          "maxBedrooms",
          "minBudget",
          "maxBudget",
          "currency",
          "preferredAreas",
          "mustHaves",
          "dealBreakers",
          "minArea",
          "timeline",
          "purpose",
          "financingType",
          "preApproved",
          "leadScore",
          "isActive",
        ];

        for (const field of allowedFields) {
          if (body[field] !== undefined) {
            updateData[field] =
              typeof body[field] === "string"
                ? body[field].trim()
                : body[field];
          }
        }

        // Budget validation
        const minBudget = updateData.minBudget ?? existing.minBudget;
        const maxBudget = updateData.maxBudget ?? existing.maxBudget;
        if (
          minBudget !== null &&
          maxBudget !== null &&
          typeof minBudget === "number" &&
          typeof maxBudget === "number" &&
          minBudget > maxBudget
        ) {
          return ApiResponses.badRequest("minBudget cannot exceed maxBudget");
        }

        updateData.lastContactAt = new Date();

        const client = await prisma.clientRequirements.update({
          where: { id },
          data: updateData,
        });

        // Re-run matching: find matching available properties
        const matchResults = await runMatchingForClient(
          client,
          effectiveUserId,
        );

        logger.info(
          "[Client Detail API] Requirements updated and matching refreshed",
          {
            clientId: id,
            matchCount: matchResults.length,
          },
        );

        return ApiResponses.success({
          client,
          matchesRefreshed: matchResults.length,
          topMatches: matchResults.slice(0, 5),
        });
      } catch (error) {
        logger.error("[Client Detail API] PUT error", {
          error: getErrorMessage(error),
        });
        return ApiResponses.serverError("Failed to update client requirements");
      }
    },
  ),
  rateLimiters.authGeneral,
);

/**
 * DELETE /api/real-estate/clients/[id]
 * Soft-delete: sets isActive = false and removes associated matches.
 */
export const DELETE = withRateLimit(
  withCSRF(
    async (
      request: NextRequest,
      { params }: { params: Promise<{ id: string }> },
    ) => {
      try {
        const { id } = await params;

        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");
            await convexMutation("real_estate/mutations:deleteClient", {
              id,
              userId: effectiveUserId,
            });
            return ApiResponses.success({
              deleted: true,
              message: "Client requirements deactivated and matches removed",
            });
          } catch (convexErr) {
            console.error(
              "[Convex fallback] real_estate clients DELETE:",
              convexErr,
            );
          }
        }

        const existing = await prisma.clientRequirements.findFirst({
          where: { id, userId: effectiveUserId },
        });
        if (!existing) {
          return ApiResponses.notFound("Client requirements not found");
        }

        // Soft-delete: deactivate and remove matches in a transaction
        await prisma.$transaction(async (tx) => {
          await tx.clientRequirements.update({
            where: { id },
            data: { isActive: false },
          });

          // Remove associated property matches
          await tx.propertyMatch.deleteMany({
            where: { requirementId: id },
          });
        });

        logger.info("[Client Detail API] Client requirements deactivated", {
          clientId: id,
          clientName: existing.clientName,
        });

        return ApiResponses.success({
          deleted: true,
          message: "Client requirements deactivated and matches removed",
        });
      } catch (error) {
        logger.error("[Client Detail API] DELETE error", {
          error: getErrorMessage(error),
        });
        return ApiResponses.serverError("Failed to delete client requirements");
      }
    },
  ),
  rateLimiters.authGeneral,
);

/**
 * Run matching algorithm for a single client requirement set.
 * Returns scored property matches sorted descending.
 */
async function runMatchingForClient(
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  requirements: any,
  userId: string,
): Promise<Array<{ propertyId: string; score: number; reasons: string[] }>> {
  // Fetch available properties
  const properties = await prisma.property.findMany({
    where: {
      userId,
      status: "AVAILABLE",
      ...(requirements.listingType
        ? { listingType: requirements.listingType }
        : {}),
    },
    select: {
      id: true,
      propertyType: true,
      listingType: true,
      price: true,
      currency: true,
      bedrooms: true,
      builtUpArea: true,
      city: true,
      district: true,
      amenities: true,
    },
  });

  const results: Array<{
    propertyId: string;
    score: number;
    reasons: string[];
  }> = [];
  const reqTypes = (requirements.propertyTypes as string[]) || [];
  const reqAreas = (requirements.preferredAreas as string[]) || [];
  const reqMustHaves = (requirements.mustHaves as string[]) || [];

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

    // Price match (30% weight) -- max 30 points
    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 above budget");
      }
    } else {
      score += 15; // No budget filter
    }

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

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

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

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

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

  // Sort by score descending
  results.sort((a, b) => b.score - a.score);

  // Upsert PropertyMatch records for top results
  for (const result of results.slice(0, 50)) {
    try {
      await prisma.propertyMatch.upsert({
        where: {
          propertyId_requirementId: {
            propertyId: result.propertyId,
            requirementId: requirements.id,
          },
        },
        update: {
          matchScore: result.score,
          matchReasons: result.reasons,
        },
        create: {
          id: createId(),
          propertyId: result.propertyId,
          requirementId: requirements.id,
          matchScore: result.score,
          matchReasons: result.reasons,
        },
      });
    } catch {
      // Upsert conflict is non-fatal
    }
  }

  return results;
}
