"use server";

import { revalidatePath } from "next/cache";
import { getServerSession } from "next-auth";
import { Op } from "sequelize";
import { z } from "zod";
import { authOptions } from "@/lib/auth/auth-options";
import { initDb } from "@/lib/db";
import { getActiveParticipantProfile } from "@/lib/auth/require-participant";
import { FEATURES } from "@/config/features";
import {
  applyEdges,
  lockDomesticState,
  refillQueue,
} from "@/lib/domestic/feed-engine";
import { validateDomesticAnswer } from "@/lib/domestic/rules";
import {
  CONSENT_KEYS,
  getActiveConsentRecordId,
} from "@/lib/participant/consent";
import type { ActionResult } from "@/lib/actions/auth";

const answerSchema = z.object({
  queueItemId: z.string().uuid(),
  idempotencyKey: z.string().min(8).max(100),
  value: z.unknown(),
});

const skipSchema = z.object({ queueItemId: z.string().uuid() });

class FeedError extends Error {}

async function participantForAction() {
  if (!FEATURES.participantPlatform || !FEATURES.domesticFeed) return null;
  const session = await getServerSession(authOptions);
  if (!session?.user?.id) return null;
  return getActiveParticipantProfile(session.user.id);
}

/**
 * Append an answer for the exact version served, evaluate branching edges
 * and refill the queue — all in one transaction. Re-submitting the same
 * idempotency key is a no-op.
 */
export async function submitDomesticAnswer(input: {
  queueItemId: string;
  idempotencyKey: string;
  value: unknown;
}): Promise<ActionResult<{ unlocked: number; duplicate: boolean }>> {
  const profile = await participantForAction();
  if (!profile) return { success: false, error: "Participant profile not active" };

  const parsed = answerSchema.safeParse(input);
  if (!parsed.success) return { success: false, error: "Invalid answer" };
  const { queueItemId, idempotencyKey, value } = parsed.data;

  const {
    sequelize,
    DomesticAnswer,
    DomesticQuestion,
    DomesticQuestionVersion,
    ParticipantQuestionQueue,
  } = initDb();

  try {
    const result = await sequelize.transaction(async (t) => {
      const existing = await DomesticAnswer.findOne({
        where: { idempotencyKey },
        transaction: t,
      });
      if (existing) {
        if (existing.participantProfileId !== profile.id) {
          throw new FeedError("Invalid answer");
        }
        return { unlocked: 0, duplicate: true };
      }

      const consentRecordId = await getActiveConsentRecordId(
        profile.id,
        CONSENT_KEYS.domesticFeed,
        t
      );
      if (!consentRecordId) {
        throw new FeedError("Turn on Nonstop survey answers in Profile & Privacy first");
      }

      const state = await lockDomesticState(profile.id, t);

      const item = await ParticipantQuestionQueue.findOne({
        where: {
          id: queueItemId,
          participantProfileId: profile.id,
          status: { [Op.in]: ["queued", "served"] },
        },
        transaction: t,
        lock: t.LOCK.UPDATE,
      });
      if (!item) throw new FeedError("This question is no longer available");

      const [question, version] = await Promise.all([
        DomesticQuestion.findByPk(item.domesticQuestionId, { transaction: t }),
        DomesticQuestionVersion.findByPk(item.questionVersionId, { transaction: t }),
      ]);
      if (!question || !version || question.status !== "published") {
        await item.update({ status: "expired", activeKey: null }, { transaction: t });
        throw new FeedError("This question is no longer available");
      }

      const validated = validateDomesticAnswer(
        version.type,
        version.optionsConfig,
        value
      );
      if (!validated.ok) throw new FeedError(validated.error);

      const now = new Date();
      await item.update(
        { status: "answered", answeredAt: now, activeKey: null },
        { transaction: t }
      );

      const suppressed = new Set(state.suppressedQuestionIds ?? []);
      const before = await ParticipantQuestionQueue.count({
        where: { participantProfileId: profile.id, reason: "branch" },
        transaction: t,
      });
      const appliedEdgeIds = await applyEdges(
        profile.id,
        version,
        validated.value,
        suppressed,
        t
      );
      const after = await ParticipantQuestionQueue.count({
        where: { participantProfileId: profile.id, reason: "branch" },
        transaction: t,
      });

      const priorRevisions = await DomesticAnswer.count({
        where: {
          participantProfileId: profile.id,
          domesticQuestionId: question.id,
        },
        transaction: t,
      });

      await DomesticAnswer.create(
        {
          participantProfileId: profile.id,
          domesticQuestionId: question.id,
          questionVersionId: version.id,
          queueItemId: item.id,
          value: validated.value,
          revision: priorRevisions + 1,
          consentRecordId,
          source: "feed",
          idempotencyKey,
          appliedEdgeIds,
          answeredAt: now,
        },
        { transaction: t }
      );

      await state.update(
        {
          completedCount: state.completedCount + 1,
          queueRevision: state.queueRevision + 1,
          lastAnsweredAt: now,
          suppressedQuestionIds: Array.from(suppressed),
        },
        { transaction: t }
      );

      await refillQueue(profile.id, t, Array.from(suppressed));

      return { unlocked: Math.max(0, after - before), duplicate: false };
    });

    revalidatePath("/participant");
    revalidatePath("/participant/domestic");
    revalidatePath("/participant/activity");
    return { success: true, data: result };
  } catch (err) {
    if (err instanceof FeedError) return { success: false, error: err.message };
    return { success: false, error: "Could not save your answer. Please try again." };
  }
}

export async function skipDomesticQuestion(input: {
  queueItemId: string;
}): Promise<ActionResult> {
  const profile = await participantForAction();
  if (!profile) return { success: false, error: "Participant profile not active" };

  const parsed = skipSchema.safeParse(input);
  if (!parsed.success) return { success: false, error: "Invalid request" };

  const { sequelize, ParticipantQuestionQueue } = initDb();

  try {
    await sequelize.transaction(async (t) => {
      const state = await lockDomesticState(profile.id, t);
      const item = await ParticipantQuestionQueue.findOne({
        where: {
          id: parsed.data.queueItemId,
          participantProfileId: profile.id,
          status: { [Op.in]: ["queued", "served"] },
        },
        transaction: t,
        lock: t.LOCK.UPDATE,
      });
      if (!item) throw new FeedError("This question is no longer available");

      await item.update({ status: "skipped", activeKey: null }, { transaction: t });
      await state.update(
        {
          skippedCount: state.skippedCount + 1,
          queueRevision: state.queueRevision + 1,
        },
        { transaction: t }
      );
      await refillQueue(profile.id, t, state.suppressedQuestionIds ?? []);
    });
  } catch (err) {
    if (err instanceof FeedError) return { success: false, error: err.message };
    return { success: false, error: "Could not skip this question" };
  }

  revalidatePath("/participant/domestic");
  return { success: true };
}
