import { and, count, desc, eq, isNull, sql } from 'drizzle-orm';

import { db } from '@/lib/db';
import { clients } from '@/lib/db/schema/clients';
import { interests } from '@/lib/db/schema/interests';
import { berths } from '@/lib/db/schema/berths';
import { systemSettings, auditLogs } from '@/lib/db/schema/system';
import { PIPELINE_STAGES } from '@/lib/constants';

// ─── Default pipeline weights ────────────────────────────────────────────────

const DEFAULT_PIPELINE_WEIGHTS: Record<string, number> = {
  open: 0.05,
  details_sent: 0.10,
  in_communication: 0.20,
  visited: 0.35,
  signed_eoi_nda: 0.50,
  deposit_10pct: 0.70,
  contract: 0.90,
  completed: 1.00,
};

// ─── KPIs ─────────────────────────────────────────────────────────────────────

export async function getKpis(portId: string) {
  const [totalClientsRow] = await db
    .select({ value: count() })
    .from(clients)
    .where(and(eq(clients.portId, portId), isNull(clients.archivedAt)));

  const [activeInterestsRow] = await db
    .select({ value: count() })
    .from(interests)
    .where(and(eq(interests.portId, portId), isNull(interests.archivedAt)));

  // Pipeline value: SUM berths.price via JOIN from non-archived interests with berthId
  const pipelineRows = await db
    .select({ price: berths.price })
    .from(interests)
    .innerJoin(berths, eq(interests.berthId, berths.id))
    .where(
      and(
        eq(interests.portId, portId),
        isNull(interests.archivedAt),
        sql`${interests.berthId} IS NOT NULL`,
      ),
    );

  const pipelineValueUsd = pipelineRows.reduce((acc, row) => {
    return acc + (row.price ? parseFloat(String(row.price)) : 0);
  }, 0);

  // Occupancy rate: (sold + under_offer) / total * 100
  const allBerthsRows = await db
    .select({ status: berths.status })
    .from(berths)
    .where(eq(berths.portId, portId));

  const totalBerths = allBerthsRows.length;
  const occupiedBerths = allBerthsRows.filter(
    (b) => b.status === 'sold' || b.status === 'under_offer',
  ).length;
  const occupancyRate = totalBerths > 0 ? (occupiedBerths / totalBerths) * 100 : 0;

  return {
    totalClients: totalClientsRow?.value ?? 0,
    activeInterests: activeInterestsRow?.value ?? 0,
    pipelineValueUsd,
    occupancyRate,
  };
}

// ─── Pipeline Counts ──────────────────────────────────────────────────────────

export async function getPipelineCounts(portId: string) {
  const rows = await db
    .select({
      stage: interests.pipelineStage,
      count: sql<number>`count(*)::int`,
    })
    .from(interests)
    .where(and(eq(interests.portId, portId), isNull(interests.archivedAt)))
    .groupBy(interests.pipelineStage);

  const countsByStage = Object.fromEntries(rows.map((r) => [r.stage, r.count]));

  return PIPELINE_STAGES.map((stage) => ({
    stage,
    count: countsByStage[stage] ?? 0,
  }));
}

// ─── Revenue Forecast ─────────────────────────────────────────────────────────

export async function getRevenueForecast(portId: string) {
  // Load weights from systemSettings
  let weights: Record<string, number> = DEFAULT_PIPELINE_WEIGHTS;
  let weightsSource: 'db' | 'default' = 'default';

  const settingRow = await db.query.systemSettings.findFirst({
    where: and(
      eq(systemSettings.key, 'pipeline_weights'),
      eq(systemSettings.portId, portId),
    ),
  });

  if (settingRow?.value) {
    try {
      const parsed = settingRow.value as Record<string, number>;
      if (typeof parsed === 'object' && parsed !== null) {
        weights = parsed;
        weightsSource = 'db';
      }
    } catch {
      // Fall through to defaults
    }
  }

  // Fetch all non-archived interests with a linked berth and its price
  const interestRows = await db
    .select({
      id: interests.id,
      pipelineStage: interests.pipelineStage,
      berthPrice: berths.price,
    })
    .from(interests)
    .innerJoin(berths, eq(interests.berthId, berths.id))
    .where(
      and(
        eq(interests.portId, portId),
        isNull(interests.archivedAt),
        sql`${interests.berthId} IS NOT NULL`,
      ),
    );

  // Build stageBreakdown
  const stageMap: Record<string, { count: number; weightedValue: number }> = {};

  for (const row of interestRows) {
    const stage = row.pipelineStage ?? 'open';
    const price = row.berthPrice ? parseFloat(String(row.berthPrice)) : 0;
    const weight = weights[stage] ?? 0;
    const weighted = price * weight;

    if (!stageMap[stage]) {
      stageMap[stage] = { count: 0, weightedValue: 0 };
    }
    stageMap[stage]!.count += 1;
    stageMap[stage]!.weightedValue += weighted;
  }

  const stageBreakdown = PIPELINE_STAGES.map((stage) => ({
    stage,
    count: stageMap[stage]?.count ?? 0,
    weightedValue: stageMap[stage]?.weightedValue ?? 0,
  }));

  const totalWeightedValue = stageBreakdown.reduce(
    (acc, s) => acc + s.weightedValue,
    0,
  );

  return {
    totalWeightedValue,
    stageBreakdown,
    weightsSource,
  };
}

// ─── Recent Activity ──────────────────────────────────────────────────────────

export async function getRecentActivity(portId: string, limit = 20) {
  const rows = await db
    .select({
      id: auditLogs.id,
      action: auditLogs.action,
      entityType: auditLogs.entityType,
      entityId: auditLogs.entityId,
      userId: auditLogs.userId,
      metadata: auditLogs.metadata,
      createdAt: auditLogs.createdAt,
    })
    .from(auditLogs)
    .where(eq(auditLogs.portId, portId))
    .orderBy(desc(auditLogs.createdAt))
    .limit(limit);

  return rows;
}
