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

import { db } from '@/lib/db';
import { redis } from '@/lib/redis';
import { interests, interestNotes } from '@/lib/db/schema/interests';
import { reminders } from '@/lib/db/schema/operations';
import { emailThreads } from '@/lib/db/schema/email';
import { logger } from '@/lib/logger';

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

export interface InterestScore {
  totalScore: number; // 0-100 (normalised)
  breakdown: {
    pipelineAge: number;     // 0-100
    stageSpeed: number;      // 0-100
    documentCompleteness: number; // 0-100
    engagement: number;      // 0-100
    berthLinked: number;     // 0 or 25
  };
  calculatedAt: Date;
}

// ─── Redis cache ──────────────────────────────────────────────────────────────

const SCORE_KEY = (interestId: string) => `interest-score:${interestId}`;
const SCORE_TTL = 3600; // 1 hour

// ─── Scoring helpers ──────────────────────────────────────────────────────────

function scorePipelineAge(createdAt: Date): number {
  const days = Math.floor((Date.now() - createdAt.getTime()) / (1000 * 60 * 60 * 24));
  if (days <= 30) return 100;
  if (days <= 60) return 80;
  if (days <= 90) return 60;
  if (days <= 180) return 40;
  return 20;
}

function scoreStageSpeed(createdAt: Date, pipelineStage: string): number {
  // Approximate stage index based on known pipeline order
  const STAGE_ORDER: Record<string, number> = {
    open: 0,
    details_sent: 1,
    in_communication: 2,
    visited: 3,
    signed_eoi_nda: 4,
    deposit_10pct: 5,
    contract: 6,
    completed: 7,
  };

  const stageIndex = STAGE_ORDER[pipelineStage] ?? 0;
  if (stageIndex === 0) {
    // Still at open — no progression
    return 0;
  }

  const daysSinceCreation = Math.max(
    1,
    (Date.now() - createdAt.getTime()) / (1000 * 60 * 60 * 24),
  );

  // Average days per stage transition
  const avgDaysPerStage = daysSinceCreation / stageIndex;

  // Thresholds: <7 days/stage = great, <14 = ok, <30 = slow, >=30 = cold
  if (avgDaysPerStage < 7) return 100;
  if (avgDaysPerStage < 14) return 75;
  if (avgDaysPerStage < 30) return 50;
  if (avgDaysPerStage < 60) return 25;
  return 10;
}

function scoreDocumentCompleteness(interest: {
  eoiStatus: string | null;
  contractStatus: string | null;
  depositStatus: string | null;
  dateEoiSigned: Date | null;
  dateContractSigned: Date | null;
  dateDepositReceived: Date | null;
}): number {
  let score = 0;

  // EOI signed
  if (interest.eoiStatus === 'signed' || interest.dateEoiSigned != null) {
    score += 30;
  }
  // Contract
  if (interest.contractStatus === 'signed' || interest.dateContractSigned != null) {
    score += 30;
  }
  // Deposit
  if (
    interest.depositStatus === 'received' ||
    interest.depositStatus === 'paid' ||
    interest.dateDepositReceived != null
  ) {
    score += 40;
  }

  return Math.min(score, 100);
}

// ─── Main scoring function ────────────────────────────────────────────────────

export async function calculateInterestScore(
  interestId: string,
  portId: string,
): Promise<InterestScore> {
  // Try cache first
  try {
    const cached = await redis.get(SCORE_KEY(interestId));
    if (cached) {
      const parsed = JSON.parse(cached) as InterestScore & { calculatedAt: string };
      return { ...parsed, calculatedAt: new Date(parsed.calculatedAt) };
    }
  } catch (err) {
    logger.warn({ err, interestId }, 'Redis cache read failed for interest score');
  }

  // Fetch interest
  const interest = await db.query.interests.findFirst({
    where: and(eq(interests.id, interestId), eq(interests.portId, portId)),
  });

  if (!interest) {
    throw new Error(`Interest not found: ${interestId}`);
  }

  // 1. Pipeline age
  const pipelineAge = scorePipelineAge(interest.createdAt);

  // 2. Stage speed
  const stageSpeed = scoreStageSpeed(interest.createdAt, interest.pipelineStage);

  // 3. Document completeness
  const documentCompleteness = scoreDocumentCompleteness(interest);

  // 4. Engagement — notes, emails, reminders in last 30 days
  const thirtyDaysAgo = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000);

  const [notesResult, remindersResult, emailResult] = await Promise.all([
    db
      .select({ value: count() })
      .from(interestNotes)
      .where(
        and(
          eq(interestNotes.interestId, interestId),
          gte(interestNotes.createdAt, thirtyDaysAgo),
        ),
      ),
    db
      .select({ value: count() })
      .from(reminders)
      .where(
        and(
          eq(reminders.interestId, interestId),
          eq(reminders.status, 'completed'),
          gte(reminders.completedAt, thirtyDaysAgo),
        ),
      ),
    db
      .select({ value: count() })
      .from(emailThreads)
      .where(
        and(
          eq(emailThreads.clientId, interest.clientId),
          eq(emailThreads.portId, portId),
          gte(emailThreads.lastMessageAt, thirtyDaysAgo),
        ),
      ),
  ]);

  const notesCount = notesResult[0]?.value ?? 0;
  const remindersCount = remindersResult[0]?.value ?? 0;
  const emailCount = emailResult[0]?.value ?? 0;

  const notesScore = Math.min(notesCount * 10, 50);
  const emailScore = Math.min(emailCount * 5, 30);
  const remindersScore = Math.min(remindersCount * 10, 20);
  const engagement = Math.min(notesScore + emailScore + remindersScore, 100);

  // 5. Berth linked
  const berthLinked = interest.berthId != null ? 25 : 0;

  // ── Normalise: max raw = 100+100+100+100+25 = 425 → /425 * 100 ──
  const RAW_MAX = 425;
  const rawTotal = pipelineAge + stageSpeed + documentCompleteness + engagement + berthLinked;
  const totalScore = Math.round((rawTotal / RAW_MAX) * 100);

  const result: InterestScore = {
    totalScore,
    breakdown: {
      pipelineAge,
      stageSpeed,
      documentCompleteness,
      engagement,
      berthLinked,
    },
    calculatedAt: new Date(),
  };

  // Write to cache (fire-and-forget)
  redis
    .setex(SCORE_KEY(interestId), SCORE_TTL, JSON.stringify(result))
    .catch((err) => logger.warn({ err, interestId }, 'Redis cache write failed for interest score'));

  return result;
}

// ─── Bulk scoring ─────────────────────────────────────────────────────────────

export async function calculateBulkScores(
  portId: string,
): Promise<Array<{ interestId: string; score: InterestScore }>> {
  const allInterests = await db
    .select({ id: interests.id })
    .from(interests)
    .where(and(eq(interests.portId, portId), isNull(interests.archivedAt)));

  const results = await Promise.allSettled(
    allInterests.map(async (i) => {
      const score = await calculateInterestScore(i.id, portId);
      return { interestId: i.id, score };
    }),
  );

  return results
    .filter((r): r is PromiseFulfilledResult<{ interestId: string; score: InterestScore }> =>
      r.status === 'fulfilled',
    )
    .map((r) => r.value);
}
