import { query } from "../_generated/server";
import { v } from "convex/values";

/**
 * Get analytics events (paginated, filterable)
 * Phase 8: Content, Careers, Real Estate, Analytics, Reviews
 */
export const getEvents = query({
  args: {
    userId: v.optional(v.string()),
    eventName: v.optional(v.string()),
    category: v.optional(v.string()),
    limit: v.optional(v.number()),
  },
  handler: async (ctx, args) => {
    const limit = args.limit ?? 100;

    if (args.userId) {
      return await ctx.db
        .query("analytics_events")
        .withIndex("by_userId_createdAt", (q) => q.eq("userId", args.userId))
        .order("desc")
        .take(limit);
    }

    if (args.category) {
      return await ctx.db
        .query("analytics_events")
        .withIndex("by_category_createdAt", (q) =>
          q.eq("category", args.category!),
        )
        .order("desc")
        .take(limit);
    }

    return await ctx.db.query("analytics_events").order("desc").take(limit);
  },
});

/**
 * Get site visit records (paginated)
 * Phase 8: Content, Careers, Real Estate, Analytics, Reviews
 */
export const getSiteVisits = query({
  args: {
    page: v.optional(v.string()),
    userId: v.optional(v.string()),
    limit: v.optional(v.number()),
  },
  handler: async (ctx, args) => {
    const limit = args.limit ?? 100;

    if (args.userId) {
      return await ctx.db
        .query("site_visits")
        .withIndex("by_userId", (q) => q.eq("userId", args.userId))
        .order("desc")
        .take(limit);
    }

    if (args.page) {
      return await ctx.db
        .query("site_visits")
        .withIndex("by_page", (q) => q.eq("page", args.page!))
        .order("desc")
        .take(limit);
    }

    return await ctx.db.query("site_visits").order("desc").take(limit);
  },
});

/**
 * Get aggregated metrics summary for a user
 * Phase 8: Content, Careers, Real Estate, Analytics, Reviews
 */
export const getMetricsSummary = query({
  args: {
    userId: v.string(),
    period: v.optional(v.string()),
  },
  handler: async (ctx, { userId }) => {
    const events = await ctx.db
      .query("analytics_events")
      .withIndex("by_userId_createdAt", (q) => q.eq("userId", userId))
      .collect();
    return {
      totalBookings: events.filter((e) => e.category === "booking").length,
      totalRevenue: events
        .filter((e) => e.category === "payment")
        .reduce(
          (sum, e) =>
            sum +
            ((e.properties as { value?: number } | undefined)?.value ?? 0),
          0,
        ),
      totalEvents: events.length,
    };
  },
});

/**
 * Get workflow analytics for a user
 * Phase 8: Content, Careers, Real Estate, Analytics, Reviews
 */
export const getWorkflowAnalytics = query({
  args: {
    userId: v.string(),
    workflowType: v.optional(v.string()),
  },
  handler: async (ctx, args) => {
    const q = ctx.db
      .query("workflow_analytics")
      .withIndex("by_userId_date", (q) => q.eq("userId", args.userId));

    if (args.workflowType) {
      return await q
        .filter((f) => f.eq(f.field("workflowType"), args.workflowType))
        .order("desc")
        .collect();
    }

    return await q.order("desc").collect();
  },
});

/**
 * Get dashboard metrics — cross-domain aggregate for a user.
 * Queries bookings, quotations, and vehicle_rentals tables
 * for combined revenue and counts.
 * Phase 8: Content, Careers, Real Estate, Analytics, Reviews
 */
