/**
 * Zod schemas for admin API routes and workflows.
 *
 * All admin API endpoints validate inputs using these schemas before passing
 * to workflow functions. No raw admin logic lives in the route handlers.
 */

import { z } from 'zod';
import { PAYMENT_STATUS } from '@/lib/enums/payment-status';
import { groupDealState } from '@/lib/enums/group-deal-state';
import { redemptionStatus } from '@/lib/enums/redemption-status';

// ─── Deal approval ────────────────────────────────────────────────────────────

export const approveDealBodySchema = z.object({
  note: z.string().trim().max(2000).optional(),
});

export type ApproveDealBody = z.infer<typeof approveDealBodySchema>;

export const rejectDealBodySchema = z.object({
  reason: z.enum([
    'WRONG_PRICE',
    'MISSING_BAD_IMAGE',
    'CATEGORY_MISMATCH',
    'CONTENT_POLICY',
    'UNCLEAR_DESCRIPTION',
    'OTHER',
  ]),
  detail: z
    .string()
    .trim()
    .max(2000)
    .optional()
    .transform((v) => (v?.length ? v : undefined)),
});

export type RejectDealBody = z.infer<typeof rejectDealBodySchema>;

export const listDealsQuerySchema = z.object({
  limit: z.coerce.number().int().min(1).max(100).default(20),
  offset: z.coerce.number().int().min(0).default(0),
  state: z
    .enum([
      'DRAFT',
      'UNDER_REVIEW',
      'PENDING_APPROVAL',
      'ACTIVE',
      'PAUSED',
      'SOLD_OUT',
      'EXPIRED',
      'REJECTED',
    ])
    .optional(),
  vendorSearch: z.string().trim().max(200).optional(),
  search: z.string().trim().max(200).optional(),
});

export type ListDealsQuery = z.infer<typeof listDealsQuerySchema>;

// ─── Vendor management ────────────────────────────────────────────────────────

export const upgradeVendorBodySchema = z.object({
  tier: z.enum(['NEW', 'VETERAN']),
  note: z.string().trim().max(2000).optional(),
});

export type UpgradeVendorBody = z.infer<typeof upgradeVendorBodySchema>;

export const activateVendorBodySchema = z.object({
  note: z.string().trim().max(2000).optional(),
});

export type ActivateVendorBody = z.infer<typeof activateVendorBodySchema>;

export const freezeVendorBodySchema = z.object({
  reason: z.string().trim().min(1).max(2000),
  durationDays: z.number().int().min(1).max(3650).optional(),
});

export type FreezeVendorBody = z.infer<typeof freezeVendorBodySchema>;

export const unfreezeVendorBodySchema = z.object({
  note: z.string().trim().max(2000).optional(),
});

export type UnfreezeVendorBody = z.infer<typeof unfreezeVendorBodySchema>;

export const banVendorBodySchema = z.object({
  reason: z.string().trim().min(1).max(2000),
});

export type BanVendorBody = z.infer<typeof banVendorBodySchema>;

export const sendVendorMessageBodySchema = z.object({
  subject: z.string().trim().min(1).max(200),
  body: z.string().trim().min(1).max(5000),
});

export type SendVendorMessageBody = z.infer<typeof sendVendorMessageBodySchema>;

const vendorStateFilter = z.enum([
  'PENDING_FIRST_APPROVAL',
  'PENDING_PROCESSOR',
  'ACTIVE',
  'VETERAN',
  'FROZEN',
  'BANNED',
  'REJECTED',
  'FLAGGED',
]);

