import { DEFAULT_OPENAI_MODEL } from "@capakraken/shared";
import { TRPCError } from "@trpc/server";
import type {
  ChatCompletion,
  ChatCompletionCreateParamsNonStreaming,
} from "openai/resources/chat/completions/completions";
import { z } from "zod";
import { createAiClient, isAiConfigured, loggedAiCall, parseAiError } from "../ai-client.js";
import type { TRPCContext } from "../trpc.js";
import { buildInsightSnapshot, type InsightsDbAccess } from "./insights-anomalies.js";

type InsightsProcedureContext = Pick<TRPCContext, "db">;

export const projectNarrativeInputSchema = z.object({ projectId: z.string() });

type ProjectNarrativeInput = z.infer<typeof projectNarrativeInputSchema>;

/**
 * Count business days between two dates (Mon-Fri).
 */
export function countBusinessDays(start: Date, end: Date): number {
  let count = 0;
  const d = new Date(start);
  while (d <= end) {
    const dow = d.getDay();
    if (dow !== 0 && dow !== 6) count++;
    d.setDate(d.getDate() + 1);
  }
  return count;
}

export async function getAnomalyDetail(ctx: InsightsProcedureContext) {
  const snapshot = await buildInsightSnapshot(ctx.db as unknown as InsightsDbAccess);
  return {
    anomalies: snapshot.anomalies,
    count: snapshot.anomalies.length,
  };
}

