import { describe, expect, it, vi } from "bun:test"

import { runTicketAutomationsForEvent } from "../convex/automations"
import type { AutomationConditionGroup, TicketForAutomation } from "../convex/automationsEngine"
import { evaluateAutomationConditions } from "../convex/automationsEngine"

function buildTicket(overrides: Partial<TicketForAutomation> = {}): TicketForAutomation {
  const base: TicketForAutomation = {
    tenantId: "tenant-1",
    status: "AWAITING_ATTENDANCE",
    priority: "MEDIUM",
    channel: "EMAIL",
    queueId: "queue-1" as never,
    companyId: "company-1" as never,
    categoryId: "cat-1" as never,
    subcategoryId: "subcat-1" as never,
    tags: ["vip", "windows"],
    formTemplate: "DEFAULT",
    chatEnabled: true,
  }

  return { ...base, ...overrides }
}

describe("automationsEngine.evaluateAutomationConditions", () => {
  it("retorna true quando não há condições", () => {
    const ticket = buildTicket()
    expect(evaluateAutomationConditions(ticket, null)).toBe(true)
    expect(evaluateAutomationConditions(ticket, undefined)).toBe(true)
  })

  it("aplica AND: todas devem bater", () => {
    const ticket = buildTicket({ priority: "HIGH" })
    const group: AutomationConditionGroup = {
      op: "AND",
      conditions: [
        { field: "priority", op: "eq", value: "HIGH" },
        { field: "status", op: "eq", value: "AWAITING_ATTENDANCE" },
      ],
    }
    expect(evaluateAutomationConditions(ticket, group)).toBe(true)
  })

  it("aplica OR: basta uma bater", () => {
    const ticket = buildTicket({ priority: "LOW" })
    const group: AutomationConditionGroup = {
      op: "OR",
      conditions: [
        { field: "priority", op: "eq", value: "HIGH" },
        { field: "priority", op: "eq", value: "LOW" },
      ],
    }
    expect(evaluateAutomationConditions(ticket, group)).toBe(true)
  })

  it("suporta comparadores eq/neq/in/not_in para campos simples", () => {
    const ticket = buildTicket()

    expect(
      evaluateAutomationConditions(ticket, {
        op: "AND",
        conditions: [{ field: "companyId", op: "eq", value: "company-1" }],
      })
    ).toBe(true)

    expect(
      evaluateAutomationConditions(ticket, {
        op: "AND",
        conditions: [{ field: "queueId", op: "neq", value: "queue-2" }],
      })
    ).toBe(true)

    expect(
      evaluateAutomationConditions(ticket, {
        op: "AND",
        conditions: [{ field: "priority", op: "in", value: ["LOW", "MEDIUM"] }],
      })
    ).toBe(true)

    expect(
      evaluateAutomationConditions(ticket, {
        op: "AND",
        conditions: [{ field: "status", op: "not_in", value: ["RESOLVED", "PAUSED"] }],
      })
    ).toBe(true)
  })

  it("suporta chatEnabled (is_true/is_false/eq/neq)", () => {
    const ticket = buildTicket({ chatEnabled: true })

    expect(
      evaluateAutomationConditions(ticket, {
        op: "AND",
        conditions: [{ field: "chatEnabled", op: "is_true" }],
      })
    ).toBe(true)

    expect(
      evaluateAutomationConditions(ticket, {
        op: "AND",
        conditions: [{ field: "chatEnabled", op: "is_false" }],
      })
    ).toBe(false)

    expect(
      evaluateAutomationConditions(ticket, {
        op: "AND",
        conditions: [{ field: "chatEnabled", op: "eq", value: true }],
      })
    ).toBe(true)

    expect(
      evaluateAutomationConditions(ticket, {
        op: "AND",
        conditions: [{ field: "chatEnabled", op: "neq", value: false }],
      })
    ).toBe(true)
  })

  it("suporta tag (contains/not_contains/eq/neq)", () => {
    const ticket = buildTicket({ tags: ["vip", "linux"] })

    expect(
      evaluateAutomationConditions(ticket, {
        op: "AND",
        conditions: [{ field: "tag", op: "contains", value: "vip" }],
      })
    ).toBe(true)

    expect(
      evaluateAutomationConditions(ticket, {
        op: "AND",
        conditions: [{ field: "tag", op: "not_contains", value: "windows" }],
      })
    ).toBe(true)

    expect(
      evaluateAutomationConditions(ticket, {
        op: "AND",
        conditions: [{ field: "tag", op: "eq", value: "linux" }],
      })
    ).toBe(true)

    expect(
      evaluateAutomationConditions(ticket, {
        op: "AND",
        conditions: [{ field: "tag", op: "neq", value: "vip" }],
      })
    ).toBe(false)
  })
})

