import { eq, and, gte, lte, desc, sql } from "drizzle-orm";
import { npsResponses } from "../../db/schema";
import type { DrizzleDB } from "../../db/config/migrations";
import type { NewNPSResponse, NPSResponse } from "../../db/schema";

export type NPSScore = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10;

export interface NPSResult {
  score: number;
  promoters: number;
  passives: number;
  detractors: number;
  totalResponses: number;
  responseRate: number;
}

export function categorizeNPSScore(score: NPSScore): "detractor" | "passive" | "promoter" {
  if (score <= 6) return "detractor";
  if (score <= 8) return "passive";
  return "promoter";
}

export async function submitNPSResponse(
  db: DrizzleDB,
  input: {
    score: NPSScore;
    userId?: number;
    feedback?: string;
    surveyId?: string;
    respondentEmail?: string;
  }
): Promise<NPSResponse> {
  const category = categorizeNPSScore(input.score);

  const response: NewNPSResponse = {
    userId: input.userId ?? null,
    score: input.score,
    category,
    feedback: input.feedback ?? null,
    surveyId: input.surveyId ?? null,
    respondentEmail: input.respondentEmail ?? null,
  };

  const result = await db.insert(npsResponses).values(response).returning();
  return result[0]!;
}

export async function calculateNPS(
  db: DrizzleDB,
  periodStart?: Date,
  periodEnd?: Date
): Promise<NPSResult> {
  const conditions: import("drizzle-orm").SQL[] = [];

  if (periodStart) {
    conditions.push(gte(npsResponses.createdAt, periodStart));
  }
  if (periodEnd) {
    conditions.push(lte(npsResponses.createdAt, periodEnd));
  }

  const whereClause = conditions.length > 0 ? and(...conditions) : undefined;

  const responses = await db
    .select()
    .from(npsResponses)
    .where(whereClause)
    .orderBy(desc(npsResponses.createdAt));

  const promoters = responses.filter((r) => r.category === "promoter").length;
  const passives = responses.filter((r) => r.category === "passive").length;
  const detractors = responses.filter((r) => r.category === "detractor").length;
  const total = responses.length;

  const npsScore = total > 0 ? Math.round(((promoters - detractors) / total) * 100) : 0;

  return {
    score: npsScore,
    promoters,
    passives,
    detractors,
    totalResponses: total,
    responseRate: total > 0 ? promoters / total : 0,
  };
}

export async function getNPSResponses(
  db: DrizzleDB,
  category?: "detractor" | "passive" | "promoter",
  periodStart?: Date,
  periodEnd?: Date,
  limit = 50
): Promise<NPSResponse[]> {
  const conditions: import("drizzle-orm").SQL[] = [];

  if (category) {
    conditions.push(eq(npsResponses.category, category));
  }
  if (periodStart) {
    conditions.push(gte(npsResponses.createdAt, periodStart));
  }
  if (periodEnd) {
    conditions.push(lte(npsResponses.createdAt, periodEnd));
  }

  const whereClause = conditions.length > 0 ? and(...conditions) : undefined;

  const query = db.select().from(npsResponses).orderBy(desc(npsResponses.createdAt)).limit(limit);

  return whereClause ? await query.where(whereClause) : await query;
}

export async function getNPSOverTime(
  db: DrizzleDB,
  granularity: "weekly" | "monthly" = "weekly"
): Promise<Record<string, NPSResult>> {
  const responses = await db
    .select()
    .from(npsResponses)
    .orderBy(npsResponses.createdAt);

  const grouped: Record<string, NPSResponse[]> = {};

  for (const response of responses) {
    const date = response.createdAt;
    const key =
      granularity === "weekly"
        ? getWeekKey(date)
        : getMonthKey(date);

    if (!grouped[key]) grouped[key] = [];
    grouped[key].push(response);
  }

  const result: Record<string, NPSResult> = {};

  for (const [period, periodResponses] of Object.entries(grouped)) {
    const promoters = periodResponses.filter((r) => r.category === "promoter").length;
    const passives = periodResponses.filter((r) => r.category === "passive").length;
    const detractors = periodResponses.filter((r) => r.category === "detractor").length;
    const total = periodResponses.length;

    result[period] = {
      score: total > 0 ? Math.round(((promoters - detractors) / total) * 100) : 0,
      promoters,
      passives,
      detractors,
      totalResponses: total,
      responseRate: total > 0 ? promoters / total : 0,
    };
  }

  return result;
}

export function generateNPSSurveyEmail(
  surveyId: string,
  recipientEmail: string,
  surveyUrl: string
): string {
  return `
Hello,

We'd love to hear about your experience with Scripter. How likely are you to recommend us to a friend or colleague?

Rate us 0-10: ${surveyUrl}?email=${encodeURIComponent(recipientEmail)}&survey_id=${surveyId}

0 = Not at all likely
10 = Extremely likely

You can also share optional feedback to help us improve.

Thank you,
The FrenoCorp Team
`.trim();
}

export function generateNPSSurveyInAppPrompt(): { question: string; scale: string; options: string[] } {
  return {
    question: "How likely are you to recommend Scripter to a friend or colleague?",
    scale: "0-10",
    options: Array.from({ length: 11 }, (_, i) => i.toString()),
  };
}

function getWeekKey(date: Date): string {
  const start = new Date(date);
  start.setDate(start.getDate() - start.getDay());
  return start.toISOString().split("T")[0]!;
}

function getMonthKey(date: Date): string {
  return date.toISOString().slice(0, 7);
}