export const listVendorsQuerySchema = z.object({
  // Accepts a single state or a comma-separated list (e.g. "FROZEN,BANNED");
  // listVendors splits on ',' downstream. FLAGGED is a virtual filter (not a real
  // accountState enum label), so it is only valid on its own — never inside a list,
  // which would reach the inArray branch and throw a Postgres enum error.
  state: z
    .string()
    .refine(
      (s) => {
        const parts = s.split(',');
        if (parts.length === 1) return vendorStateFilter.safeParse(parts[0]).success;
        return parts.every((v) => v !== 'FLAGGED' && vendorStateFilter.safeParse(v).success);
      },
      { message: 'Invalid vendor state' },
    )
    .optional(),
  tier: z.enum(['NEW', 'VETERAN']).optional(),
  search: z.string().trim().max(200).optional(),
  from: z.iso.datetime({ offset: true }).optional(),
  to: z.iso.datetime({ offset: true }).optional(),
  hideNoDeals: z.coerce.boolean().default(true),
  limit: z.coerce.number().int().min(1).max(100).default(12),
  offset: z.coerce.number().int().min(0).default(0),
});

export type ListVendorsQuery = z.infer<typeof listVendorsQuerySchema>;

// ─── Approve anyway ──────────────────────────────────────────────────────────

export const approveAnywayBodySchema = z.object({
  note: z.string().trim().max(2000).optional(),
});

export type ApproveAnywayBody = z.infer<typeof approveAnywayBodySchema>;

// ─── Review removal queue ─────────────────────────────────────────────────────

export const listRemovalsQuerySchema = z.object({
  limit: z.coerce.number().int().min(1).max(100).default(20),
  offset: z.coerce.number().int().min(0).default(0),
  status: z.enum(['PENDING', 'APPROVED', 'REJECTED']).default('PENDING'),
});

export type ListRemovalsQuery = z.infer<typeof listRemovalsQuerySchema>;

// ─── Earnings report ──────────────────────────────────────────────────────────

const earningsSortBySchema = z.enum([
  'vendorBusinessName',
  'monthYearMonth',
  'purchaseCount',
  'grossAgorot',
  'vendorAmountAgorot',
  'platformFeeAgorot',
  'refundedAgorot',
]);

export const earningsReportQuerySchema = z.object({
  vendorId: z.uuid().optional(),
  from: z.iso.date().optional(),
  to: z.iso.date().optional(),
  search: z.string().trim().min(1).max(200).optional(),
  sortBy: earningsSortBySchema.optional(),
  sortDir: z.enum(['asc', 'desc']).optional(),
  limit: z.coerce.number().int().min(1).max(200).default(50),
  offset: z.coerce.number().int().min(0).default(0),
  export: z.enum(['csv']).optional(),
});

export type EarningsReportQuery = z.infer<typeof earningsReportQuerySchema>;

// ─── Purchase management ──────────────────────────────────────────────────────

export const listPurchasesQuerySchema = z.object({
  paymentStatus: z.enum(PAYMENT_STATUS).optional(),
  redemptionStatus: redemptionStatus.schema.optional(),
  vendorId: z.uuid().optional(),
  from: z.iso.date().optional(),
  to: z.iso.date().optional(),
  limit: z.coerce.number().int().min(1).max(100).default(20),
  offset: z.coerce.number().int().min(0).default(0),
});

export type ListPurchasesQuery = z.infer<typeof listPurchasesQuerySchema>;

// ─── Purchase actions ─────────────────────────────────────────────────────────

export const refundPurchaseBodySchema = z.object({
  /** Partial refund amount in agorot (integer). Omit for full refund. */
  amountAgorot: z.number().int().positive().optional(),
  note: z.string().trim().max(2000).optional(),
});

export type RefundPurchaseBody = z.infer<typeof refundPurchaseBodySchema>;

export const resendReceiptBodySchema = z.object({
  // No additional body fields required; purchaseId comes from URL param
});

export type ResendReceiptBody = z.infer<typeof resendReceiptBodySchema>;

// ─── Report tickets ───────────────────────────────────────────────────────────

export const createReportBodySchema = z.object({
  targetType: z.enum(['VENDOR', 'DEAL', 'REVIEW', 'USER']),
  targetId: z.uuid(),
  reason: z.enum(['FACTUAL_ERROR', 'MISLEADING', 'WRONG_HOURS', 'BUG', 'OTHER']),
  body: z.string().trim().max(5000).default(''),
});

export type CreateReportBody = z.infer<typeof createReportBodySchema>;

