"use server";

import { revalidatePath } from "next/cache";
import { initDb } from "@/lib/db";
import { requireClientWorkspace } from "@/lib/auth/require-auth";
import {
  createSurveySchema,
  updateSurveySchema,
  surveyStatusTransitionSchema,
} from "@/lib/validation/survey";
import { publishSurveySchema } from "@/lib/validation/question";
import { getScopedSurvey } from "@/lib/queries/survey-queries";
import { getBuilderSurvey } from "@/lib/queries/builder-queries";
import { slugify, randomSlugSuffix } from "@/lib/utils/slugify";
import { normalizeDisplayConfig } from "@/lib/utils/survey-display";
import { normalizeThankYouConfig } from "@/lib/utils/thank-you-screen";
import type { ActionResult } from "@/lib/actions/auth";

function revalidateSurveyPaths() {
  revalidatePath("/app/dashboard");
  revalidatePath("/app/surveys");
}

/** Create a new draft survey in the user's workspace. */
export async function createSurvey(
  input?: { title?: string }
): Promise<ActionResult<{ surveyId: string }>> {
  const { userId, workspaceId } = await requireClientWorkspace();

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

  const { Survey, AuditLog } = initDb();

  const survey = await Survey.create({
    workspaceId,
    title: parsed.data.title ?? "Untitled Survey",
    status: "draft",
    createdBy: userId,
    welcomeScreenConfig: {
      title: parsed.data.title ?? "Untitled Survey",
      description: "Help us improve. This survey takes about 2 minutes.",
      buttonLabel: "Start Survey",
    },
  });

  await AuditLog.create({
    actorUserId: userId,
    workspaceId,
    action: "survey.created",
    metadata: { surveyId: survey.id },
  });

  revalidateSurveyPaths();
  return { success: true, data: { surveyId: survey.id } };
}

/** Rename or update survey metadata. */
export async function updateSurvey(
  surveyId: string,
  input: { title?: string; description?: string | null }
): Promise<ActionResult> {
  const { userId, workspaceId } = await requireClientWorkspace();

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

  const survey = await getScopedSurvey(surveyId, workspaceId);
  if (!survey) {
    return { success: false, error: "Survey not found" };
  }

  await survey.update({
    ...(parsed.data.title !== undefined && { title: parsed.data.title }),
    ...(parsed.data.description !== undefined && {
      description: parsed.data.description,
    }),
  });

  const { AuditLog } = initDb();
  await AuditLog.create({
    actorUserId: userId,
    workspaceId,
    action: "survey.updated",
    metadata: { surveyId },
  });

  revalidateSurveyPaths();
  return { success: true };
}

/** Transition survey status (draft → active → closed). */
export async function transitionSurveyStatus(
  surveyId: string,
  status: "draft" | "active" | "closed"
): Promise<ActionResult> {
  const { userId, workspaceId } = await requireClientWorkspace();

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

  const survey = await getScopedSurvey(surveyId, workspaceId);
  if (!survey) {
    return { success: false, error: "Survey not found" };
  }

  const updates: {
    status: "draft" | "active" | "closed";
    publishedAt?: Date;
    closedAt?: Date | null;
  } = { status };

  if (status === "active" && survey.status === "draft") {
    updates.publishedAt = new Date();
  }
  if (status === "closed") {
    updates.closedAt = new Date();
  }
  if (status === "active" && survey.status === "closed") {
    updates.closedAt = null;
  }

  await survey.update(updates);

  const { AuditLog } = initDb();
  await AuditLog.create({
    actorUserId: userId,
    workspaceId,
    action: "survey.status_changed",
    metadata: { surveyId, status },
  });

  revalidateSurveyPaths();
  return { success: true };
}

