/**
 * Rotas de Agendamentos de Mensagem
 *
 * GET    /api/scheduled                    — Lista agendamentos do tenant
 * POST   /api/scheduled                    — Cria agendamento
 * GET    /api/scheduled/:id                — Detalhe
 * PUT    /api/scheduled/:id                — Atualiza
 * DELETE /api/scheduled/:id                — Remove
 * POST   /api/scheduled/:id/pause          — Pausa
 * POST   /api/scheduled/:id/resume         — Retoma
 * POST   /api/scheduled/:id/run-now        — Executa imediatamente
 * GET    /api/scheduled/:id/logs           — Histórico de execuções
 */
import { Router, type Request, type Response } from 'express'
import { z } from 'zod'
import { Cron } from 'croner'
import { prisma } from '../../infra/database/prisma'
import type { SchedulerService } from './scheduler.service'

const recipientSchema = z.object({
  jid:          z.string(),
  name:         z.string().optional(),
  birthday:     z.string().optional(),
  anniversary:  z.string().optional(),
  signupDate:   z.string().optional(),
})

const scheduleSchema = z.object({
  instanceId:    z.string(),
  name:          z.string().min(1).max(100),
  templateId:    z.string().optional(),
  payload:       z.record(z.unknown()),
  recipientMode: z.enum(['MANUAL', 'ALL_CONTACTS', 'TAG_FILTER']).default('MANUAL'),
  recipients:    z.array(recipientSchema).default([]),
  scheduleType:  z.enum(['ONCE', 'RECURRING', 'EVENT']),
  cronExpr:      z.string().optional(),
  scheduledAt:   z.string().datetime().optional(),
  eventType:     z.enum(['BIRTHDAY', 'ANNIVERSARY', 'SIGNUP_DAYS']).optional(),
  timezone:      z.string().default('America/Sao_Paulo'),
})

function getId(req: Request): string {
  const id = req.params['id']
  return Array.isArray(id) ? id[0] : id
}

function calcNextRun(data: Partial<z.infer<typeof scheduleSchema>>): Date | null {
  if (data.scheduleType === 'ONCE' && data.scheduledAt) {
    return new Date(data.scheduledAt)
  }
  if (data.scheduleType === 'RECURRING' && data.cronExpr) {
    try {
      const job = new Cron(data.cronExpr, { paused: true })
      return job.nextRun() ?? null
    } catch { return null }
  }
  return null
}