export const getDashboardMetrics = query({
  args: {
    userId: v.string(),
    organizationId: v.optional(v.string()),
    startDate: v.optional(v.number()),
    endDate: v.optional(v.number()),
  },
  handler: async (ctx, { userId, organizationId, startDate, endDate }) => {
    // ── Bookings ──────────────────────────────────────────────────────
    let bookings;
    if (startDate !== undefined && endDate !== undefined) {
      bookings = await ctx.db
        .query("bookings")
        .withIndex("by_userId_appointmentDate", (q) =>
          q
            .eq("userId", userId)
            .gte("appointmentDate", startDate)
            .lte("appointmentDate", endDate),
        )
        .collect();
    } else {
      bookings = await ctx.db
        .query("bookings")
        .withIndex("by_userId", (q) => q.eq("userId", userId))
        .collect();
    }

    const totalBookings = bookings.length;
    const completedBookings = bookings.filter(
      (b) => b.status === "completed",
    ).length;
    const pendingBookings = bookings.filter(
      (b) => b.status === "pending",
    ).length;
    const cancelledBookings = bookings.filter(
      (b) => b.status === "cancelled",
    ).length;
    const noShowBookings = bookings.filter(
      (b) => b.status === "no_show",
    ).length;
    const totalRevenue = bookings.reduce(
      (sum, b) => sum + (b.totalAmount ?? 0),
      0,
    );

    // Deposit breakdowns
    const depositsCollected = bookings
      .filter((b) => b.depositPaid === true)
      .reduce((sum, b) => sum + (b.depositAmount ?? 0), 0);
    const depositsPending = bookings
      .filter(
        (b) =>
          b.depositPaid === false &&
          b.depositAmount !== undefined &&
          b.depositAmount > 0,
      )
      .reduce((sum, b) => sum + (b.depositAmount ?? 0), 0);

    // Rates
    const averageBookingValue =
      totalBookings > 0 ? totalRevenue / totalBookings : 0;
    const resolvedTotal =
      completedBookings + cancelledBookings + noShowBookings;
    const completionRate =
      resolvedTotal > 0 ? (completedBookings / resolvedTotal) * 100 : 0;
    const noShowRate =
      totalBookings > 0 ? (noShowBookings / totalBookings) * 100 : 0;
    const cancellationRate =
      totalBookings > 0 ? (cancelledBookings / totalBookings) * 100 : 0;

    // Fetch total customers for this user
    const customers = await ctx.db
      .query("customers")
      .withIndex("by_userId", (q) => q.eq("userId", userId))
      .collect();
    const totalCustomers = customers.length;

    // New customers created during the selected period
    const newCustomers =
      startDate !== undefined && endDate !== undefined
        ? customers.filter(
            (c) => c.createdAt >= startDate && c.createdAt <= endDate,
          ).length
        : totalCustomers;

    // ── Quotation metrics ─────────────────────────────────────────────
    // Use quotations table directly (has amountPaid, paymentStatus, total)
    // via the by_userId index. Filter by paidAt for date range when applicable.
    let quotations = await ctx.db
      .query("quotations")
      .withIndex("by_userId", (q) => q.eq("userId", userId))
      .collect();

    // Date-filter quotations: use paidAt for paid quotations
    if (startDate !== undefined && endDate !== undefined) {
      quotations = quotations.filter((q) => {
        // Include if paidAt falls within range, or if unpaid and createdAt is in range
        if (q.paidAt) {
          return q.paidAt >= startDate && q.paidAt <= endDate;
        }
        return q.createdAt >= startDate && q.createdAt <= endDate;
      });
    }

    const quotationCount = quotations.length;
    const quotationsPaid = quotations.filter(
      (q) => q.paymentStatus === "PAID",
    ).length;
    const quotationRevenue = quotations.reduce(
      (sum, q) => sum + (q.amountPaid ?? 0),
      0,
    );

    // Quotation pipeline breakdown by status
    const quotationsDraft = quotations.filter(
      (q) => q.status === "DRAFT",
    ).length;
    const quotationsSent = quotations.filter(
      (q) => q.status === "SENT",
    ).length;
    const quotationsAccepted = quotations.filter(
      (q) => q.status === "ACCEPTED",
    ).length;
    const quotationsCancelled = quotations.filter(
      (q) => q.status === "CANCELLED",
    ).length;

    // ── Vehicle rental metrics ────────────────────────────────────────
    // Rentals are stored with organizationId which may be either the real
    // org ID or the userId (the rentals API creates them with userId as orgId).
    // Try the real organizationId first, fall back to userId.
    let rentalRevenue = 0;
    let activeRentals = 0;
    let totalRentals = 0;

    const rentalOrgIds = organizationId
      ? [organizationId, userId]
      : [userId];

    for (const orgId of rentalOrgIds) {
      const rentals = await ctx.db
        .query("vehicle_rentals")
        .withIndex("by_organizationId_status", (q) =>
          q.eq("organizationId", orgId),
        )
        .collect();

      if (rentals.length > 0) {
        let filtered = rentals;
        if (startDate !== undefined && endDate !== undefined) {
          filtered = rentals.filter(
            (r) => r.pickupDate >= startDate && r.pickupDate <= endDate,
          );
        }
        totalRentals = filtered.length;
        activeRentals = filtered.filter((r) => r.status === "active").length;
        // Use totalAmount for active/completed rentals (amountPaid may be 0 until deposit is collected)
        // Rental amounts are stored in cents/halalas (÷100 to get QAR)
        rentalRevenue =
          filtered
            .filter((r) => r.status === "active" || r.status === "completed")
            .reduce((sum, r) => sum + (r.totalAmount ?? 0), 0) / 100;
        break; // Found rentals, no need to try next orgId
      }
    }

    // ── Combined revenue ──────────────────────────────────────────────
    const totalCombinedRevenue = totalRevenue + quotationRevenue + rentalRevenue;

    // ── Outstanding receivables ──────────────────────────────────────
    const unpaidQuotationAmount = quotations
      .filter(
        (q) => q.status === "ACCEPTED" && q.paymentStatus !== "PAID",
      )
      .reduce((sum, q) => sum + (q.total ?? 0), 0);

    const pendingDepositAmount = depositsPending; // already computed above

    // Overdue rental amount (rentals with status "overdue")
    let overdueRentalAmount = 0;
    for (const orgId of rentalOrgIds) {
      const rentals = await ctx.db
        .query("vehicle_rentals")
        .withIndex("by_organizationId_status", (q) =>
          q.eq("organizationId", orgId),
        )
        .collect();

      if (rentals.length > 0) {
        let filtered = rentals;
        if (startDate !== undefined && endDate !== undefined) {
          filtered = rentals.filter(
            (r) => r.pickupDate >= startDate && r.pickupDate <= endDate,
          );
        }
        overdueRentalAmount =
          filtered
            .filter((r) => r.status === "overdue")
            .reduce((sum, r) => sum + (r.totalAmount ?? 0), 0) / 100;
        break;
      }
    }

    const outstandingReceivables =
      unpaidQuotationAmount + pendingDepositAmount + overdueRentalAmount;

    return {
      // Booking metrics
      totalBookings,
      completedBookings,
      pendingBookings,
      cancelledBookings,
      noShowBookings,
      totalRevenue,
      totalCustomers,
      newCustomers,
      depositsCollected,
      depositsPending,
      averageBookingValue,
      completionRate,
      noShowRate,
      cancellationRate,
      // Quotation metrics
      quotationRevenue,
      quotationCount,
      quotationsPaid,
      quotationsDraft,
      quotationsSent,
      quotationsAccepted,
      quotationsCancelled,
      // Rental metrics
      rentalRevenue,
      activeRentals,
      totalRentals,
      // Combined total
      totalCombinedRevenue,
      // Outstanding receivables
      outstandingReceivables,
      unpaidQuotationAmount,
      pendingDepositAmount,
      overdueRentalAmount,
    };
  },
});

