import { and, count, eq, gte, isNull, lte, sql, sum } from 'drizzle-orm';

import { db } from '@/lib/db';
import { interests } from '@/lib/db/schema/interests';
import { berths } from '@/lib/db/schema/berths';
import { auditLogs } from '@/lib/db/schema/system';

// ─── Types ────────────────────────────────────────────────────────────────────

export interface PipelineData {
  stageCounts: Record<string, number>;
  topInterests: Array<{
    id: string;
    clientId: string;
    pipelineStage: string;
    berthPrice: string | null;
  }>;
  generatedAt: string;
}

export interface RevenueData {
  stageRevenue: Record<string, string>;
  totalCompleted: string;
  generatedAt: string;
}

export interface ActivityData {
  logs: Array<{
    id: string;
    action: string;
    entityType: string;
    entityId: string | null;
    userId: string | null;
    createdAt: Date;
  }>;
  summary: Record<string, number>;
  generatedAt: string;
}

export interface OccupancyData {
  statusCounts: Record<string, number>;
  occupancyRate: number;
  totalBerths: number;
  generatedAt: string;
}

// ─── Pipeline ─────────────────────────────────────────────────────────────────

export async function fetchPipelineData(
  portId: string,
  _params: Record<string, unknown>,
): Promise<PipelineData> {
  // Count interests per pipeline stage (non-archived)
  const stageCounts = await db
    .select({
      stage: interests.pipelineStage,
      count: count(),
    })
    .from(interests)
    .where(and(eq(interests.portId, portId), isNull(interests.archivedAt)));

  const stageCountMap: Record<string, number> = {};
  for (const row of stageCounts) {
    stageCountMap[row.stage] = row.count;
  }

  // Top 10 interests by berth price (via join)
  const topInterestsRows = await db
    .select({
      id: interests.id,
      clientId: interests.clientId,
      pipelineStage: interests.pipelineStage,
      berthPrice: berths.price,
    })
    .from(interests)
    .leftJoin(berths, eq(interests.berthId, berths.id))
    .where(and(eq(interests.portId, portId), isNull(interests.archivedAt)))
    .orderBy(sql`${berths.price} DESC NULLS LAST`)
    .limit(10);

  return {
    stageCounts: stageCountMap,
    topInterests: topInterestsRows.map((r) => ({
      id: r.id,
      clientId: r.clientId,
      pipelineStage: r.pipelineStage,
      berthPrice: r.berthPrice ? String(r.berthPrice) : null,
    })),
    generatedAt: new Date().toISOString(),
  };
}

// ─── Revenue ──────────────────────────────────────────────────────────────────

export async function fetchRevenueData(
  portId: string,
  _params: Record<string, unknown>,
): Promise<RevenueData> {
  // Sum berth prices grouped by pipeline stage
  const stageRevenue = await db
    .select({
      stage: interests.pipelineStage,
      revenue: sum(berths.price),
    })
    .from(interests)
    .leftJoin(berths, eq(interests.berthId, berths.id))
    .where(and(eq(interests.portId, portId), isNull(interests.archivedAt)))
    .groupBy(interests.pipelineStage);

  const stageRevenueMap: Record<string, string> = {};
  for (const row of stageRevenue) {
    stageRevenueMap[row.stage] = row.revenue ? String(row.revenue) : '0';
  }

  // Total revenue from completed interests
  const completedRevenue = await db
    .select({ total: sum(berths.price) })
    .from(interests)
    .leftJoin(berths, eq(interests.berthId, berths.id))
    .where(
      and(
        eq(interests.portId, portId),
        eq(interests.pipelineStage, 'completed'),
        isNull(interests.archivedAt),
      ),
    );

  return {
    stageRevenue: stageRevenueMap,
    totalCompleted: completedRevenue[0]?.total ? String(completedRevenue[0].total) : '0',
    generatedAt: new Date().toISOString(),
  };
}

// ─── Activity ─────────────────────────────────────────────────────────────────

export async function fetchActivityData(
  portId: string,
  params: Record<string, unknown>,
): Promise<ActivityData> {
  const dateFrom = params.dateFrom as string | undefined;
  const dateTo = params.dateTo as string | undefined;

  const thirtyDaysAgo = new Date();
  thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);

  const fromDate = dateFrom ? new Date(dateFrom) : thirtyDaysAgo;

  const conditions = [
    eq(auditLogs.portId, portId),
    gte(auditLogs.createdAt, fromDate),
  ];

  if (dateTo) {
    conditions.push(lte(auditLogs.createdAt, new Date(dateTo)));
  }

  const logs = await db
    .select({
      id: auditLogs.id,
      action: auditLogs.action,
      entityType: auditLogs.entityType,
      entityId: auditLogs.entityId,
      userId: auditLogs.userId,
      createdAt: auditLogs.createdAt,
    })
    .from(auditLogs)
    .where(and(...conditions))
    .orderBy(sql`${auditLogs.createdAt} DESC`)
    .limit(200);

  // Group by action type
  const summary: Record<string, number> = {};
  for (const log of logs) {
    const key = `${log.action}:${log.entityType}`;
    summary[key] = (summary[key] ?? 0) + 1;
  }

  return {
    logs,
    summary,
    generatedAt: new Date().toISOString(),
  };
}

// ─── Occupancy ────────────────────────────────────────────────────────────────

export async function fetchOccupancyData(
  portId: string,
  _params: Record<string, unknown>,
): Promise<OccupancyData> {
  const statusCounts = await db
    .select({
      status: berths.status,
      count: count(),
    })
    .from(berths)
    .where(eq(berths.portId, portId))
    .groupBy(berths.status);

  const statusCountMap: Record<string, number> = {};
  let totalBerths = 0;
  for (const row of statusCounts) {
    statusCountMap[row.status] = row.count;
    totalBerths += row.count;
  }

  const occupiedCount =
    (statusCountMap['under_offer'] ?? 0) + (statusCountMap['sold'] ?? 0);
  const occupancyRate = totalBerths > 0 ? (occupiedCount / totalBerths) * 100 : 0;

  return {
    statusCounts: statusCountMap,
    occupancyRate: Math.round(occupancyRate * 10) / 10,
    totalBerths,
    generatedAt: new Date().toISOString(),
  };
}
