"use server";

import { headers } from "next/headers";
import { initDb } from "@/lib/db";
import { getPublicSurveyBySlug } from "@/lib/queries/filler-queries";
import { submitFillerSchema } from "@/lib/validation/filler";
import { hashIp } from "@/lib/utils/ip-hash";
import {
  normalizeAnswerForStorage,
  validateFillerAnswer,
} from "@/lib/utils/filler-validation";
import { getAnsweredQuestionPath } from "@/lib/utils/survey-logic";
import type { ActionResult } from "@/lib/actions/auth";
import type { FillerQuestion, FillerAnswerValue } from "@/types/filler";

/** Submit a completed public survey response. */
export async function submitSurveyResponse(input: {
  slug: string;
  collectorId: string;
  surveyId: string;
  startedAt: string;
  answers: { questionId: string; value: unknown }[];
}): Promise<ActionResult> {
  const parsed = submitFillerSchema.safeParse(input);
  if (!parsed.success) {
    return { success: false, error: "Invalid submission" };
  }

  const surveyData = await getPublicSurveyBySlug(parsed.data.slug);
  if (
    !surveyData ||
    surveyData.surveyId !== parsed.data.surveyId ||
    surveyData.collectorId !== parsed.data.collectorId
  ) {
    return { success: false, error: "Survey not available" };
  }

  const { sequelize, Survey, Workspace, Respondent, Answer } = initDb();
  const questionMap = new Map(
    surveyData.questions.map((q) => [q.id, q])
  );

  const headerList = headers();
  const forwarded = headerList.get("x-forwarded-for");
  const ip = forwarded?.split(",")[0]?.trim() ?? "unknown";
  const userAgent = headerList.get("user-agent") ?? null;

  const answerMap = new Map<string, FillerAnswerValue>(
    parsed.data.answers.map((a) => [a.questionId, a.value as FillerAnswerValue])
  );

  const visiblePath = getAnsweredQuestionPath(surveyData.questions, answerMap);

  for (const question of visiblePath) {
    const value = answerMap.get(question.id) ?? null;
    const validation = validateFillerAnswer(question, value);
    if (!validation.valid) {
      return {
        success: false,
        error: validation.message ?? "Invalid or missing answer",
      };
    }
  }

  const submittedIds = new Set(parsed.data.answers.map((a) => a.questionId));
  const visibleIds = new Set(visiblePath.map((q) => q.id));
  for (const id of Array.from(submittedIds)) {
    if (!visibleIds.has(id)) {
      return { success: false, error: "Invalid submission" };
    }
  }

  const surveyRecord = await Survey.findByPk(parsed.data.surveyId);
  if (!surveyRecord) {
    return { success: false, error: "Survey not available" };
  }

  const started = new Date(parsed.data.startedAt);
  const completedAt = new Date();
  const timeToCompleteSeconds = Math.max(
    0,
    Math.round((completedAt.getTime() - started.getTime()) / 1000)
  );

  try {
    await sequelize.transaction(async (t) => {
      const ws = await Workspace.findByPk(surveyRecord.workspaceId, {
        transaction: t,
        lock: t.LOCK.UPDATE,
      });

      if (!ws || ws.isSuspended) {
        throw new Error("UNAVAILABLE");
      }

      if (ws.responseQuotaUsed >= ws.responseQuotaLimit) {
        throw new Error("QUOTA_EXCEEDED");
      }

      const respondent = await Respondent.create(
        {
          surveyId: parsed.data.surveyId,
          collectorId: parsed.data.collectorId,
          status: "completed",
          startedAt: started,
          completedAt,
          timeToCompleteSeconds,
          ipHash: hashIp(ip),
          userAgent,
        },
        { transaction: t }
      );

      for (const ans of parsed.data.answers) {
        const question = questionMap.get(ans.questionId) as
          | FillerQuestion
          | undefined;
        if (!question) continue;

        await Answer.create(
          {
            respondentId: respondent.id,
            questionId: ans.questionId,
            value: normalizeAnswerForStorage(
              question,
              ans.value as FillerAnswerValue
            ),
          },
          { transaction: t }
        );
      }

      await ws.increment("responseQuotaUsed", { transaction: t });
    });

    return { success: true };
  } catch (err) {
    if (err instanceof Error) {
      if (err.message === "QUOTA_EXCEEDED") {
        return { success: false, error: "Response limit reached for this survey" };
      }
      if (err.message === "UNAVAILABLE") {
        return { success: false, error: "Survey not available" };
      }
    }
    return { success: false, error: "Failed to save response" };
  }
}
