import { and, asc, eq, gte, lte } from 'drizzle-orm'
import { z } from 'zod'
import { db } from '../db/client.js'
import { plannerEntries } from '../db/schema.js'
import { findAuthenticatedUser } from '../lib/authSession.js'

const dateSchema = z.string().regex(/^\d{4}-\d{2}-\d{2}$/)

const plannerPayloadSchema = z.object({
  payload: z.record(z.any()),
})

const plannerRangeQuerySchema = z.object({
  from: dateSchema.optional(),
  to: dateSchema.optional(),
})

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

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

  return user
}

export async function registerPlannerRoutes(app) {
  app.get('/api/planner', async (request, reply) => {
    const user = await requireAuthenticatedUser(request, reply)

    if (!user) {
      return
    }

    const query = plannerRangeQuerySchema.safeParse(request.query ?? {})

    if (!query.success) {
      return reply.code(400).send({
        message: '조회 범위가 올바르지 않습니다.',
        issues: query.error.flatten(),
      })
    }

    const filters = [eq(plannerEntries.userId, user.id)]

    if (query.data.from) {
      filters.push(gte(plannerEntries.entryDate, query.data.from))
    }

    if (query.data.to) {
      filters.push(lte(plannerEntries.entryDate, query.data.to))
    }

    const entries = await db
      .select({
        entryDate: plannerEntries.entryDate,
        payload: plannerEntries.payload,
        createdAt: plannerEntries.createdAt,
        updatedAt: plannerEntries.updatedAt,
      })
      .from(plannerEntries)
      .where(and(...filters))
      .orderBy(asc(plannerEntries.entryDate))

    return {
      entries,
    }
  })

  app.get('/api/planner/:entryDate', async (request, reply) => {
    const user = await requireAuthenticatedUser(request, reply)

    if (!user) {
      return
    }

    const dateResult = dateSchema.safeParse(request.params.entryDate)

    if (!dateResult.success) {
      return reply.code(400).send({
        message: '날짜 형식이 올바르지 않습니다.',
      })
    }

    const [entry] = await db
      .select()
      .from(plannerEntries)
      .where(
        and(
          eq(plannerEntries.userId, user.id),
          eq(plannerEntries.entryDate, dateResult.data),
        ),
      )
      .limit(1)

    return {
      entry: entry ?? null,
    }
  })

  app.put('/api/planner/:entryDate', async (request, reply) => {
    const user = await requireAuthenticatedUser(request, reply)

    if (!user) {
      return
    }

    const dateResult = dateSchema.safeParse(request.params.entryDate)

    if (!dateResult.success) {
      return reply.code(400).send({
        message: '날짜 형식이 올바르지 않습니다.',
      })
    }

    const payloadResult = plannerPayloadSchema.safeParse(request.body)

    if (!payloadResult.success) {
      return reply.code(400).send({
        message: '플래너 저장 데이터가 올바르지 않습니다.',
        issues: payloadResult.error.flatten(),
      })
    }

    const now = new Date()

    const [entry] = await db
      .insert(plannerEntries)
      .values({
        userId: user.id,
        entryDate: dateResult.data,
        payload: payloadResult.data.payload,
        createdAt: now,
        updatedAt: now,
      })
      .onConflictDoUpdate({
        target: [plannerEntries.userId, plannerEntries.entryDate],
        set: {
          payload: payloadResult.data.payload,
          updatedAt: now,
        },
      })
      .returning()

    return {
      message: '플래너가 저장되었습니다.',
      entry,
    }
  })

  app.delete('/api/planner/:entryDate', async (request, reply) => {
    const user = await requireAuthenticatedUser(request, reply)

    if (!user) {
      return
    }

    const dateResult = dateSchema.safeParse(request.params.entryDate)

    if (!dateResult.success) {
      return reply.code(400).send({
        message: '날짜 형식이 올바르지 않습니다.',
      })
    }

    const deletedEntries = await db
      .delete(plannerEntries)
      .where(
        and(
          eq(plannerEntries.userId, user.id),
          eq(plannerEntries.entryDate, dateResult.data),
        ),
      )
      .returning()

    return {
      message: deletedEntries.length > 0 ? '플래너가 삭제되었습니다.' : '삭제할 플래너가 없습니다.',
      deleted: deletedEntries.length > 0,
    }
  })
}