export function buildScheduledRoutes(_scheduler: SchedulerService): Router {
  const router = Router()

  // ── LIST ──────────────────────────────────────────────────────────────────
  router.get('/', async (req: Request, res: Response) => {
    const tenantId = req.tenantId!
    const status = Array.isArray(req.query['status']) ? req.query['status'][0] : req.query['status'] as string | undefined
    const type   = Array.isArray(req.query['type'])   ? req.query['type'][0]   : req.query['type']   as string | undefined

    const schedules = await prisma.scheduledMessage.findMany({
      where: {
        tenantId,
        ...(status ? { status: status as any } : {}),
        ...(type   ? { scheduleType: type as any } : {}),
      },
      include: {
        template: { select: { id: true, name: true, type: true } },
        _count:   { select: { logs: true } },
      },
      orderBy: { createdAt: 'desc' },
    })

    res.json(schedules)
  })

  // ── CREATE ────────────────────────────────────────────────────────────────
  router.post('/', async (req: Request, res: Response) => {
    const tenantId = req.tenantId!
    const parse = scheduleSchema.safeParse(req.body)
    if (!parse.success) {
      res.status(400).json({ error: parse.error.errors })
      return
    }

    const data = parse.data
    const nextRunAt = calcNextRun(data)

    const schedule = await prisma.scheduledMessage.create({
      data: {
        tenantId,
        instanceId:    data.instanceId,
        name:          data.name,
        templateId:    data.templateId,
        payload:       data.payload as any,
        recipientMode: data.recipientMode,
        recipients:    data.recipients as any,
        scheduleType:  data.scheduleType,
        cronExpr:      data.cronExpr,
        scheduledAt:   data.scheduledAt ? new Date(data.scheduledAt) : null,
        eventType:     data.eventType ?? null,
        timezone:      data.timezone,
        nextRunAt,
      },
    })

    res.status(201).json(schedule)
  })

  // ── GET ONE ───────────────────────────────────────────────────────────────
  router.get('/:id', async (req: Request, res: Response) => {
    const tenantId = req.tenantId!
    const sched = await prisma.scheduledMessage.findFirst({
      where: { id: getId(req), tenantId },
      include: {
        template: true,
        logs: { orderBy: { runAt: 'desc' }, take: 5 },
      },
    })
    if (!sched) { res.status(404).json({ error: 'Agendamento não encontrado' }); return }
    res.json(sched)
  })

  // ── UPDATE ────────────────────────────────────────────────────────────────
  router.put('/:id', async (req: Request, res: Response) => {
    const tenantId = req.tenantId!
    const id = getId(req)
    const exists = await prisma.scheduledMessage.findFirst({
      where: { id, tenantId },
    })
    if (!exists) { res.status(404).json({ error: 'Agendamento não encontrado' }); return }

    const parse = scheduleSchema.partial().safeParse(req.body)
    if (!parse.success) {
      res.status(400).json({ error: parse.error.errors })
      return
    }

    const data = parse.data
    const nextRunAt = calcNextRun({ ...exists, ...data } as any)

    const { payload, recipients, scheduledAt, eventType, ...rest } = data
    const updated = await prisma.scheduledMessage.update({
      where: { id },
      data: {
        ...rest,
        ...(payload    !== undefined ? { payload:    payload    as any  } : {}),
        ...(recipients !== undefined ? { recipients: recipients as any  } : {}),
        ...(scheduledAt !== undefined ? { scheduledAt: new Date(scheduledAt as string) } : {}),
        ...(eventType   !== undefined ? { eventType } : {}),
        status:   'ACTIVE',
        nextRunAt,
      },
    })

    res.json(updated)
  })

  // ── DELETE ────────────────────────────────────────────────────────────────
  router.delete('/:id', async (req: Request, res: Response) => {
    const tenantId = req.tenantId!
    const id = getId(req)
    const exists = await prisma.scheduledMessage.findFirst({
      where: { id, tenantId },
    })
    if (!exists) { res.status(404).json({ error: 'Agendamento não encontrado' }); return }

    await prisma.scheduledMessage.delete({ where: { id } })
    res.status(204).send()
  })

  // ── PAUSE ─────────────────────────────────────────────────────────────────
  router.post('/:id/pause', async (req: Request, res: Response) => {
    const tenantId = req.tenantId!
    const id = getId(req)
    const exists = await prisma.scheduledMessage.findFirst({ where: { id, tenantId } })
    if (!exists) { res.status(404).json({ error: 'Agendamento não encontrado' }); return }
    await prisma.scheduledMessage.update({ where: { id }, data: { status: 'PAUSED' } })
    res.json({ ok: true })
  })

  // ── RESUME ────────────────────────────────────────────────────────────────
  router.post('/:id/resume', async (req: Request, res: Response) => {
    const tenantId = req.tenantId!
    const id = getId(req)
    const exists = await prisma.scheduledMessage.findFirst({ where: { id, tenantId } })
    if (!exists) { res.status(404).json({ error: 'Agendamento não encontrado' }); return }
    await prisma.scheduledMessage.update({ where: { id }, data: { status: 'ACTIVE' } })
    res.json({ ok: true })
  })

  // ── RUN NOW ───────────────────────────────────────────────────────────────
  router.post('/:id/run-now', async (req: Request, res: Response) => {
    const tenantId = req.tenantId!
    const id = getId(req)
    const sched = await prisma.scheduledMessage.findFirst({ where: { id, tenantId } })
    if (!sched) { res.status(404).json({ error: 'Agendamento não encontrado' }); return }

    // Marca nextRunAt no passado — o tick do scheduler pega no próximo minuto
    await prisma.scheduledMessage.update({
      where: { id },
      data:  { nextRunAt: new Date(Date.now() - 1000), status: 'ACTIVE' },
    })
    res.json({ ok: true, message: 'Execução agendada para o próximo tick' })
  })

  // ── LOGS ──────────────────────────────────────────────────────────────────
  router.get('/:id/logs', async (req: Request, res: Response) => {
    const tenantId = req.tenantId!
    const id = getId(req)
    const exists = await prisma.scheduledMessage.findFirst({ where: { id, tenantId } })
    if (!exists) { res.status(404).json({ error: 'Agendamento não encontrado' }); return }

    const logs = await prisma.scheduledMessageLog.findMany({
      where:   { scheduleId: id },
      orderBy: { runAt: 'desc' },
      take:    50,
    })

    res.json(logs)
  })

  return router
}
