import {pageQueryShape} from '@crumbs/core/schemas'
import {Hono} from 'hono'
import {HTTPException} from 'hono/http-exception'
import {z} from 'zod'

import {type GuardedVars} from '../auth/middleware'
import {schema} from '../db/client'
import {uuidParams, validate} from '../lib/validate'
import {blockStateBetween} from '../services/blocks'
import {listAuthorCrumbs} from '../services/crumbs'
import {listActivityFeed} from '../services/feed'
import {
  hydrateProfiles,
  listFriendsPage,
  requireViewableAuthor,
} from '../services/friends'
import {listTrailSummaries} from '../services/trails'
import {authorStats} from '../services/users'

const users = new Hono<{Variables: GuardedVars}>()
  .get('/:id', uuidParams('id'), async (c) => {
    const me = c.get('user')
    const {id} = c.req.valid('param')

    // a block by the other party hides the profile entirely; a block by the
    // viewer keeps it visible (so they can unblock) but marks it blocked and
    // withholds content
    const {blocking, blockedBy} = await blockStateBetween(me.id, id)
    if (blockedBy)
      throw new HTTPException(404, {message: 'not_found'})

    const base = (await hydrateProfiles(me.id, [id])).get(id)
    if (!base) throw new HTTPException(404, {message: 'not_found'})

    const friendshipStatus = blocking
      ? ('blocked' as const)
      : base.friendshipStatus
    const canViewContent =
      friendshipStatus === 'self' || friendshipStatus === 'friends'

    const [crumbs, trails] = canViewContent
      ? await Promise.all([
          authorStats(schema.crumbs, id),
          authorStats(schema.trails, id),
        ])
      : [
          {count: 0, preview: null},
          {count: 0, preview: null},
        ]

    return c.json({
      profile: {
        ...base,
        friendshipStatus,
        crumbCount: crumbs.count,
        trailCount: trails.count,
        crumbPreview: crumbs.preview,
        trailPreview: trails.preview,
      },
    })
  })
  .get(
    '/:id/crumbs',
    uuidParams('id'),
    validate('query', z.object(pageQueryShape)),
    async (c) => {
      const me = c.get('user')
      const {id} = c.req.valid('param')
      const {limit, cursor} = c.req.valid('query')
      await requireViewableAuthor(me.id, id)

      const {crumbs, nextCursor} = await listAuthorCrumbs({
        authorIds: [id],
        limit,
        cursor,
      })
      return c.json({crumbs, nextCursor})
    },
  )
  .get(
    '/:id/activity',
    uuidParams('id'),
    validate('query', z.object(pageQueryShape)),
    async (c) => {
      const me = c.get('user')
      const {id} = c.req.valid('param')
      const {limit, cursor} = c.req.valid('query')
      await requireViewableAuthor(me.id, id)

      const {items, nextCursor} = await listActivityFeed({
        viewerId: me.id,
        authorIds: [id],
        limit,
        cursor,
      })
      return c.json({items, nextCursor})
    },
  )
  .get(
    '/:id/friends',
    uuidParams('id'),
    validate('query', z.object(pageQueryShape)),
    async (c) => {
      const me = c.get('user')
      const {id} = c.req.valid('param')
      const {limit, cursor} = c.req.valid('query')
      await requireViewableAuthor(me.id, id)

      const {friends, nextCursor} = await listFriendsPage({
        subjectId: id,
        viewerId: me.id,
        limit,
        cursor,
      })
      return c.json({friends, nextCursor})
    },
  )
  .get(
    '/:id/trails',
    uuidParams('id'),
    validate('query', z.object(pageQueryShape)),
    async (c) => {
      const me = c.get('user')
      const {id} = c.req.valid('param')
      const {limit, cursor} = c.req.valid('query')
      await requireViewableAuthor(me.id, id)

      const {trails, nextCursor} = await listTrailSummaries({
        subjectId: id,
        viewerId: me.id,
        limit,
        cursor,
      })
      return c.json({trails, nextCursor})
    },
  )

export default users
