import { z } from 'zod';

import { baseListQuerySchema } from '@/lib/api/list-query';

/**
 * User-settable file categories (allow-list).
 *
 * M24: `category` is a free string used to gate the public, unauthenticated
 * stream-by-id surface (`/api/public/files/[id]` only serves rows where
 * `category === 'branding'`). When the upload/update API accepted an open
 * `z.string()`, any user with `files.upload` could self-set
 * `category='branding'` on their own file and make it publicly streamable +
 * CDN-cached 24h (a confused-deputy self-exposure). This allow-list reserves
 * the system-managed categories so the general upload/update path can never
 * set them.
 *
 * RESERVED (deliberately EXCLUDED) — never settable via the validated API:
 *   - 'branding' → public-stream gate (set only by the admin branding writers:
 *      `admin/settings/image` route + `logo.service.setPortLogo`, both of which
 *      bypass this schema)
 *   - 'avatar'   → system-managed user avatar (set only by `me/avatar`, which
 *      also bypasses this schema)
 */
export const USER_FILE_CATEGORIES = [
  'eoi',
  'contract',
  'image',
  'receipt',
  'correspondence',
  'misc',
  'other',
] as const;

export const userFileCategorySchema = z.enum(USER_FILE_CATEGORIES);

export const uploadFileSchema = z.object({
  filename: z.string().min(1).max(255),
  clientId: z.string().optional(),
  yachtId: z.string().optional(),
  companyId: z.string().optional(),
  folderId: z.string().uuid().optional(),
  category: userFileCategorySchema.optional(),
  entityType: z.string().optional(),
  entityId: z.string().optional(),
});

export const updateFileSchema = z.object({
  filename: z.string().min(1).max(255).optional(),
  category: userFileCategorySchema.optional(),
});

export const listFilesSchema = baseListQuerySchema
  .extend({
    clientId: z.string().optional(),
    yachtId: z.string().optional(),
    companyId: z.string().optional(),
    interestId: z.string().optional(),
    category: z.string().optional(),
    folderId: z
      .string()
      .uuid()
      .optional()
      .transform((v) => (v === '' ? null : v)),
    /** Entity-aggregated projection params - mutually exclusive with folderId.
     *  'interest' splits direct-attached into "This deal" (files.interestId
     *  matches) and "From client" (client-level + other deals); the other
     *  values fall through to the legacy symmetric-reach aggregator. */
    entityType: z.enum(['client', 'company', 'yacht', 'interest']).optional(),
    entityId: z.string().uuid().optional(),
  })
  .refine(
    (q) => !(q.folderId !== undefined && (q.entityType !== undefined || q.entityId !== undefined)),
    {
      message: 'folderId is mutually exclusive with entityType/entityId',
      path: ['folderId'],
    },
  )
  .refine((q) => Boolean(q.entityType) === Boolean(q.entityId), {
    message: 'entityType and entityId must be provided together',
    path: ['entityType'],
  });

// The API schema rejects the reserved categories ('branding'/'avatar') at
// parse time (M24). But the `uploadFile`/`updateFile` SERVICE is also called
// directly by the system writers (admin branding image, user avatar) that
// legitimately set those reserved categories and bypass the schema — so the
// service-facing input type keeps `category` as a plain string. Validation
// strictness lives in the schema; the type just must not block the system
// callers.
export type UploadFileInput = Omit<z.infer<typeof uploadFileSchema>, 'category'> & {
  category?: string;
};
export type UpdateFileInput = Omit<z.infer<typeof updateFileSchema>, 'category'> & {
  category?: string;
};
export type ListFilesInput = z.infer<typeof listFilesSchema>;
