import { and, eq, sql } from 'drizzle-orm'
import { z } from 'zod'
import { db } from '../db/client.js'
import { findAuthenticatedUser } from '../lib/authSession.js'
import { authSessions, users } from '../db/schema.js'

const adminUserIdSchema = z.coerce.number().int().positive()
const adminStatusSchema = z.object({
  disabled: z.boolean(),
})

async function requireAdminUser(request, reply) {
  const user = await findAuthenticatedUser(request)

  if (!user) {
    reply.code(401).send({
      message: '인증이 필요합니다.',
    })
    return null
  }

  if (user.role !== 'admin') {
    reply.code(403).send({
      message: '관리자만 접근할 수 있습니다.',
    })
    return null
  }

  return user
}

export async function registerAdminRoutes(app) {
  app.get('/api/admin/overview', async (request, reply) => {
    const adminUser = await requireAdminUser(request, reply)

    if (!adminUser) {
      return
    }

    const [summary] = await db
      .select({
        totalUsers: sql`count(*)::int`,
        totalAdmins: sql`count(*) filter (where ${users.role} = 'admin')::int`,
        disabledUsers: sql`count(*) filter (where ${users.disabledAt} is not null)::int`,
        verifiedUsers: sql`count(*) filter (where ${users.emailVerifiedAt} is not null)::int`,
        activeUsers30d: sql`count(*) filter (where ${users.lastLoginAt} >= now() - interval '30 days')::int`,
        newUsers7d: sql`count(*) filter (where ${users.createdAt} >= now() - interval '7 days')::int`,
      })
      .from(users)

    const plannerCountResult = await db.execute(
      sql`select count(*)::int as count from planner_entries`,
    )
    const goalCountResult = await db.execute(
      sql`select count(*)::int as count from goals`,
    )

    const userRowsResult = await db.execute(sql`
      select
        u.id,
        u.nickname,
        u.email,
        u.role,
        u.disabled_at as "disabledAt",
        u.created_at as "createdAt",
        u.email_verified_at as "emailVerifiedAt",
        u.last_login_at as "lastLoginAt",
        count(distinct pe.id)::int as "plannerEntryCount",
        count(distinct g.id)::int as "goalCount",
        count(distinct s.id)::int as "activeSessionCount",
        max(pe.entry_date) as "lastEntryDate",
        max(pe.updated_at) as "lastEntryUpdatedAt"
      from users u
      left join planner_entries pe on pe.user_id = u.id
      left join goals g on g.user_id = u.id
      left join auth_sessions s on s.user_id = u.id and s.expires_at > now()
      group by u.id
      order by coalesce(u.last_login_at, u.created_at) desc, u.id desc
    `)

    const recentLoginsResult = await db.execute(sql`
      select
        id,
        nickname,
        email,
        role,
        last_login_at as "lastLoginAt"
      from users
      where last_login_at is not null
      order by last_login_at desc
      limit 5
    `)

    return {
      summary: {
        totalUsers: summary?.totalUsers ?? 0,
        totalAdmins: summary?.totalAdmins ?? 0,
        verifiedUsers: summary?.verifiedUsers ?? 0,
        activeUsers30d: summary?.activeUsers30d ?? 0,
        newUsers7d: summary?.newUsers7d ?? 0,
        disabledUsers: summary?.disabledUsers ?? 0,
        totalPlannerEntries: plannerCountResult.rows[0]?.count ?? 0,
        totalGoals: goalCountResult.rows[0]?.count ?? 0,
      },
      users: userRowsResult.rows.map((row) => ({
        ...row,
        isActiveRecently: row.lastLoginAt
          ? new Date(row.lastLoginAt).getTime() >= Date.now() - 30 * 24 * 60 * 60 * 1000
          : false,
      })),
      recentLogins: recentLoginsResult.rows,
    }
  })

  app.get('/api/admin/users/:userId/detail', async (request, reply) => {
    const adminUser = await requireAdminUser(request, reply)

    if (!adminUser) {
      return
    }

    const userIdResult = adminUserIdSchema.safeParse(request.params.userId)

    if (!userIdResult.success) {
      return reply.code(400).send({
        message: '대상 사용자 값이 올바르지 않습니다.',
      })
    }

    const userId = userIdResult.data

    const detailResult = await db.execute(sql`
      select
        u.id,
        u.nickname,
        u.email,
        u.role,
        u.disabled_at as "disabledAt",
        u.created_at as "createdAt",
        u.updated_at as "updatedAt",
        u.email_verified_at as "emailVerifiedAt",
        u.last_login_at as "lastLoginAt",
        count(distinct pe.id)::int as "plannerEntryCount",
        count(distinct g.id)::int as "goalCount",
        count(distinct s.id)::int as "activeSessionCount"
      from users u
      left join planner_entries pe on pe.user_id = u.id
      left join goals g on g.user_id = u.id
      left join auth_sessions s on s.user_id = u.id and s.expires_at > now()
      where u.id = ${userId}
      group by u.id
      limit 1
    `)

    const detailUser = detailResult.rows[0]

    if (!detailUser) {
      return reply.code(404).send({
        message: '대상 사용자를 찾을 수 없습니다.',
      })
    }

    const plannerEntriesResult = await db.execute(sql`
      select
        entry_date as "entryDate",
        payload,
        created_at as "createdAt",
        updated_at as "updatedAt"
      from planner_entries
      where user_id = ${userId}
      order by entry_date desc
      limit 12
    `)

    const goalsResult = await db.execute(sql`
      select
        id,
        title,
        target_date as "targetDate",
        active_from as "activeFrom",
        active_until as "activeUntil",
        color,
        created_at as "createdAt",
        updated_at as "updatedAt"
      from goals
      where user_id = ${userId}
      order by updated_at desc, id desc
      limit 20
    `)

    return {
      user: detailUser,
      plannerEntries: plannerEntriesResult.rows,
      goals: goalsResult.rows,
    }
  })

  app.put('/api/admin/users/:userId/status', async (request, reply) => {
    const adminUser = await requireAdminUser(request, reply)

    if (!adminUser) {
      return
    }

    const userIdResult = adminUserIdSchema.safeParse(request.params.userId)
    const payload = adminStatusSchema.safeParse(request.body)

    if (!userIdResult.success || !payload.success) {
      return reply.code(400).send({
        message: '관리자 요청 값이 올바르지 않습니다.',
      })
    }

    const userId = userIdResult.data

    const [targetUser] = await db
      .select()
      .from(users)
      .where(eq(users.id, userId))
      .limit(1)

    if (!targetUser) {
      return reply.code(404).send({
        message: '대상 사용자를 찾을 수 없습니다.',
      })
    }

    if (targetUser.role === 'admin') {
      return reply.code(403).send({
        message: '관리자 계정 상태는 여기서 변경할 수 없습니다.',
      })
    }

    const disabled = payload.data.disabled
    const now = new Date()

    const [updatedUser] = await db
      .update(users)
      .set({
        disabledAt: disabled ? now : null,
        updatedAt: now,
      })
      .where(eq(users.id, userId))
      .returning()

    if (disabled) {
      await db.delete(authSessions).where(eq(authSessions.userId, userId))
    }

    return {
      message: disabled ? '계정을 비활성화했습니다.' : '계정을 다시 사용할 수 있게 했습니다.',
      user: updatedUser,
    }
  })

  app.post('/api/admin/users/:userId/revoke-sessions', async (request, reply) => {
    const adminUser = await requireAdminUser(request, reply)

    if (!adminUser) {
      return
    }

    const userIdResult = adminUserIdSchema.safeParse(request.params.userId)

    if (!userIdResult.success) {
      return reply.code(400).send({
        message: '대상 사용자 값이 올바르지 않습니다.',
      })
    }

    const userId = userIdResult.data

    const [targetUser] = await db
      .select()
      .from(users)
      .where(eq(users.id, userId))
      .limit(1)

    if (!targetUser) {
      return reply.code(404).send({
        message: '대상 사용자를 찾을 수 없습니다.',
      })
    }

    if (targetUser.role === 'admin') {
      return reply.code(403).send({
        message: '관리자 계정 세션은 여기서 정리하지 않습니다.',
      })
    }

    const deletedSessions = await db
      .delete(authSessions)
      .where(eq(authSessions.userId, userId))
      .returning({ id: authSessions.id })

    return {
      message: deletedSessions.length > 0 ? '해당 사용자의 로그인 세션을 모두 종료했습니다.' : '종료할 로그인 세션이 없습니다.',
      revokedCount: deletedSessions.length,
    }
  })

  app.delete('/api/admin/users/:userId', async (request, reply) => {
    const adminUser = await requireAdminUser(request, reply)

    if (!adminUser) {
      return
    }

    const userIdResult = adminUserIdSchema.safeParse(request.params.userId)

    if (!userIdResult.success) {
      return reply.code(400).send({
        message: '대상 사용자 값이 올바르지 않습니다.',
      })
    }

    const userId = userIdResult.data

    const [targetUser] = await db
      .select()
      .from(users)
      .where(eq(users.id, userId))
      .limit(1)

    if (!targetUser) {
      return reply.code(404).send({
        message: '대상 사용자를 찾을 수 없습니다.',
      })
    }

    if (targetUser.role === 'admin') {
      return reply.code(403).send({
        message: '관리자 계정은 여기서 삭제할 수 없습니다.',
      })
    }

    await db.delete(users).where(and(eq(users.id, userId), eq(users.role, 'user')))

    return {
      message: '사용자 계정과 관련 데이터를 삭제했습니다.',
    }
  })
}