export async function generateProjectNarrative(
  ctx: InsightsProcedureContext,
  input: ProjectNarrativeInput,
) {
  const [project, settings] = await Promise.all([
    ctx.db.project.findUnique({
      where: { id: input.projectId },
      include: {
        demandRequirements: {
          select: {
            id: true,
            role: true,
            headcount: true,
            hoursPerDay: true,
            startDate: true,
            endDate: true,
            status: true,
            _count: { select: { assignments: true } },
          },
        },
        assignments: {
          select: {
            id: true,
            role: true,
            hoursPerDay: true,
            startDate: true,
            endDate: true,
            status: true,
            dailyCostCents: true,
            resource: { select: { displayName: true } },
          },
        },
      },
    }),
    ctx.db.systemSettings.findUnique({ where: { id: "singleton" } }),
  ]);

  if (!project) {
    throw new TRPCError({ code: "NOT_FOUND", message: "Project not found" });
  }

  if (!settings || !isAiConfigured(settings)) {
    throw new TRPCError({
      code: "PRECONDITION_FAILED",
      message: "AI is not configured. Please set credentials in Admin → Settings.",
    });
  }

  const configuredSettings = settings;

  const now = new Date();
  const totalDays = countBusinessDays(project.startDate, project.endDate);
  const elapsedDays = countBusinessDays(
    project.startDate,
    now < project.endDate ? now : project.endDate,
  );
  const progressPercent = totalDays > 0 ? Math.round((elapsedDays / totalDays) * 100) : 0;

  const totalDemandHeadcount = project.demandRequirements.reduce((sum, demand) => sum + demand.headcount, 0);
  const filledDemandHeadcount = project.demandRequirements.reduce(
    (sum, demand) => sum + Math.min(demand._count.assignments, demand.headcount),
    0,
  );
  const staffingPercent = totalDemandHeadcount > 0
    ? Math.round((filledDemandHeadcount / totalDemandHeadcount) * 100)
    : 100;

  const totalCostCents = project.assignments.reduce((sum, assignment) => {
    const days = countBusinessDays(assignment.startDate, assignment.endDate);
    return sum + assignment.dailyCostCents * days;
  }, 0);

  const budgetCents = project.budgetCents;
  const budgetUsedPercent = budgetCents > 0 ? Math.round((totalCostCents / budgetCents) * 100) : 0;

  const overrunAssignments = project.assignments.filter(
    (assignment) => assignment.endDate > project.endDate,
  );

  const dataContext = [
    `Project: ${project.name} (${project.shortCode})`,
    `Status: ${project.status}`,
    `Timeline: ${project.startDate.toISOString().slice(0, 10)} to ${project.endDate.toISOString().slice(0, 10)} (${progressPercent}% elapsed)`,
    `Budget: ${(budgetCents / 100).toLocaleString("en-US", { style: "currency", currency: "EUR" })} | Estimated cost: ${(totalCostCents / 100).toLocaleString("en-US", { style: "currency", currency: "EUR" })} (${budgetUsedPercent}% of budget)`,
    `Staffing: ${filledDemandHeadcount}/${totalDemandHeadcount} positions filled (${staffingPercent}%)`,
    `Active assignments: ${project.assignments.filter((assignment) => assignment.status === "ACTIVE" || assignment.status === "CONFIRMED").length}`,
    overrunAssignments.length > 0
      ? `Timeline risk: ${overrunAssignments.length} assignment(s) extend beyond project end date`
      : "No timeline overruns detected",
  ].join("\n");

  const prompt = `Generate a concise executive summary for this project covering: budget status, staffing completeness, timeline risk, and key action items. Be specific with numbers. Keep it to 3-5 sentences.

${dataContext}`;

  const client = createAiClient(configuredSettings);
  const model = configuredSettings.azureOpenAiDeployment ?? DEFAULT_OPENAI_MODEL;
  const maxTokens = configuredSettings.aiMaxCompletionTokens ?? 300;
  const temperature = configuredSettings.aiTemperature ?? 1;
  const provider = configuredSettings.aiProvider ?? "openai";

  let narrative = "";
  try {
    const completionRequest: ChatCompletionCreateParamsNonStreaming = {
      messages: [
        {
          role: "system",
          content: "You are a project management analyst providing brief executive summaries. Be factual and action-oriented.",
        },
        { role: "user", content: prompt },
      ],
      max_completion_tokens: maxTokens,
      model,
      stream: false,
      ...(temperature !== 1 ? { temperature } : {}),
    };

    const completion = await loggedAiCall<ChatCompletion>(provider, model, prompt.length, () =>
      client.chat.completions.create(completionRequest),
    );
    narrative = completion.choices[0]?.message?.content?.trim() ?? "";
  } catch (error) {
    throw new TRPCError({
      code: "INTERNAL_SERVER_ERROR",
      message: `AI call failed: ${parseAiError(error)}`,
    });
  }

  if (!narrative) {
    throw new TRPCError({
      code: "INTERNAL_SERVER_ERROR",
      message: "AI returned an empty response.",
    });
  }

  const generatedAt = new Date().toISOString();
  const existingDynamic = (project.dynamicFields as Record<string, unknown>) ?? {};

  await ctx.db.project.update({
    where: { id: input.projectId },
    data: {
      dynamicFields: {
        ...existingDynamic,
        aiNarrative: narrative,
        aiNarrativeGeneratedAt: generatedAt,
      },
    },
  });

  return { narrative, generatedAt };
}

export async function detectAnomalies(ctx: InsightsProcedureContext) {
  const snapshot = await buildInsightSnapshot(ctx.db as unknown as InsightsDbAccess);
  return snapshot.anomalies;
}

export async function getInsightsSummary(ctx: InsightsProcedureContext) {
  const snapshot = await buildInsightSnapshot(ctx.db as unknown as InsightsDbAccess);
  return snapshot.summary;
}

export async function getCachedNarrative(
  ctx: InsightsProcedureContext,
  input: ProjectNarrativeInput,
) {
  const project = await ctx.db.project.findUnique({
    where: { id: input.projectId },
    select: { dynamicFields: true },
  });

  if (!project) {
    throw new TRPCError({ code: "NOT_FOUND", message: "Project not found" });
  }

  const dynamicFields = project.dynamicFields as Record<string, unknown> | null;
  const narrative = (dynamicFields?.aiNarrative as string) ?? null;
  const generatedAt = (dynamicFields?.aiNarrativeGeneratedAt as string) ?? null;
  return { narrative, generatedAt };
}
