/**
 * Broker Availability API
 * GET - Get broker availability blocks for a date range
 * PUT - Add/update availability block
 */

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

export const dynamic = "force-dynamic";

/**
 * GET /api/real-estate/brokers/[id]/availability
 * Get broker availability blocks for a date range.
 * Query params: startDate, endDate (ISO date strings)
 */
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 data-source branch ──────────────────────────────────
      if (getDataSource("real_estate") === "convex") {
        try {
          const { convexQuery } = await import("@/lib/convex-client");

          const startDateParam = request.nextUrl.searchParams.get("startDate");
          const endDateParam = request.nextUrl.searchParams.get("endDate");
          const startDateMs = startDateParam
            ? new Date(startDateParam).getTime()
            : Date.now();
          const endDateMs = endDateParam
            ? new Date(endDateParam).getTime()
            : Date.now() + 7 * 24 * 60 * 60 * 1000;

          // Get broker details for default schedule
          const brokerData = await convexQuery(
            "real_estate/queries:getBrokerById",
            { id: id as any },
          );

          if (brokerData) {
            const availData = await convexQuery(
              "real_estate/queries:getBrokerAvailability",
              { brokerId: id, startDate: startDateMs, endDate: endDateMs },
            );

            return ApiResponses.success({
              brokerId: id,
              defaultSchedule: {
                workingDays: (brokerData as any).workingDays,
                workStartTime: (brokerData as any).workStartTime,
                workEndTime: (brokerData as any).workEndTime,
              },
              blocks: (availData as any)?.blocks || [],
              viewings: (availData as any)?.viewings || [],
            });
          }
        } catch (error) {
          console.error(
            "[Convex fallback] real_estate brokers/[id]/availability GET:",
            error,
          );
          // Fall through to Prisma
        }
      }

      // Verify broker belongs to this org
      const broker = await prisma.broker.findFirst({
        where: { id, userId: effectiveUserId },
        select: {
          id: true,
          workingDays: true,
          workStartTime: true,
          workEndTime: true,
        },
      });
      if (!broker) {
        return ApiResponses.notFound("Broker not found");
      }

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

      // Default: current week
      const startDate = startDateParam ? new Date(startDateParam) : new Date();
      const endDate = endDateParam
        ? new Date(endDateParam)
        : new Date(Date.now() + 7 * 24 * 60 * 60 * 1000);

      if (isNaN(startDate.getTime()) || isNaN(endDate.getTime())) {
        return ApiResponses.badRequest(
          "Invalid date format. Use ISO date strings.",
        );
      }

      // Get availability blocks
      const blocks = await prisma.brokerAvailability.findMany({
        where: {
          brokerId: id,
          date: {
            gte: startDate,
            lte: endDate,
          },
        },
        orderBy: [{ date: "asc" }, { startTime: "asc" }],
      });

      // Also get scheduled viewings in this range for conflict visibility
      const viewings = await prisma.propertyViewing.findMany({
        where: {
          brokerId: id,
          viewingDate: { gte: startDate, lte: endDate },
          status: { notIn: ["CANCELLED", "NO_SHOW"] },
        },
        select: {
          id: true,
          viewingDate: true,
          viewingTime: true,
          duration: true,
          status: true,
          clientName: true,
        },
        orderBy: [{ viewingDate: "asc" }, { viewingTime: "asc" }],
      });

      return ApiResponses.success({
        brokerId: id,
        defaultSchedule: {
          workingDays: broker.workingDays,
          workStartTime: broker.workStartTime,
          workEndTime: broker.workEndTime,
        },
        blocks,
        viewings,
      });
    } catch (error) {
      logger.error("[Broker Availability API] GET error", {
        error: getErrorMessage(error),
      });
      return ApiResponses.serverError("Failed to fetch availability");
    }
  },
  rateLimiters.authGeneral,
);

/**
 * PUT /api/real-estate/brokers/[id]/availability
 * Add/update availability block.
 * Body: { date, startTime, endTime, isBlocked, reason? }
 */
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);

        // Verify broker belongs to this org
        const broker = await prisma.broker.findFirst({
          where: { id, userId: effectiveUserId },
          select: { id: true },
        });
        if (!broker) {
          return ApiResponses.notFound("Broker not found");
        }

        const body = await request.json();

        // Validate required fields
        if (!body.date) {
          return ApiResponses.badRequest("date is required");
        }
        if (!body.startTime || !body.endTime) {
          return ApiResponses.badRequest("startTime and endTime are required");
        }

        const blockDate = new Date(body.date);
        if (isNaN(blockDate.getTime())) {
          return ApiResponses.badRequest("Invalid date format");
        }

        // Validate time format (HH:MM)
        const timeRegex = /^([01]\d|2[0-3]):([0-5]\d)$/;
        if (!timeRegex.test(body.startTime) || !timeRegex.test(body.endTime)) {
          return ApiResponses.badRequest("Time must be in HH:MM format");
        }

        if (body.startTime >= body.endTime) {
          return ApiResponses.badRequest("startTime must be before endTime");
        }

        // Check for existing block on the same date and time range; update if exists
        const dateStart = new Date(blockDate);
        dateStart.setHours(0, 0, 0, 0);
        const dateEnd = new Date(blockDate);
        dateEnd.setHours(23, 59, 59, 999);

        const existingBlock = await prisma.brokerAvailability.findFirst({
          where: {
            brokerId: id,
            date: { gte: dateStart, lt: dateEnd },
            startTime: body.startTime,
            endTime: body.endTime,
          },
        });

        let block;
        if (existingBlock) {
          block = await prisma.brokerAvailability.update({
            where: { id: existingBlock.id },
            data: {
              isBlocked: body.isBlocked ?? true,
              reason: body.reason?.trim() || null,
            },
          });
        } else {
          block = await prisma.brokerAvailability.create({
            data: {
              id: createId(),
              brokerId: id,
              date: new Date(body.date),
              startTime: body.startTime,
              endTime: body.endTime,
              isBlocked: body.isBlocked ?? true,
              reason: body.reason?.trim() || null,
            },
          });
        }

        logger.info("[Broker Availability API] Block saved", {
          blockId: block.id,
          brokerId: id,
          date: body.date,
        });

        return ApiResponses.success({ block });
      } catch (error) {
        logger.error("[Broker Availability API] PUT error", {
          error: getErrorMessage(error),
        });
        return ApiResponses.serverError("Failed to update availability");
      }
    },
  ),
  rateLimiters.authGeneral,
);