export const resolveReportBodySchema = z.object({
  resolution: z.string().trim().min(1).max(2000),
});

export type ResolveReportBody = z.infer<typeof resolveReportBodySchema>;

export const dismissReportBodySchema = z.object({
  note: z.string().trim().max(2000).optional(),
});

export type DismissReportBody = z.infer<typeof dismissReportBodySchema>;

// ─── Unified approval queue ───────────────────────────────────────────────────

export const listApprovalQueueQuerySchema = z.object({
  type: z.enum(['all', 'deal', 'image', 'review', 'report', 'job']).default('all'),
  limit: z.coerce.number().int().min(1).max(100).default(50),
  offset: z.coerce.number().int().min(0).default(0),
});

export type ListApprovalQueueQuery = z.infer<typeof listApprovalQueueQuerySchema>;

// ─── Image moderation ─────────────────────────────────────────────────────────

export const listImagesQuerySchema = z.object({
  scanStatus: z.enum(['PENDING', 'CLEAN', 'INFECTED', 'ERROR']).optional(),
  approvalStatus: z.enum(['PENDING', 'APPROVED', 'REJECTED']).optional(),
  limit: z.coerce.number().int().min(1).max(100).default(20),
  offset: z.coerce.number().int().min(0).default(0),
});

export type ListImagesQuery = z.infer<typeof listImagesQuerySchema>;

export const listReportsQuerySchema = z.object({
  status: z.enum(['OPEN', 'INVESTIGATING', 'RESOLVED', 'DISMISSED']).optional(),
  targetType: z.enum(['VENDOR', 'DEAL', 'REVIEW', 'USER']).optional(),
  limit: z.coerce.number().int().min(1).max(100).default(20),
  offset: z.coerce.number().int().min(0).default(0),
});

export type ListReportsQuery = z.infer<typeof listReportsQuerySchema>;

// ─── User management ──────────────────────────────────────────────────────────

const csv = (vals: readonly [string, ...string[]]) =>
  z
    .string()
    .transform((s) => s.split(',').filter(Boolean))
    .pipe(z.array(z.enum(vals)))
    .optional();

export const listUsersQuerySchema = z.object({
  states: csv(['ACTIVE', 'FROZEN', 'DELETED_PENDING', 'DELETED']),
  roles: csv(['admin', 'vendor', 'affiliate', 'customer']),
  search: z.string().trim().max(200).optional(),
  joinedFrom: z
    .string()
    .regex(/^\d{4}-\d{2}-\d{2}$/)
    .optional(),
  joinedTo: z
    .string()
    .regex(/^\d{4}-\d{2}-\d{2}$/)
    .optional(),
  purchaseActivity: z.enum(['has', 'none']).optional(),
  minPurchases: z.coerce.number().int().min(0).optional(),
  hasEmail: z
    .enum(['true', 'false'])
    .transform((v) => v === 'true')
    .optional(),
  emailVerified: z
    .enum(['true', 'false'])
    .transform((v) => v === 'true')
    .optional(),
  hasShippingAddress: z
    .enum(['true', 'false'])
    .transform((v) => v === 'true')
    .optional(),
  cityCode: z.string().trim().max(20).optional(),
  sort: z.enum(['joined_desc', 'joined_asc', 'purchases_desc', 'name_asc']).default('joined_desc'),
  limit: z.coerce.number().int().min(1).max(100).default(20),
  offset: z.coerce.number().int().min(0).default(0),
});

export type ListUsersQuery = z.infer<typeof listUsersQuerySchema>;

export const userActionBodySchema = z.object({
  reason: z.string().trim().min(1).max(2000),
});

export type UserActionBody = z.infer<typeof userActionBodySchema>;

export const setAdminFlagBodySchema = z.object({
  isAdmin: z.boolean(),
  reason: z.string().trim().max(2000).optional(),
});

export type SetAdminFlagBody = z.infer<typeof setAdminFlagBodySchema>;

// ─── Outbox monitor ───────────────────────────────────────────────────────────