describe("automations.runTicketAutomationsForEvent", () => {
  it("agenda automação DELAYED via scheduler.runAfter", async () => {
    const runAfter = vi.fn(async () => {})

    const ticketId = "ticket_1" as never
    const automationId = "auto_1" as never
    const ticket = buildTicket({ tenantId: "tenant-1" }) as unknown as { _id: unknown; tenantId: string }
    const automations = [
      {
        _id: automationId,
        tenantId: "tenant-1",
        name: "Auto",
        enabled: true,
        trigger: "TICKET_CREATED",
        timing: "DELAYED",
        delayMs: 60_000,
        conditions: null,
        actions: [{ type: "SET_CHAT_ENABLED", enabled: true }],
        createdBy: "user_1",
        createdAt: 0,
        updatedAt: 0,
      },
    ]

    const take = vi.fn(async () => automations)
    const filter = vi.fn(() => ({ take }))
    const withIndex = vi.fn(() => ({ filter, take }))
    const query = vi.fn(() => ({ withIndex }))
    const get = vi.fn(async (id: unknown) => {
      if (id === ticketId) return { ...ticket, _id: ticketId, tenantId: "tenant-1" }
      return null
    })

    await runTicketAutomationsForEvent(
      {
        db: { get, query } as unknown,
        scheduler: { runAfter } as unknown,
      } as never,
      { tenantId: "tenant-1", ticketId, eventType: "TICKET_CREATED" }
    )

    expect(runAfter).toHaveBeenCalledTimes(1)
    expect(runAfter).toHaveBeenCalledWith(
      60_000,
      expect.anything(),
      expect.objectContaining({ automationId, ticketId, eventType: "TICKET_CREATED" })
    )
  })

  it("agenda envio de e-mail quando ação SEND_EMAIL é aplicada", async () => {
    const runAfter = vi.fn(async () => {})

    const ticketId = "ticket_1" as never
    const automationId = "auto_1" as never

    const ticket = {
      _id: ticketId,
      tenantId: "tenant-1",
      reference: 123,
      subject: "Teste de automação",
      status: "PENDING",
      priority: "MEDIUM",
      channel: "EMAIL",
      requesterId: "user_req_1" as never,
      requesterSnapshot: { name: "Renan", email: "cliente@empresa.com" },
      createdAt: 0,
      updatedAt: 0,
    }

    const automations = [
      {
        _id: automationId,
        tenantId: "tenant-1",
        name: "Auto e-mail",
        enabled: true,
        trigger: "TICKET_CREATED",
        timing: "IMMEDIATE",
        delayMs: undefined,
        conditions: null,
        actions: [
          {
            type: "SEND_EMAIL",
            subject: "Atualização do chamado #{{ticket.reference}}",
            message: "Olá {{requester.name}}, recebemos seu chamado: {{ticket.subject}}",
            ctaTarget: "PORTAL",
            ctaLabel: "Abrir chamado",
            recipients: [{ type: "REQUESTER" }],
          },
        ],
        createdBy: "user_1",
        createdAt: 0,
        updatedAt: 0,
      },
    ]

    const takeAutomations = vi.fn(async () => automations)
    const filterAutomations = vi.fn(() => ({ take: takeAutomations }))
    const withIndexAutomations = vi.fn(() => ({ filter: filterAutomations, take: takeAutomations }))

    const takeRuns = vi.fn(async () => [])
    const orderRuns = vi.fn(() => ({ take: takeRuns }))
    const withIndexRuns = vi.fn(() => ({ order: orderRuns, take: takeRuns }))

    const query = vi.fn((table: string) => {
      if (table === "ticketAutomations") {
        return { withIndex: withIndexAutomations }
      }
      if (table === "ticketAutomationRuns") {
        return { withIndex: withIndexRuns }
      }
      throw new Error(`unexpected table: ${table}`)
    })

    const get = vi.fn(async (id: unknown) => {
      if (id === ticketId) return ticket
      return null
    })

    const insert = vi.fn(async () => "run_1" as never)
    const patch = vi.fn(async () => {})
    const del = vi.fn(async () => {})

    await runTicketAutomationsForEvent(
      {
        db: { get, query, insert, patch, delete: del } as unknown,
        scheduler: { runAfter } as unknown,
      } as never,
      { tenantId: "tenant-1", ticketId, eventType: "TICKET_CREATED" }
    )

    expect(runAfter).toHaveBeenCalledTimes(1)
    expect(runAfter).toHaveBeenCalledWith(
      1,
      expect.anything(),
      expect.objectContaining({
        to: ["cliente@empresa.com"],
        subject: "Atualização do chamado #123",
        html: expect.any(String),
      })
    )
  })
})
