import "server-only";

import { Prisma } from "@prisma/client";
import { prisma } from "@/lib/prisma";

export type DiagnosticModuleProgress = {
  key: string;
  name: string;
  completion: number;
  answeredQuestions: number;
  totalQuestions: number;
  status: "Completado" | "En curso" | "Pendiente";
  resumeQuestionIndex: number;
};

type DiagnosticQuestion = {
  id: string;
  prompt: string;
  helpText: string | null;
  sortOrder: number;
  options: {
    id: string;
    label: string;
    sortOrder: number;
  }[];
  selectedAnswerOptionId: string | null;
  evidence: {
    notes: string;
    links: string[];
  } | null;
};

function parseResponseEvidence(rawValue: unknown): { notes: string; links: string[] } | null {
  if (!rawValue || typeof rawValue !== "object" || Array.isArray(rawValue)) {
    return null;
  }

  const value = rawValue as Record<string, unknown>;
  const notes = typeof value.notes === "string" ? value.notes.trim() : "";
  const links = Array.isArray(value.links)
    ? value.links
        .filter((entry): entry is string => typeof entry === "string")
        .map((entry) => entry.trim())
        .filter((entry) => entry.length > 0)
    : [];

  if (!notes && links.length === 0) {
    return null;
  }

  return {
    notes,
    links,
  };
}

function isLegacyResponseEvidenceSchemaError(error: unknown) {
  return error instanceof Prisma.PrismaClientKnownRequestError && (error.code === "P2021" || error.code === "P2022");
}

export async function getDiagnosticOverview(userId: string) {
  const modules = await prisma.diagnosticModule.findMany({
    orderBy: [{ sortOrder: "asc" }, { name: "asc" }],
    include: {
      questions: {
        orderBy: [{ sortOrder: "asc" }, { createdAt: "asc" }],
        select: {
          id: true,
        },
      },
    },
  });

  const allQuestionIds = modules.flatMap((module) => module.questions.map((question) => question.id));

  const responses = allQuestionIds.length
    ? await prisma.response.findMany({
        where: {
          userId,
          questionId: {
            in: allQuestionIds,
          },
        },
        select: {
          questionId: true,
        },
      })
    : [];

  const answeredQuestionIdSet = new Set(responses.map((response) => response.questionId));

  const modulesWithProgress: DiagnosticModuleProgress[] = modules.map((module) => {
    const questionIds = module.questions.map((question) => question.id);
    const totalQuestions = questionIds.length;

    const answeredQuestions = questionIds.reduce((total, questionId) => {
      return total + (answeredQuestionIdSet.has(questionId) ? 1 : 0);
    }, 0);

    const completion = totalQuestions > 0 ? Math.round((answeredQuestions / totalQuestions) * 100) : 0;

    let status: DiagnosticModuleProgress["status"] = "Pendiente";

    if (completion >= 100 && totalQuestions > 0) {
      status = "Completado";
    } else if (completion > 0) {
      status = "En curso";
    }

    const firstUnansweredIndex = questionIds.findIndex((questionId) => !answeredQuestionIdSet.has(questionId));
    const resumeQuestionIndex = firstUnansweredIndex >= 0 ? firstUnansweredIndex + 1 : Math.max(totalQuestions, 1);

    return {
      key: module.key,
      name: module.name,
      completion,
      answeredQuestions,
      totalQuestions,
      status,
      resumeQuestionIndex,
    };
  });

  const totalQuestions = modulesWithProgress.reduce((total, module) => total + module.totalQuestions, 0);
  const answeredQuestions = modulesWithProgress.reduce((total, module) => total + module.answeredQuestions, 0);
  const overallCompletion = totalQuestions > 0 ? Math.round((answeredQuestions / totalQuestions) * 100) : 0;

  const activeResumeModule =
    modulesWithProgress.find((module) => module.status === "En curso") ??
    modulesWithProgress.find((module) => module.status === "Pendiente") ??
    modulesWithProgress[0] ??
    null;

  const resumeHref = activeResumeModule
    ? `/diagnostic/${activeResumeModule.key}?q=${activeResumeModule.resumeQuestionIndex}`
    : null;

  return {
    modules: modulesWithProgress,
    stats: {
      modules: modulesWithProgress.length,
      completedModules: modulesWithProgress.filter((module) => module.status === "Completado").length,
      overallCompletion,
      answeredQuestions,
      totalQuestions,
    },
    resumeHref,
  };
}

export async function getDiagnosticModuleQuestions(userId: string, moduleKey: string) {
  const moduleRecord = await prisma.diagnosticModule.findUnique({
    where: { key: moduleKey },
    select: {
      id: true,
      key: true,
      name: true,
      description: true,
      questions: {
        orderBy: [{ sortOrder: "asc" }, { createdAt: "asc" }],
        select: {
          id: true,
          prompt: true,
          helpText: true,
          sortOrder: true,
          answerOptions: {
            orderBy: [{ sortOrder: "asc" }, { createdAt: "asc" }],
            select: {
              id: true,
              label: true,
              sortOrder: true,
            },
          },
        },
      },
    },
  });

  if (!moduleRecord) {
    return null;
  }

  const questionIds = moduleRecord.questions.map((question) => question.id);

  let responses: {
    questionId: string;
    answerOptionId: string;
    evidence?: unknown;
  }[] = [];

  if (questionIds.length) {
    try {
      responses = await prisma.response.findMany({
        where: {
          userId,
          questionId: {
            in: questionIds,
          },
        },
        select: {
          questionId: true,
          answerOptionId: true,
          evidence: true,
        },
      });
    } catch (error) {
      if (!isLegacyResponseEvidenceSchemaError(error)) {
        throw error;
      }

      responses = await prisma.response.findMany({
        where: {
          userId,
          questionId: {
            in: questionIds,
          },
        },
        select: {
          questionId: true,
          answerOptionId: true,
        },
      });
    }
  }

  const answerByQuestionId = new Map(responses.map((response) => [response.questionId, response.answerOptionId]));
  const evidenceByQuestionId = new Map(responses.map((response) => [response.questionId, parseResponseEvidence(response.evidence)]));

  const questions: DiagnosticQuestion[] = moduleRecord.questions.map((question) => ({
    id: question.id,
    prompt: question.prompt,
    helpText: question.helpText,
    sortOrder: question.sortOrder,
    options: question.answerOptions.map((option) => ({
      id: option.id,
      label: option.label,
      sortOrder: option.sortOrder,
    })),
    selectedAnswerOptionId: answerByQuestionId.get(question.id) ?? null,
    evidence: evidenceByQuestionId.get(question.id) ?? null,
  }));

  const answeredCount = questions.reduce((total, question) => total + (question.selectedAnswerOptionId ? 1 : 0), 0);
  const totalQuestions = questions.length;
  const completion = totalQuestions > 0 ? Math.round((answeredCount / totalQuestions) * 100) : 0;

  const firstUnansweredQuestionIndex = questions.findIndex((question) => !question.selectedAnswerOptionId);

  return {
    module: {
      key: moduleRecord.key,
      name: moduleRecord.name,
      description: moduleRecord.description,
    },
    questions,
    progress: {
      answeredCount,
      totalQuestions,
      completion,
      defaultQuestionIndex: firstUnansweredQuestionIndex >= 0 ? firstUnansweredQuestionIndex + 1 : Math.max(totalQuestions, 1),
    },
  };
}
