import {z} from 'zod'

import {CrumbSchema} from './crumbs'
import {pageQueryShape} from './pagination'

export const TRAIL_NAME_MAX = 100
export const TRAIL_DESCRIPTION_MAX = 500

export const TrailNameSchema = z
  .string()
  .trim()
  .min(1)
  .max(TRAIL_NAME_MAX)

export const TrailDescriptionSchema = z
  .string()
  .trim()
  .max(TRAIL_DESCRIPTION_MAX)

export const CreateTrailSchema = z.object({
  // client-minted id for an idempotent create: a replayed create whose ack
  // was lost to a dropped connection upserts on this pk instead of doubling
  id: z.uuid().optional(),
  name: TrailNameSchema,
  crumbId: z.uuid().optional(),
})

// partial edit; an empty description clears it
export const UpdateTrailSchema = z
  .object({
    name: TrailNameSchema.optional(),
    description: TrailDescriptionSchema.optional(),
  })
  .refine(
    (v) => v.name !== undefined || v.description !== undefined,
    {message: 'nothing to update'},
  )

export const TrailMemberSchema = z.object({
  handle: z.string(),
  avatarUrl: z.url().nullable(),
})

export const TrailSchema = z.object({
  id: z.uuid(),
  authorId: z.uuid(),
  authorHandle: z.string(),
  authorAvatarUrl: z.url().nullable(),
  name: z.string(),
  description: z.string().nullable(),
  coverImageUrl: z.url().nullable(),
  crumbCount: z.number().int().nonnegative(),
  containsCrumb: z.boolean().optional(),
  pinned: z.boolean(),
  unseenCount: z.number().int().nonnegative(),
  members: z.array(TrailMemberSchema),
  createdAt: z.iso.datetime(),
  updatedAt: z.iso.datetime(),
})

export const TRAIL_POST_MAX = 300

// the trail page embeds this many latest posts; the rest paginate
export const TRAIL_POSTS_PREVIEW = 3

export const TrailPostTextSchema = z
  .string()
  .trim()
  .min(1)
  .max(TRAIL_POST_MAX)

export const CreateTrailPostSchema = z.object({
  // client-minted id for an idempotent create, as on crumbs and trails
  id: z.uuid().optional(),
  text: TrailPostTextSchema,
})

export const TrailPostFacetSchema = z.object({
  start: z.number().int().nonnegative(),
  end: z.number().int().nonnegative(),
  userId: z.uuid(),
})

export const TrailPostAuthorSchema = z.object({
  id: z.uuid(),
  handle: z.string(),
  avatarUrl: z.url().nullable(),
})

export const TrailPostSchema = z.object({
  id: z.uuid(),
  text: z.string(),
  facets: z.array(TrailPostFacetSchema),
  author: TrailPostAuthorSchema,
  createdAt: z.iso.datetime(),
})

export const ReactionEmojiSchema = z.string().min(1).max(8)

export const CrumbReactionSummarySchema = z.object({
  emoji: ReactionEmojiSchema,
  count: z.number().int().positive(),
  mine: z.boolean(),
})

export const SetReactionSchema = z.object({
  emoji: ReactionEmojiSchema,
})

export const ListTrailsQuerySchema = z.object({
  crumbId: z.uuid().optional(),
  ...pageQueryShape,
})

export const TrailCrumbSchema = CrumbSchema.extend({
  reactions: z.array(CrumbReactionSummarySchema),
})

export const TrailWithCrumbsSchema = TrailSchema.extend({
  crumbs: z.array(TrailCrumbSchema),
})

export const ListTrailsResponseSchema = z.object({
  trails: z.array(TrailSchema),
})

export const TrailResponseSchema = z.object({
  trail: TrailSchema,
})

export const AddCrumbToTrailSchema = z.object({
  crumbId: z.uuid(),
})

// target index within the trail's ordering; clamped server-side
export const MoveTrailCrumbSchema = z.object({
  position: z.number().int().nonnegative(),
})

export type CreateTrailInput = z.infer<typeof CreateTrailSchema>
export type TrailPost = z.infer<typeof TrailPostSchema>
export type TrailPostAuthor = z.infer<typeof TrailPostAuthorSchema>
export type TrailMember = z.infer<typeof TrailMemberSchema>
export type Trail = z.infer<typeof TrailSchema>
export type TrailCrumb = z.infer<typeof TrailCrumbSchema>
export type TrailWithCrumbs = z.infer<typeof TrailWithCrumbsSchema>
export type CrumbReactionSummary = z.infer<
  typeof CrumbReactionSummarySchema
>
