import { z } from "zod";

export const HumanSchema = z.object({
  id: z.string().nullable(),
  created_at: z.coerce.date().nullable(),
  email: z.string().email(),
  email_prefix: z.string(),
});

export type Human = z.infer<typeof HumanSchema>;

export const NetworkSchema = z.object({
  id: z.string(),
  name: z.string(),
  admin_human: HumanSchema,
  humans: z.array(HumanSchema),
  created_at: z.coerce.date(),
});

export type Network = z.infer<typeof NetworkSchema>;

export const ListNetworksResponseSchema = z.array(NetworkSchema);
export type ListNetworksResponse = z.infer<typeof ListNetworksResponseSchema>;

// --- Network request/response types ---

const CreateNetworkRequestSchema = z.object({
  name: z.string(),
});
export type CreateNetworkRequest = z.infer<typeof CreateNetworkRequestSchema>;

const AddMembersRequestSchema = z.object({
  email_addresses: z.array(z.string().email()),
});
export type AddMembersRequest = z.infer<typeof AddMembersRequestSchema>;

// --- Depot types ---

const PrepareUploadRequestSchema = z.object({
  network_id: z.string(),
  name: z.string(),
  content_type: z.string(),
  content_length: z.number(),
});
export type PrepareUploadRequest = z.infer<typeof PrepareUploadRequestSchema>;

export const PrepareUploadResponseSchema = z.object({
  object_id: z.string(),
  upload_url: z.string(),
  upload_headers: z.record(z.string(), z.string()),
});
export type PrepareUploadResponse = z.infer<typeof PrepareUploadResponseSchema>;

export const DepotObjectSchema = z.object({
  id: z.string(),
  name: z.string(),
  content_type: z.string(),
  content_length: z.number(),
  contains_content: z.boolean(),
  created_at: z.coerce.date(),
});
export type DepotObject = z.infer<typeof DepotObjectSchema>;

// --- Particle property schemas ---

export const StreamPropertiesSchema = z.object({
  name: z.string(),
  status: z.enum(["open", "closed"]),
  description: z.string().optional(),
});
export type StreamProperties = z.infer<typeof StreamPropertiesSchema>;

export const FolderPropertiesSchema = z.object({
  name: z.string(),
  color: z.string().optional(),
});
export type FolderProperties = z.infer<typeof FolderPropertiesSchema>;

export const MediaPropertiesSchema = z.object({
  object_id: z.string(),
  mime_type: z.string(),
  duration_ms: z.number(),
  size_bytes: z.number(),
});
export type MediaProperties = z.infer<typeof MediaPropertiesSchema>;

export const FilePropertiesSchema = z.object({
  object_id: z.string(),
  filename: z.string(),
  mime_type: z.string(),
  size_bytes: z.number(),
});
export type FileProperties = z.infer<typeof FilePropertiesSchema>;

export const TextPropertiesSchema = z.object({
  content: z.string(),
});
export type TextProperties = z.infer<typeof TextPropertiesSchema>;

export const QuestPropertiesSchema = z.object({
  title: z.string(),
  description: z.string(),
  status: z.string().optional(),
  assigned_to: z.string().email().optional(),
});
export type QuestProperties = z.infer<typeof QuestPropertiesSchema>;

export const PaperPropertiesSchema = z.object({
  title: z.string(),
  content: z.string(),
});
export type PaperProperties = z.infer<typeof PaperPropertiesSchema>;

export interface ParticlePropertiesMap {
  stream: StreamProperties;
  folder: FolderProperties;
  media: MediaProperties;
  file: FileProperties;
  text: TextProperties;
  quest: QuestProperties;
  paper: PaperProperties;
}

// --- Unified Particle types ---

const ParticleBaseSchema = z.object({
  id: z.string(),
  created_at: z.coerce.date(),
  created_by_email: z.string().email(),
  updated_at: z.coerce.date().optional(),
});

export const ParticleSchema = z.discriminatedUnion("type", [
  ParticleBaseSchema.extend({
    type: z.literal("stream"), properties: StreamPropertiesSchema,
    // e.g. ["human:aron@acme.com", "human:john@acme.com"] - visible only to Aron and John
    // e.g. ["network:123"] - visible to everyone in the network
    visible_to: z.array(z.string()),
    // Marks human_id to their `playback_position_at`: where they left off in a conversation
    playback_markers: z.record(z.string(), z.coerce.date()).optional(),
    // Timestamp of the most recent child particle
    //   used for sorting streams by recent activity without needing to query subcollections
    last_child_created_at: z.coerce.date().optional(),
  }),
  ParticleBaseSchema.extend({
    type: z.literal("folder"), properties: FolderPropertiesSchema,
    // e.g. ["human:aron@acme.com", "human:john@acme.com"] - visible only to Aron and John
    // e.g. ["network:123"] - visible to everyone in the network
    visible_to: z.array(z.string()),
  }),
  ParticleBaseSchema.extend({ type: z.literal("media"), properties: MediaPropertiesSchema }),
  ParticleBaseSchema.extend({ type: z.literal("file"), properties: FilePropertiesSchema }),
  ParticleBaseSchema.extend({ type: z.literal("text"), properties: TextPropertiesSchema }),
  ParticleBaseSchema.extend({ type: z.literal("quest"), properties: QuestPropertiesSchema }),
  ParticleBaseSchema.extend({ type: z.literal("paper"), properties: PaperPropertiesSchema }),
]);

export type Particle = z.infer<typeof ParticleSchema>;

export type ParticleType = Particle["type"];

/** Container types can have children subcollections */
export const CONTAINER_TYPES: ReadonlySet<ParticleType> = new Set(["stream", "folder"]);

export function isContainerType(type: ParticleType): boolean {
  return CONTAINER_TYPES.has(type);
}

// --- Auth types ---

const RequestCodeRequestSchema = z.object({
  email: z.string().email(),
});
export type RequestCodeRequest = z.infer<typeof RequestCodeRequestSchema>;

const SignInRequestSchema = z.object({
  email: z.string().email(),
  code: z.string(),
});
export type SignInRequest = z.infer<typeof SignInRequestSchema>;

export const SignInResponseSchema = z.object({
  human: HumanSchema,
  token: z.string(),
});
export type SignInResponse = z.infer<typeof SignInResponseSchema>;
