import { z } from "zod";
import { FLOW_STEP_ORDER } from "../../../app/(app)/create/utils/flowSteps";
import { customMethodCardFieldBlocksByIdSchema } from "../../../lib/create/customMethodCardFieldBlocks";
import {
  CUSTOM_METHOD_CARD_WIZARD_MAX_DESCRIPTION_CHARS,
  CUSTOM_METHOD_CARD_WIZARD_MAX_FIELD_CHARS,
} from "../../../lib/create/customMethodCardWizardConstants";
import { EMAIL_MAX_LEN } from "../../../lib/create/isValidCreateFlowSaveEmail";
import { MAX_STAKEHOLDER_EMAILS } from "../../../lib/create/stakeholderLimits";
import { assertPlainJsonValue, DEFAULT_PLAIN_JSON_LIMITS } from "./plainJson";

const flowStepTuple = FLOW_STEP_ORDER as unknown as [string, ...string[]];

const createFlowStepSchema = z.enum(flowStepTuple);

const communityStructureChipSnapshotRowSchema = z.object({
  id: z.string().max(200),
  label: z.string().max(2000),
  state: z.string().max(32).optional(),
});

const communityStructureChipSnapshotsSchema = z
  .object({
    organizationTypes: z.array(communityStructureChipSnapshotRowSchema).optional(),
    scale: z.array(communityStructureChipSnapshotRowSchema).optional(),
    maturity: z.array(communityStructureChipSnapshotRowSchema).optional(),
  })
  .strict();

const coreValueDetailEntrySchema = z.object({
  meaning: z.string().max(8000),
  signals: z.string().max(8000),
  supportText: z.string().max(2000).optional(),
});

/**
 * Final-review edit modal details per group, merged onto preset defaults at
 * publish time. Shapes mirror the custom-rule add-method modals.
 */
const communicationMethodDetailEntrySchema = z.object({
  corePrinciple: z.string().max(8000),
  logisticsAdmin: z.string().max(8000),
  codeOfConduct: z.string().max(8000),
});

const membershipMethodDetailEntrySchema = z.object({
  eligibility: z.string().max(8000),
  joiningProcess: z.string().max(8000),
  expectations: z.string().max(8000),
});

const decisionApproachDetailEntrySchema = z.object({
  corePrinciple: z.string().max(8000),
  applicableScope: z.array(z.string().max(2000)).max(50),
  selectedApplicableScope: z.array(z.string().max(2000)).max(50),
  stepByStepInstructions: z.string().max(8000),
  consensusLevel: z.number().int().min(0).max(100).optional(),
  objectionsDeadlocks: z.string().max(8000),
});

const conflictManagementDetailEntrySchema = z.object({
  corePrinciple: z.string().max(8000),
  applicableScope: z.array(z.string().max(2000)).max(50),
  selectedApplicableScope: z.array(z.string().max(2000)).max(50),
  processProtocol: z.string().max(8000),
  restorationFallbacks: z.string().max(8000),
});

const customMethodCardMetaEntrySchema = z.object({
  label: z.string().max(CUSTOM_METHOD_CARD_WIZARD_MAX_FIELD_CHARS),
  supportText: z.string().max(CUSTOM_METHOD_CARD_WIZARD_MAX_DESCRIPTION_CHARS),
});

/** Normalized (trim + lowercase) stakeholder email for drafts + publish. */
const stakeholderEmailSchema = z
  .string()
  .max(320)
  .transform((s) => s.trim().toLowerCase())
  .pipe(z.string().email());

export { MAX_STAKEHOLDER_EMAILS } from "../../../lib/create/stakeholderLimits";

/**
 * Published rule `document` column: arbitrary JSON object with safety bounds.
 */
export const publishedRuleDocumentSchema = z
  .record(z.string(), z.unknown())
  .superRefine((doc, ctx) => {
    const err = assertPlainJsonValue(doc, 0, DEFAULT_PLAIN_JSON_LIMITS);
    if (err) {
      ctx.addIssue({
        code: z.ZodIssueCode.custom,
        message: err,
      });
    }
  });

/**
 * Create-flow draft payload: known optional fields plus passthrough for future steps.
 * Full tree must satisfy {@link assertPlainJsonValue}.
 */
