import type { FastifyInstance } from 'fastify'
import bcrypt from 'bcryptjs'
import { z } from 'zod'
import { db, logEvent } from '../db/index.js'

const credentialsSchema = z.object({
  username: z.string().min(3).max(64),
  password: z.string().min(8).max(256),
})

export async function authRoutes(app: FastifyInstance) {
  app.get('/api/system/setup-status', async () => {
    const row = db.prepare('SELECT COUNT(*) as count FROM users').get() as { count: number }
    return { needsSetup: row.count === 0 }
  })

  app.post('/api/setup', async (req, reply) => {
    const existing = db.prepare('SELECT COUNT(*) as count FROM users').get() as { count: number }
    if (existing.count > 0) {
      return reply.code(409).send({ error: 'Setup already completed' })
    }
    const parsed = credentialsSchema.safeParse(req.body)
    if (!parsed.success) {
      return reply.code(400).send({ error: parsed.error.issues[0]?.message ?? 'Invalid input' })
    }
    const { username, password } = parsed.data
    const passwordHash = await bcrypt.hash(password, 12)
    const result = db
      .prepare('INSERT INTO users (username, password_hash) VALUES (?, ?)')
      .run(username, passwordHash)
    const token = app.jwt.sign({ sub: Number(result.lastInsertRowid), username })
    logEvent('account_created', `Account created for ${username}`)
    return { token }
  })

  app.post('/api/auth/login', async (req, reply) => {
    const parsed = credentialsSchema.safeParse(req.body)
    if (!parsed.success) {
      return reply.code(400).send({ error: 'Invalid input' })
    }
    const { username, password } = parsed.data
    const user = db.prepare('SELECT * FROM users WHERE username = ?').get(username) as
      | { id: number; username: string; password_hash: string }
      | undefined
    if (!user || !(await bcrypt.compare(password, user.password_hash))) {
      return reply.code(401).send({ error: 'Invalid username or password' })
    }
    const token = app.jwt.sign({ sub: user.id, username: user.username })
    logEvent('user_login', `${user.username} logged in`)
    return { token }
  })

  app.get('/api/auth/me', { onRequest: [app.authenticate] }, async (req) => {
    const payload = req.user as { sub: number; username: string }
    const user = db
      .prepare('SELECT id, username, display_name, email, avatar_data_url FROM users WHERE id = ?')
      .get(payload.sub)
    return { user }
  })

  const profileUpdateSchema = z.object({
    displayName: z.string().max(128).nullable().optional(),
    email: z.string().email().max(256).nullable().optional(),
    avatarDataUrl: z.string().max(2_000_000).nullable().optional(),
  })

  app.put('/api/auth/me', { onRequest: [app.authenticate] }, async (req, reply) => {
    const payload = req.user as { sub: number; username: string }
    const parsed = profileUpdateSchema.safeParse(req.body)
    if (!parsed.success) {
      return reply.code(400).send({ error: parsed.error.issues[0]?.message ?? 'Invalid input' })
    }
    const { displayName, email, avatarDataUrl } = parsed.data
    if (displayName !== undefined) db.prepare('UPDATE users SET display_name = ? WHERE id = ?').run(displayName, payload.sub)
    if (email !== undefined) db.prepare('UPDATE users SET email = ? WHERE id = ?').run(email, payload.sub)
    if (avatarDataUrl !== undefined) db.prepare('UPDATE users SET avatar_data_url = ? WHERE id = ?').run(avatarDataUrl, payload.sub)
    const user = db
      .prepare('SELECT id, username, display_name, email, avatar_data_url FROM users WHERE id = ?')
      .get(payload.sub)
    return { user }
  })
}
