/**
 * Viewing Calendar API
 * GET - Calendar view data grouped by date
 *
 * Returns viewings grouped by date with property and client info,
 * plus summary statistics by status and broker.
 *
 * Supports both NextAuth and OTP (auth_token) authentication
 */

import { NextRequest, NextResponse } 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";

export const dynamic = "force-dynamic";

/**
 * GET /api/real-estate/viewings/calendar
 * Returns viewings organized for calendar display.
 *
 * Query params:
 *   - startDate (required): ISO date string
 *   - endDate (required): ISO date string
 *   - brokerId (optional): filter by broker
 */
export const GET = withRateLimit(async (request: NextRequest) => {
  try {
    const userEmail = await getAuthenticatedEmail(request, auth);

    if (!userEmail) {
      logger.warn("Unauthorized calendar request");
      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);

    // NOTE: data-source routing via getDataSource() is a no-op now —
    // everything goes through the Convex-backed prisma adapter below.
    // The earlier "Convex fast path" that existed here was broken
    // (wrong arg types + always returned empty data) and has been
    // removed. The adapter path produces the right shape.

    const searchParams = request.nextUrl.searchParams;
    const startDate = searchParams.get("startDate");
    const endDate = searchParams.get("endDate");
    const brokerId = searchParams.get("brokerId");

    // --- Validation ---
    if (!startDate || !endDate) {
      return ApiResponses.badRequest(
        "Both startDate and endDate query parameters are required",
      );
    }

    const parsedStart = new Date(startDate);
    const parsedEnd = new Date(endDate);

    if (isNaN(parsedStart.getTime()) || isNaN(parsedEnd.getTime())) {
      return ApiResponses.badRequest(
        "Invalid date format. Use ISO 8601 (YYYY-MM-DD)",
      );
    }

    if (parsedStart > parsedEnd) {
      return ApiResponses.badRequest(
        "startDate must be before or equal to endDate",
      );
    }

    // Limit range to 90 days to prevent excessive queries
    const diffMs = parsedEnd.getTime() - parsedStart.getTime();
    const diffDays = diffMs / (1000 * 60 * 60 * 24);
    if (diffDays > 90) {
      return ApiResponses.badRequest("Date range cannot exceed 90 days");
    }

    // --- Build where clause ---
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    const whereClause: any = {
      userId: effectiveUserId,
      viewingDate: {
        gte: parsedStart,
        lte: parsedEnd,
      },
      status: { not: "CANCELLED" },
    };

    if (brokerId) {
      whereClause.brokerId = brokerId;
    }

    // --- Fetch viewings ---
    const viewings = await prisma.propertyViewing.findMany({
      where: whereClause,
      include: {
        property: {
          select: {
            id: true,
            titleEn: true,
            titleAr: true,
            referenceNumber: true,
            propertyType: true,
            city: true,
            district: true,
          },
        },
        broker: {
          select: {
            id: true,
            nameEn: true,
            nameAr: true,
            phone: true,
            photoUrl: true,
          },
        },
      },
      orderBy: [{ viewingDate: "asc" }, { viewingTime: "asc" }],
    });

    // --- Group viewings by date ---
    const calendarData: Record<
      string,
      {
        date: string;
        viewings: typeof viewings;
        count: number;
      }
    > = {};

    // viewingDate may come back as a Date (Prisma) or a number/string
    // (Convex adapter). Normalise to YYYY-MM-DD before grouping.
    const toDateKey = (raw: unknown): string => {
      let d: Date;
      if (raw instanceof Date) d = raw;
      else if (typeof raw === "number") d = new Date(raw);
      else if (typeof raw === "string") d = new Date(raw);
      else d = new Date();
      return d.toISOString().split("T")[0];
    };

    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    for (const viewing of viewings as any[]) {
      const dateKey = toDateKey(viewing.viewingDate);
      if (!calendarData[dateKey]) {
        calendarData[dateKey] = {
          date: dateKey,
          viewings: [],
          count: 0,
        };
      }
      calendarData[dateKey].viewings.push(viewing);
      calendarData[dateKey].count++;
    }

    // --- Broker summary stats for the period ---
    const brokerStats = await prisma.propertyViewing.groupBy({
      by: ["brokerId"],
      where: {
        ...whereClause,
        brokerId: { not: null },
      },
      _count: { id: true },
    });

    const brokerIds = (brokerStats as Array<{ brokerId: string | null }>)
      .map((s) => s.brokerId)
      .filter(Boolean) as string[];

    const brokers =
      brokerIds.length > 0
        ? await prisma.broker.findMany({
            where: { id: { in: brokerIds } },
            select: { id: true, nameEn: true, nameAr: true },
          })
        : [];

    const brokerMap = new Map(
      (brokers as Array<{ id: string; nameEn: string; nameAr: string }>).map(
        (b) => [b.id, b],
      ),
    );

    const brokerSummary = (
      brokerStats as Array<{ brokerId: string | null; _count: { id: number } }>
    ).map((stat) => ({
      brokerId: stat.brokerId,
      broker: stat.brokerId ? brokerMap.get(stat.brokerId) : null,
      viewingCount: stat._count.id,
    }));

    // --- Status summary ---
    const statusCounts = await prisma.propertyViewing.groupBy({
      by: ["status"],
      where: whereClause,
      _count: { id: true },
    });

    const statusSummary = (
      statusCounts as Array<{ status: string; _count: { id: number } }>
    ).reduce<Record<string, number>>((acc, curr) => {
      acc[curr.status] = curr._count.id;
      return acc;
    }, {});

    logger.info("Calendar data retrieved", {
      userEmail,
      dateRange: `${startDate} to ${endDate}`,
      totalViewings: viewings.length,
      daysWithViewings: Object.keys(calendarData).length,
    });

    // Flat viewings list in the shape the ViewingsCalendar component expects.
    // (calendar[] is the grouped version for other consumers.)
    // viewingDate may be a Date (Prisma) or epoch ms (Convex adapter);
    // normalise to ISO string so the component's date comparison works.
    const toIso = (raw: unknown): string => {
      if (raw instanceof Date) return raw.toISOString();
      if (typeof raw === "number") return new Date(raw).toISOString();
      if (typeof raw === "string") return raw;
      return new Date().toISOString();
    };

    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    const flatViewings = (viewings as any[]).map((v) => ({
      id: v._id ?? v.id,
      propertyId: v.propertyId ?? "",
      propertyTitle: v.property?.titleEn ?? "(unspecified property)",
      clientName: v.clientName ?? "",
      clientPhone: v.clientPhone ?? "",
      clientEmail: v.clientEmail ?? undefined,
      viewingDate: toIso(v.viewingDate),
      viewingTime: v.viewingTime ?? "",
      duration: v.duration ?? 30,
      viewingType: v.viewingType ?? "INDIVIDUAL",
      brokerId: v.brokerId ?? "",
      brokerName: v.broker?.nameEn ?? "",
      status: v.status,
      notes: v.clientNotes ?? v.notes ?? undefined,
    }));

    return NextResponse.json({
      success: true,
      viewings: flatViewings,
      calendar: Object.values(calendarData),
      summary: {
        totalViewings: viewings.length,
        daysWithViewings: Object.keys(calendarData).length,
        byStatus: statusSummary,
        byBroker: brokerSummary,
      },
      dateRange: {
        from: startDate,
        to: endDate,
      },
    });
  } catch (error) {
    logger.error("Calendar data error", {
      error: getErrorMessage(error),
    });
    return ApiResponses.serverError("Failed to retrieve calendar data");
  }
}, rateLimiters.authGeneral);