/**
 * Get revenue comparison between two periods (e.g. current 30 days vs previous 30 days).
 * Returns current revenue, previous revenue, and percentage change across
 * bookings, quotations, and vehicle rentals.
 */
export const getRevenueComparison = query({
  args: {
    userId: v.string(),
    organizationId: v.optional(v.string()),
    currentStart: v.number(), // epoch ms
    currentEnd: v.number(),
    previousStart: v.number(),
    previousEnd: v.number(),
  },
  handler: async (ctx, args) => {
    const { userId, organizationId, currentStart, currentEnd, previousStart, previousEnd } = args;

    // ── Helper: sum booking revenue for a date range ──────────────────
    const bookingRevenue = async (start: number, end: number) => {
      const bookings = await ctx.db
        .query("bookings")
        .withIndex("by_userId_appointmentDate", (q) =>
          q.eq("userId", userId).gte("appointmentDate", start).lte("appointmentDate", end),
        )
        .collect();
      return bookings
        .filter((b) => b.status === "confirmed" || b.status === "completed")
        .reduce((sum, b) => sum + (b.totalAmount ?? 0), 0);
    };

    // ── Helper: sum quotation revenue for a date range ────────────────
    const quotationRevenue = async (start: number, end: number) => {
      const quotations = await ctx.db
        .query("quotations")
        .withIndex("by_userId", (q) => q.eq("userId", userId))
        .collect();
      return quotations
        .filter((q) => {
          if (q.paymentStatus !== "PAID") return false;
          const ts = q.paidAt ?? q.createdAt;
          return ts >= start && ts <= end;
        })
        .reduce((sum, q) => sum + (q.amountPaid ?? 0), 0);
    };

    // ── Helper: sum rental revenue for a date range ───────────────────
    const rentalRevenue = async (start: number, end: number) => {
      const rentalOrgIds = organizationId ? [organizationId, userId] : [userId];
      for (const orgId of rentalOrgIds) {
        const rentals = await ctx.db
          .query("vehicle_rentals")
          .withIndex("by_organizationId_status", (q) => q.eq("organizationId", orgId))
          .collect();
        if (rentals.length > 0) {
          const filtered = rentals.filter(
            (r) => r.pickupDate >= start && r.pickupDate <= end,
          );
          // Rental amounts are stored in cents/halalas — divide by 100
          return (
            filtered
              .filter((r) => r.status === "active" || r.status === "completed")
              .reduce((sum, r) => sum + (r.totalAmount ?? 0), 0) / 100
          );
        }
      }
      return 0;
    };

    // ── Compute both periods ──────────────────────────────────────────
    const [curBooking, curQuotation, curRental, prevBooking, prevQuotation, prevRental] =
      await Promise.all([
        bookingRevenue(currentStart, currentEnd),
        quotationRevenue(currentStart, currentEnd),
        rentalRevenue(currentStart, currentEnd),
        bookingRevenue(previousStart, previousEnd),
        quotationRevenue(previousStart, previousEnd),
        rentalRevenue(previousStart, previousEnd),
      ]);

    const currentRevenue = curBooking + curQuotation + curRental;
    const previousRevenue = prevBooking + prevQuotation + prevRental;

    const percentageChange =
      previousRevenue > 0
        ? ((currentRevenue - previousRevenue) / previousRevenue) * 100
        : currentRevenue > 0
          ? 100
          : 0;

    return { currentRevenue, previousRevenue, percentageChange };
  },
});