export const listOutboxQuerySchema = z.object({
  status: z.enum(['pending', 'processed', 'failed', 'abandoned']).optional(),
  limit: z.coerce.number().int().min(1).max(100).default(20),
  offset: z.coerce.number().int().min(0).default(0),
});

export type ListOutboxQuery = z.infer<typeof listOutboxQuerySchema>;

export const retryOutboxBodySchema = z.object({
  // Event ID comes from URL param; no additional body fields required
});

export type RetryOutboxBody = z.infer<typeof retryOutboxBodySchema>;

// ─── Audit trail ─────────────────────────────────────────────────────────────

export const listAuditQuerySchema = z.object({
  export: z.literal('csv').optional(),
  targetType: z
    .enum(['DEAL', 'VENDOR', 'REVIEW', 'USER', 'PURCHASE', 'KB_ARTICLE', 'SETTING', 'OUTBOX_EVENT'])
    .optional(),
  action: z
    .enum([
      'APPROVE',
      'REJECT',
      'FREEZE',
      'UNFREEZE',
      'BAN',
      'REMOVE_REVIEW',
      'RESTORE_REVIEW',
      'ADD_QUANTITY',
      'GRANT_ADMIN',
      'REVOKE_ADMIN',
      'REQUEST_REFUND',
      'RESEND_RECEIPT',
      'KB_CREATE',
      'KB_UPDATE',
      'KB_DELETE',
      'SETTING_UPDATE',
      'OUTBOX_RETRY',
    ])
    .optional(),
  adminId: z.uuid().optional(),
  adminSearch: z.string().trim().max(200).optional(),
  from: z.iso.date().optional(),
  to: z.iso.date().optional(),
  limit: z.coerce.number().int().min(1).max(100).default(20),
  offset: z.coerce.number().int().min(0).default(0),
});

export type ListAuditQuery = z.infer<typeof listAuditQuerySchema>;

// ─── LLM job list ─────────────────────────────────────────────────────────────

export const listLlmJobsQuerySchema = z.object({
  status: z.enum(['PENDING', 'RUNNING', 'COMPLETED', 'FAILED', 'TIMED_OUT']).optional(),
  jobType: z
    .enum(['DEAL_MODERATION', 'IMAGE_APPROVAL', 'REVIEW_PRESCORING', 'VENDOR_VIOLATION'])
    .optional(),
  limit: z.coerce.number().int().min(1).max(100).default(25),
  offset: z.coerce.number().int().min(0).default(0),
});

export type ListLlmJobsQuery = z.infer<typeof listLlmJobsQuerySchema>;

// ─── System health ────────────────────────────────────────────────────────────

export const rearmEntityBodySchema = z.object({
  entityType: z.enum(['deal', 'group_deal', 'personal_offer', 'scheduled_publish', 'gold_window']),
  entityId: z.string().min(1).max(500),
});

export type RearmEntityBody = z.infer<typeof rearmEntityBodySchema>;

// ─── Settlement oversight ─────────────────────────────────────────────────────

export const listSettlementsQuerySchema = z.object({
  /** Filter by settlement status. 'all' returns both pending and paid. */
  status: z.enum(['pending', 'paid', 'all']).default('all'),
  /** ISO date (YYYY-MM-DD) lower bound on purchase createdAt. */
  from: z.iso.date().optional(),
  /** ISO date (YYYY-MM-DD) upper bound on purchase createdAt. */
  to: z.iso.date().optional(),
  limit: z.coerce.number().int().min(1).max(100).default(20),
  offset: z.coerce.number().int().min(0).default(0),
});

export type ListSettlementsQuery = z.infer<typeof listSettlementsQuerySchema>;

// ─── Group deal oversight ──────────────────────────────────────────────────────

export const listGroupDealsQuerySchema = z.object({
  // 'all' is a filter sentinel; union with canonical schema to avoid re-listing values.
  state: groupDealState.schema.or(z.literal('all')).default('all'),
  limit: z.coerce.number().int().min(1).max(100).default(20),
  offset: z.coerce.number().int().min(0).default(0),
});

export type ListGroupDealsQuery = z.infer<typeof listGroupDealsQuerySchema>;
