/**
 * Project members API routes — projects module.
 * Mounted at /api/projects (behind authMiddleware + requireModuleEnabled in router).
 *
 * GET    /:id/members        list project members
 * POST   /:id/members        add / upsert member (role + hourly_rate)
 * DELETE /:id/members/:uid   remove member
 */
import { Hono } from 'hono'
import {
  assertActiveTenantAssignee,
  invalidTenantReferenceBody,
  listProjectMembers,
  addProjectMember,
  removeProjectMember,
} from '@zync/db/queries'
import type { AppEnv } from '../../types'
import { requirePermission } from '../../middleware/guards'
import { addMemberSchema } from '../../schemas/projects'

export const projectMembersRoute = new Hono<AppEnv>()

function hasTenantWideProjectVisibility(role: string | null | undefined): boolean {
  return role === 'OWNER' || role === 'ADMIN' || role === 'VIEWER'
}

// GET /api/projects/:id/members
projectMembersRoute.get('/:id/members', requirePermission('projects:read'), async (c) => {
  const session = c.get('session')
  if (!session || session.type !== 'user' || !session.tid) {
    return c.json({ error: 'Unauthorized' }, 401)
  }

  const db = c.get('db')
  const access = { fullVisibility: hasTenantWideProjectVisibility(session.role), userId: session.sub }
  try {
    const members = await listProjectMembers(db, session.tid, c.req.param('id'), access)
    return c.json(members, 200)
  } catch (err) {
    if (err instanceof Error && err.message === 'Project not found') {
      return c.json({ error: 'Not found' }, 404)
    }
    throw err
  }
})

// POST /api/projects/:id/members  (upserts on existing member)
projectMembersRoute.post('/:id/members', requirePermission('projects:write'), async (c) => {
  const session = c.get('session')
  if (!session || session.type !== 'user' || !session.tid) {
    return c.json({ error: 'Unauthorized' }, 401)
  }

  const parsed = addMemberSchema.safeParse(await c.req.json())
  if (!parsed.success) {
    return c.json({ error: 'Invalid request', issues: parsed.error.issues }, 400)
  }

  const db = c.get('db')

  if (!(await assertActiveTenantAssignee(db, session.tid, parsed.data.user_id))) {
    return c.json(invalidTenantReferenceBody('members'), 400)
  }

  try {
    const member = await addProjectMember(
      db,
      session.tid,
      c.req.param('id'),
      parsed.data,
      session.sub,
      { fullVisibility: hasTenantWideProjectVisibility(session.role), userId: session.sub },
    )
    return c.json(member, 200)
  } catch (err) {
    if (err instanceof Error && err.message === 'Project not found') {
      return c.json({ error: 'Not found' }, 404)
    }
    if (err instanceof Error && err.message.startsWith('Forbidden:')) {
      return c.json({ error: err.message }, 403)
    }
    throw err
  }
})

// DELETE /api/projects/:id/members/:uid
projectMembersRoute.delete(
  '/:id/members/:uid',
  requirePermission('projects:write'),
  async (c) => {
    const session = c.get('session')
    if (!session || session.type !== 'user' || !session.tid) {
      return c.json({ error: 'Unauthorized' }, 401)
    }

    const db = c.get('db')
    try {
      await removeProjectMember(
        db,
        session.tid,
        c.req.param('id'),
        c.req.param('uid'),
        session.sub,
        { fullVisibility: hasTenantWideProjectVisibility(session.role), userId: session.sub },
      )
      return new Response(null, { status: 204 })
    } catch (err) {
      if (err instanceof Error && err.message === 'Project not found') {
        return c.json({ error: 'Not found' }, 404)
      }
      if (err instanceof Error && err.message.startsWith('Forbidden:')) {
        return c.json({ error: err.message }, 403)
      }
      throw err
    }
  },
)
