import "server-only";

import { createHash } from "crypto";
import { AiSuggestionStatus, type Prisma } from "@prisma/client";
import { prisma } from "@/lib/prisma";
import type { AiConfidence, AiDecision, AiEnvelope, AiSuggestionStatus as AiSuggestionStatusView } from "@/lib/ai/types";

type CreateAiSuggestionInput = {
  userId: string;
  moduleKey: string;
  featureKey: string;
  subjectType: string;
  subjectId: string;
  inputForHash: unknown;
  requestJson: unknown;
  responseJson: unknown;
  confidence: AiConfidence | number | null;
  engine: string;
  model: string | null;
  usageJson: unknown;
  warningsJson: unknown;
  promptVersion: string;
};

function stableStringify(value: unknown): string {
  if (value === null || value === undefined) {
    return "null";
  }

  if (typeof value === "string") {
    return JSON.stringify(value);
  }

  if (typeof value === "number" || typeof value === "boolean") {
    return String(value);
  }

  if (Array.isArray(value)) {
    return `[${value.map((item) => stableStringify(item)).join(",")}]`;
  }

  if (typeof value === "object") {
    const record = value as Record<string, unknown>;
    const keys = Object.keys(record).sort();
    return `{${keys.map((key) => `${JSON.stringify(key)}:${stableStringify(record[key])}`).join(",")}}`;
  }

  return JSON.stringify(String(value));
}

function normalizeConfidence(value: AiConfidence | number | null): number | null {
  if (typeof value === "number" && Number.isFinite(value)) {
    if (value >= 0 && value <= 1) {
      return value;
    }

    if (value >= 0 && value <= 100) {
      return value / 100;
    }

    return null;
  }

  if (value === "high") {
    return 0.9;
  }

  if (value === "medium") {
    return 0.65;
  }

  if (value === "low") {
    return 0.35;
  }

  return null;
}

function toJsonValue(value: unknown): Prisma.InputJsonValue {
  return (value ?? null) as Prisma.InputJsonValue;
}

export function createAiInputHash(input: unknown): string {
  return createHash("sha256").update(stableStringify(input)).digest("hex");
}

export async function storeAiSuggestion(input: CreateAiSuggestionInput) {
  const inputHash = createAiInputHash(input.inputForHash);

  const existing = await prisma.aiSuggestion.findFirst({
    where: {
      userId: input.userId,
      moduleKey: input.moduleKey,
      featureKey: input.featureKey,
      subjectType: input.subjectType,
      subjectId: input.subjectId,
      inputHash,
    },
  });

  if (existing) {
    return {
      suggestionId: existing.id,
      inputHash,
      status: existing.status as AiSuggestionStatusView,
      reused: true,
    };
  }

  const created = await prisma.aiSuggestion.create({
    data: {
      userId: input.userId,
      moduleKey: input.moduleKey,
      featureKey: input.featureKey,
      subjectType: input.subjectType,
      subjectId: input.subjectId,
      inputHash,
      requestJson: toJsonValue(input.requestJson),
      responseJson: toJsonValue(input.responseJson),
      confidence: normalizeConfidence(input.confidence),
      engine: input.engine,
      model: input.model,
      usageJson: toJsonValue(input.usageJson),
      warningsJson: toJsonValue(input.warningsJson),
      promptVersion: input.promptVersion,
      status: AiSuggestionStatus.GENERATED,
    },
    select: {
      id: true,
      status: true,
    },
  });

  return {
    suggestionId: created.id,
    inputHash,
    status: created.status as AiSuggestionStatusView,
    reused: false,
  };
}

export async function storeAiSuggestionFromEnvelope<T>(
  input: Omit<CreateAiSuggestionInput, "requestJson" | "responseJson" | "confidence" | "engine" | "model" | "usageJson" | "warningsJson" | "promptVersion"> & {
    envelope: AiEnvelope<T>;
    responsePayload: unknown;
  },
) {
  return storeAiSuggestion({
    userId: input.userId,
    moduleKey: input.moduleKey,
    featureKey: input.featureKey,
    subjectType: input.subjectType,
    subjectId: input.subjectId,
    inputForHash: input.inputForHash,
    requestJson: input.envelope.requestJson,
    responseJson: input.responsePayload,
    confidence: input.envelope.confidence,
    engine: input.envelope.engine,
    model: input.envelope.model,
    usageJson: input.envelope.usage,
    warningsJson: input.envelope.warnings,
    promptVersion: input.envelope.promptVersion,
  });
}

export async function applyAiSuggestionDecision(userId: string, suggestionId: string, decision: AiDecision) {
  const suggestion = await prisma.aiSuggestion.findFirst({
    where: {
      id: suggestionId,
      userId,
    },
    select: {
      id: true,
      status: true,
    },
  });

  if (!suggestion) {
    return null;
  }

  const status: AiSuggestionStatus = decision === "accept" ? AiSuggestionStatus.ACCEPTED : AiSuggestionStatus.DISMISSED;

  const updated = await prisma.aiSuggestion.update({
    where: { id: suggestionId },
    data: {
      status,
      actedAt: new Date(),
    },
    select: {
      id: true,
      status: true,
      actedAt: true,
      updatedAt: true,
    },
  });

  return {
    id: updated.id,
    status: updated.status as AiSuggestionStatusView,
    actedAt: updated.actedAt?.toISOString() ?? null,
    updatedAt: updated.updatedAt.toISOString(),
  };
}
