import "server-only"
import { prisma } from "@/server/db"
import { emailBounces, emailSummary, searchSubaccounts, viewApiKeys } from "@/server/smtp2go"

type RemoteSubaccount = {
  id?: string
  name?: string
  subaccount_id?: string
  subaccountId?: string
}

type SubaccountTarget = {
  subaccountId: string
  name: string | null
}

function parsePercent(value: unknown) {
  if (typeof value === "number") return value
  const n = Number(String(value ?? "").replace("%", ""))
  return Number.isFinite(n) ? n : null
}

function parseDate(value: unknown) {
  if (typeof value !== "string") return null
  const d = new Date(value)
  return Number.isNaN(d.getTime()) ? null : d
}

function parseNumber(value: unknown) {
  if (typeof value === "number") return Number.isFinite(value) ? value : null
  const n = Number(value)
  return Number.isFinite(n) ? n : null
}

async function listAllSubaccounts() {
  const out: RemoteSubaccount[] = []
  let continueToken: string | undefined = undefined
  let page = 0
  const maxPages = 50

  do {
    const remote = await searchSubaccounts({
      fuzzy_search: true,
      sort_direction: "asc",
      page_size: 100,
      continue_token: continueToken,
    })

    const items: RemoteSubaccount[] = Array.isArray(remote?.subaccounts)
      ? (remote.subaccounts as RemoteSubaccount[])
      : Array.isArray(remote)
      ? (remote as RemoteSubaccount[])
      : []

    out.push(...items)
    continueToken = remote?.continue_token ?? remote?.continueToken ?? undefined
    page += 1
  } while (continueToken && page < maxPages)

  return out
}

function withLimit<T>(limit: number) {
  const queue: Array<() => void> = []
  let active = 0

  const runNext = () => {
    if (active >= limit) return
    const next = queue.shift()
    if (!next) return
    active += 1
    next()
  }

  return async function <R>(fn: () => Promise<R>) {
    return new Promise<R>((resolve, reject) => {
      const task = () => {
        fn()
          .then(resolve)
          .catch(reject)
          .finally(() => {
            active -= 1
            runNext()
          })
      }
      queue.push(task)
      runNext()
    })
  }
}

async function refreshTargets(targets: SubaccountTarget[], options?: { limit?: number }) {
  const limitRun = withLimit(4)
  const sliced = options?.limit ? targets.slice(0, options.limit) : targets

  let updated = 0
  let skipped = 0
  let errors = 0

  await Promise.all(
    sliced.map((sa) =>
      limitRun(async () => {
        try {
          const subId = sa.subaccountId
          if (!subId) {
            skipped += 1
            return
          }

          const keys = await viewApiKeys({ subaccount_id: subId })
          const apiKey = keys.find((k) => k.api_key)?.api_key
          if (!apiKey) {
            skipped += 1
            return
          }

          const [bounces, summary] = await Promise.all([
            emailBounces({}, { apiKey }),
            emailSummary({}, { apiKey }),
          ])

          const cycleStart = parseDate((summary as Record<string, unknown>)?.cycle_start)
          const cycleEnd = parseDate((summary as Record<string, unknown>)?.cycle_end)

          const summaryRec = summary as Record<string, unknown>
          const data = {
            subaccountId: subId,
            name: sa.name ?? null,
            bouncePercent: parsePercent(bounces?.bounce_percent) ?? parsePercent(summaryRec?.bounce_percent),
            spamPercent: parsePercent(summaryRec?.spam_percent),
            emails: parseNumber(bounces?.emails) ?? parseNumber(summaryRec?.email_count),
            rejects: parseNumber(bounces?.rejects) ?? parseNumber(summaryRec?.bounce_rejects),
            softbounces: parseNumber(bounces?.softbounces) ?? parseNumber(summaryRec?.softbounces),
            hardbounces: parseNumber(bounces?.hardbounces) ?? parseNumber(summaryRec?.hardbounces),
            cycleStart,
            cycleEnd,
            cycleUsed: parseNumber(summaryRec?.cycle_used),
            cycleRemaining: parseNumber(summaryRec?.cycle_remaining),
            cycleMax: parseNumber(summaryRec?.cycle_max),
            spamEmails: parseNumber(summaryRec?.spam_emails),
            spamRejects: parseNumber(summaryRec?.spam_rejects),
            unsubscribes: parseNumber(summaryRec?.unsubscribes),
          }

          if (cycleStart && cycleEnd) {
            await prisma.subaccountMetric.upsert({
              where: {
                subaccountId_cycleStart_cycleEnd: {
                  subaccountId: subId,
                  cycleStart,
                  cycleEnd,
                },
              },
              update: { ...data, fetchedAt: new Date() },
              create: data,
            })
          } else {
            await prisma.subaccountMetric.create({
              data: { ...data, fetchedAt: new Date() },
            })
          }

          updated += 1
        } catch {
          errors += 1
        }
      })
    )
  )

  return { updated, skipped, errors, total: sliced.length }
}

export async function refreshSubaccountMetrics(options?: { limit?: number }) {
  const subaccounts = await listAllSubaccounts()
  const targets = subaccounts
    .map((sa) => {
      const subId = sa.subaccount_id ?? sa.subaccountId ?? sa.id
      return subId ? { subaccountId: subId, name: sa.name ?? null } : null
    })
    .filter((t): t is SubaccountTarget => Boolean(t))

  return refreshTargets(targets, options)
}

export async function refreshSubaccountMetricsByIds(
  targets: SubaccountTarget[],
  options?: { limit?: number }
) {
  if (!targets.length) {
    return { updated: 0, skipped: 0, errors: 0, total: 0 }
  }
  return refreshTargets(targets, options)
}