export const createFlowStateSchema = z
  .object({
    title: z.string().max(500).optional(),
    summary: z.string().max(8000).optional(),
    communityContext: z.string().max(200).optional(),
    communitySaveEmail: z.string().max(320).optional(),
    communityAvatarUrl: z.string().max(512).optional(),
    selectedCommunitySizeIds: z.array(z.string()).optional(),
    selectedOrganizationTypeIds: z.array(z.string()).optional(),
    selectedScaleIds: z.array(z.string()).optional(),
    selectedMaturityIds: z.array(z.string()).optional(),
    communityStructureChipSnapshots:
      communityStructureChipSnapshotsSchema.optional(),
    selectedCoreValueIds: z.array(z.string()).max(200).optional(),
    coreValuesChipsSnapshot: z
      .array(communityStructureChipSnapshotRowSchema)
      .optional(),
    coreValueDetailsByChipId: z
      .record(coreValueDetailEntrySchema)
      .optional(),
    selectedCommunicationMethodIds: z.array(z.string()).max(200).optional(),
    selectedMembershipMethodIds: z.array(z.string()).max(200).optional(),
    selectedDecisionApproachIds: z.array(z.string()).max(200).optional(),
    selectedDecisionKeyResourceIds: z.array(z.string().max(80)).max(20).optional(),
    selectedConflictManagementIds: z.array(z.string()).max(200).optional(),
    communicationMethodDetailsById: z
      .record(communicationMethodDetailEntrySchema)
      .optional(),
    membershipMethodDetailsById: z
      .record(membershipMethodDetailEntrySchema)
      .optional(),
    decisionApproachDetailsById: z
      .record(decisionApproachDetailEntrySchema)
      .optional(),
    conflictManagementDetailsById: z
      .record(conflictManagementDetailEntrySchema)
      .optional(),
    customMethodCardMetaById: z
      .record(z.string().max(80), customMethodCardMetaEntrySchema)
      .optional(),
    customMethodCardFieldBlocksById: customMethodCardFieldBlocksByIdSchema,
    methodSectionsPinCommitted: z
      .object({
        communication: z.boolean().optional(),
        membership: z.boolean().optional(),
        decisionApproaches: z.boolean().optional(),
        conflictManagement: z.boolean().optional(),
      })
      .strict()
      .optional(),
    pendingTemplateAction: z
      .object({
        slug: z.string().max(200),
        mode: z.enum(["customize", "useWithoutChanges"]),
      })
      .strict()
      .optional(),
    templateReviewBackSlug: z.string().max(200).optional(),
    templateReviewEntryFromCreateFlow: z.boolean().optional(),
    editingPublishedRuleId: z.string().max(200).optional(),
    currentStep: createFlowStepSchema.optional(),
    sections: z.array(z.unknown()).optional(),
    stakeholderEmails: z
      .array(stakeholderEmailSchema)
      .max(MAX_STAKEHOLDER_EMAILS)
      .optional(),
  })
  .passthrough()
  .superRefine((data, ctx) => {
    const err = assertPlainJsonValue(data, 0, DEFAULT_PLAIN_JSON_LIMITS);
    if (err) {
      ctx.addIssue({ code: z.ZodIssueCode.custom, message: err });
    }
  });

export const publishRuleBodySchema = z.object({
  title: z
    .string()
    .max(500)
    .transform((s) => s.trim())
    .refine((s) => s.length > 0, { message: "title required" }),
  summary: z
    .union([z.string().max(8000), z.null()])
    .optional()
    .transform((val) => {
      if (val === undefined || val === null) {
        return null;
      }
      const t = val.trim();
      return t.length > 0 ? t : null;
    }),
  document: publishedRuleDocumentSchema,
  stakeholderEmails: z
    .array(stakeholderEmailSchema)
    .max(MAX_STAKEHOLDER_EMAILS)
    .optional(),
});

export type PublishRuleBody = z.infer<typeof publishRuleBodySchema>;

export const postRuleStakeholderBodySchema = z.object({
  email: stakeholderEmailSchema,
});

export type PostRuleStakeholderBody = z.infer<
  typeof postRuleStakeholderBodySchema
>;

/** Dedupe and drop the publisher’s own email (`emails` need not be pre-normalized). */
export function uniqueStakeholderEmailsForPublish(
  emails: string[] | undefined,
  publisherEmailNormalized: string,
): string[] {
  if (!emails?.length) return [];
  const pub = publisherEmailNormalized.trim().toLowerCase();
  const seen = new Set<string>();
  const out: string[] = [];
  for (const raw of emails) {
    const e = raw.trim().toLowerCase();
    if (e === pub || seen.has(e)) continue;
    seen.add(e);
    out.push(e);
  }
  return out;
}

export const putDraftBodySchema = z.object({
  payload: createFlowStateSchema,
});
export type CreateFlowStateValidated = z.infer<typeof createFlowStateSchema>;

export const magicLinkRequestBodySchema = z.object({
  email: z.string().max(EMAIL_MAX_LEN),
  next: z.string().optional(),
  draft: createFlowStateSchema.optional(),
});
export type MagicLinkRequestBody = z.infer<typeof magicLinkRequestBodySchema>;