/**
 * Get daily stats — group analytics events by date within a range.
 * Returns array of { date, pageViews, events } for charting.
 * Phase 8: Content, Careers, Real Estate, Analytics, Reviews
 */
export const getDailyStats = query({
  args: {
    userId: v.string(),
    startDate: v.number(),
    endDate: v.number(),
  },
  handler: async (ctx, { userId, startDate, endDate }) => {
    // Query analytics events for this user within the date range
    const events = await ctx.db
      .query("analytics_events")
      .withIndex("by_userId_createdAt", (q) =>
        q
          .eq("userId", userId)
          .gte("createdAt", startDate)
          .lte("createdAt", endDate),
      )
      .collect();

    // Group by date (YYYY-MM-DD)
    const dailyMap = new Map<string, { pageViews: number; events: number }>();

    for (const event of events) {
      const dateStr = new Date(event.createdAt).toISOString().split("T")[0];
      const existing = dailyMap.get(dateStr) ?? { pageViews: 0, events: 0 };

      existing.events += 1;
      if (event.category === "page_view" || event.eventName === "page_view") {
        existing.pageViews += 1;
      }

      dailyMap.set(dateStr, existing);
    }

    // Convert map to sorted array
    const result = Array.from(dailyMap.entries())
      .map(([date, stats]) => ({
        date,
        pageViews: stats.pageViews,
        events: stats.events,
      }))
      .sort((a, b) => a.date.localeCompare(b.date));

    return result;
  },
});