/** Duplicate a survey, its questions, and skip logic rules. */
export async function duplicateSurvey(
  surveyId: string
): Promise<ActionResult<{ surveyId: string }>> {
  const { userId, workspaceId } = await requireClientWorkspace();

  const builderData = await getBuilderSurvey(surveyId, workspaceId);
  if (!builderData) {
    return { success: false, error: "Survey not found" };
  }

  const { sequelize, Survey, Question, LogicRule, AuditLog } = initDb();

  try {
    const newSurveyId = await sequelize.transaction(async (t) => {
      const source = await Survey.findOne({
        where: { id: surveyId, workspaceId },
        transaction: t,
      });
      if (!source) throw new Error("NOT_FOUND");

      const copy = await Survey.create(
        {
          workspaceId,
          title: `${source.title} (Copy)`,
          description: source.description,
          status: "draft",
          welcomeScreenConfig: source.welcomeScreenConfig,
          thankYouScreenConfig: source.thankYouScreenConfig,
          themeConfig: source.themeConfig,
          displayConfig: source.displayConfig,
          createdBy: userId,
        },
        { transaction: t }
      );

      const idMap = new Map<string, string>();

      for (const q of builderData.questions) {
        const newQuestion = await Question.create(
          {
            surveyId: copy.id,
            type: q.type,
            title: q.title,
            position: q.position,
            isRequired: q.isRequired,
            validationConfig: q.validationConfig,
            optionsConfig: q.optionsConfig,
            randomizeOptions: q.randomizeOptions,
          },
          { transaction: t }
        );
        idMap.set(q.id, newQuestion.id);
      }

      for (const q of builderData.questions) {
        const newQuestionId = idMap.get(q.id);
        if (!newQuestionId) continue;

        for (const rule of q.logicRules) {
          await LogicRule.create(
            {
              questionId: newQuestionId,
              conditionType: rule.conditionType,
              conditionValue: rule.conditionValue,
              action: rule.action,
              targetQuestionId: rule.targetQuestionId
                ? idMap.get(rule.targetQuestionId) ?? null
                : null,
            },
            { transaction: t }
          );
        }
      }

      const sourceDisplay = normalizeDisplayConfig(source.displayConfig);
      const sourceThankYou = normalizeThankYouConfig(source.thankYouScreenConfig);
      await copy.update(
        {
          displayConfig: {
            layout: sourceDisplay.layout,
            pageBreakAfterIds: sourceDisplay.pageBreakAfterIds
              .map((id) => idMap.get(id))
              .filter((id): id is string => Boolean(id)),
          },
          thankYouScreenConfig: {
            ...sourceThankYou,
            variants: (sourceThankYou.variants ?? [])
              .map((variant) => {
                const questionId = idMap.get(variant.questionId);
                if (!questionId) return null;
                return { ...variant, questionId };
              })
              .filter((v): v is NonNullable<typeof v> => Boolean(v)),
          },
        },
        { transaction: t }
      );

      await AuditLog.create(
        {
          actorUserId: userId,
          workspaceId,
          action: "survey.duplicated",
          metadata: { sourceSurveyId: surveyId, newSurveyId: copy.id },
        },
        { transaction: t }
      );

      return copy.id;
    });

    revalidateSurveyPaths();
    return { success: true, data: { surveyId: newSurveyId } };
  } catch (err) {
    if (err instanceof Error && err.message === "NOT_FOUND") {
      return { success: false, error: "Survey not found" };
    }
    return { success: false, error: "Failed to duplicate survey" };
  }
}

/** Delete a survey from the workspace. */
export async function deleteSurvey(surveyId: string): Promise<ActionResult> {
  const { userId, workspaceId } = await requireClientWorkspace();

  const survey = await getScopedSurvey(surveyId, workspaceId);
  if (!survey) {
    return { success: false, error: "Survey not found" };
  }

  await survey.destroy();

  const { AuditLog } = initDb();
  await AuditLog.create({
    actorUserId: userId,
    workspaceId,
    action: "survey.deleted",
    metadata: { surveyId, title: survey.title },
  });

  revalidateSurveyPaths();
  return { success: true };
}

/** Publish survey: activate + create default web_link collector. */
export async function publishSurvey(
  surveyId: string
): Promise<ActionResult<{ slug: string }>> {
  const { userId, workspaceId } = await requireClientWorkspace();
  const parsed = publishSurveySchema.safeParse({ surveyId });
  if (!parsed.success) {
    return { success: false, error: "Invalid input" };
  }

  const survey = await getScopedSurvey(surveyId, workspaceId);
  if (!survey) return { success: false, error: "Survey not found" };

  const { sequelize, Collector, AuditLog } = initDb();

  try {
    const slug = await sequelize.transaction(async (t) => {
      await survey.update(
        {
          status: "active",
          publishedAt: survey.publishedAt ?? new Date(),
        },
        { transaction: t }
      );

      const existing = await Collector.findOne({
        where: { surveyId, type: "web_link" },
        transaction: t,
      });

      if (existing) return existing.slug;

      const baseSlug = slugify(survey.title) || "survey";
      const newSlug = `${baseSlug}-${randomSlugSuffix()}`;

      await Collector.create(
        {
          surveyId,
          type: "web_link",
          slug: newSlug,
          isActive: true,
          config: {},
        },
        { transaction: t }
      );

      return newSlug;
    });

    await AuditLog.create({
      actorUserId: userId,
      workspaceId,
      action: "survey.published",
      metadata: { surveyId, slug },
    });

    revalidateSurveyPaths();
    revalidatePath(`/app/surveys/${surveyId}/build`);
    revalidatePath(`/app/surveys/${surveyId}/distribute`);

    return { success: true, data: { slug } };
  } catch {
    return { success: false, error: "Failed to publish survey" };
  }
}
