// src/app/api/admin/subaccounts/route.ts
import { NextResponse } from "next/server"
import { prisma } from "@/server/db"
import { requireStaff } from "@/server/auth"
import { addSenderDomain, addSubaccount, closeSubaccount, editSubaccount, reopenSubaccount, searchSubaccounts } from "@/server/smtp2go"

function extractSubaccountId(payload: unknown): string | null {
  if (!payload || typeof payload !== "object") return null
  const obj = payload as Record<string, unknown>
  const direct =
    obj.subaccount_id ??
    obj.subaccountId ??
    obj.id
  if (typeof direct === "string" && direct.trim()) return direct.trim()

  const data = obj.data
  if (data && typeof data === "object") {
    const dataObj = data as Record<string, unknown>
    const nested =
      dataObj.subaccount_id ??
      dataObj.subaccountId ??
      dataObj.id
    if (typeof nested === "string" && nested.trim()) return nested.trim()
  }
  return null
}

export async function GET(req: Request) {
  try {
    await requireStaff()
  } catch (e: unknown) {
    const msg = e instanceof Error ? e.message : String(e ?? "")
    console.warn("admin/subaccounts: unauthorized access attempt", msg)
    return NextResponse.json({ error: "UNAUTHORIZED" }, { status: 401 })
  }

  const url = new URL(req.url)
  type SearchParams = {
    fuzzy_search?: boolean
    search_terms?: string[]
    states?: string
    sort_direction?: string
    page_size?: number
    continue_token?: string
  }

  type RemoteSubaccount = {
    id?: string
    name?: string
    subaccount_id?: string
    subaccountId?: string
    state?: string
    label?: string
    smtp_username?: string
    smtpUsername?: string
    plan_size?: number
    plan_used?: number
    plan_remaining?: number
    [k: string]: unknown
  }

  const params: SearchParams = {
    fuzzy_search: url.searchParams.get("fuzzy_search") !== "false",
    search_terms: url.searchParams.get("search_terms")
      ? url.searchParams
          .get("search_terms")
          ?.split(",")
          .map((s) => s.trim())
          .filter(Boolean)
      : undefined,
    states: url.searchParams.get("states") || "all",
    sort_direction: url.searchParams.get("sort_direction") || "asc",
    page_size: Number(url.searchParams.get("page_size") ?? 100),
    continue_token: url.searchParams.get("continue_token") || undefined,
  }

  const items: RemoteSubaccount[] = []
  let continueToken: string | undefined = params.continue_token
  let page = 0
  const maxPages = 20

  do {
    const remote = await searchSubaccounts({ ...params, continue_token: continueToken }).catch((e) => {
      throw new Error("SMTP2GO_ERROR: " + (e instanceof Error ? e.message : String(e)))
    })

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

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

  const ids = items.map((it) => it.subaccount_id ?? it.subaccountId ?? it.id).filter(Boolean)

  type LocalSubaccount = {
    subaccountId: string
    tenantId: string
    tenant: { name: string | null } | null
  }
  const local = (ids.length
    ? await prisma.smtp2goSubaccount.findMany({
        where: { subaccountId: { in: ids as string[] } },
        include: { tenant: true },
      })
    : []) as LocalSubaccount[]

  const map = new Map<string, LocalSubaccount>()
  for (const row of local) {
    map.set(row.subaccountId, row)
  }

  const metas = ids.length
    ? await prisma.subaccountMeta.findMany({
        where: { subaccountId: { in: ids as string[] } },
      })
    : []
  const metaById = new Map<string, (typeof metas)[number]>()
  for (const meta of metas) {
    metaById.set(meta.subaccountId, meta)
  }

  const metrics = ids.length
    ? await prisma.subaccountMetric.findMany({
        where: { subaccountId: { in: ids as string[] } },
        orderBy: { fetchedAt: "desc" },
      })
    : []
  const metricById = new Map<string, (typeof metrics)[number]>()
  for (const metric of metrics) {
    if (!metricById.has(metric.subaccountId)) {
      metricById.set(metric.subaccountId, metric)
    }
  }

  const out = items.flatMap((it) => {
    const subId = it.subaccount_id ?? it.subaccountId ?? it.id
    if (!subId) return []
    const localEntry = map.get(subId)
    const meta = metaById.get(subId)
    const metric = metricById.get(subId)

      return [{
        id: it.id ?? subId,
        name: it.name ?? it.label ?? null,
        subaccountId: subId,
        state: it.state ?? it.status ?? null,
        label: it.label ?? null,
        smtpUsername: it.smtp_username ?? it.smtpUsername ?? null,
        planSize: typeof it.plan_size === "number" ? it.plan_size : null,
        planUsed: typeof it.plan_used === "number" ? it.plan_used : null,
        planRemaining: typeof it.plan_remaining === "number" ? it.plan_remaining : null,
        tenantId: localEntry?.tenantId ?? null,
        tenantName: localEntry?.tenant?.name ?? null,
        cnpj: meta?.cnpj ?? null,
        domain: meta?.domain ?? null,
        dkimVerified: meta?.dkimVerified ?? null,
        rpathVerified: meta?.rpathVerified ?? null,
        dkimSelector: meta?.dkimSelector ?? null,
        rpathSelector: meta?.rpathSelector ?? null,
        domainCheckedAt: meta?.domainCheckedAt ?? null,
        metricEmailCount: metric?.emails ?? null,
        metricBouncePercent: metric?.bouncePercent ?? null,
        metricSpamPercent: metric?.spamPercent ?? null,
        metricRejects: metric?.rejects ?? null,
        metricSoftbounces: metric?.softbounces ?? null,
        metricHardbounces: metric?.hardbounces ?? null,
        metricCycleUsed: metric?.cycleUsed ?? null,
        metricCycleMax: metric?.cycleMax ?? null,
        metricFetchedAt: metric?.fetchedAt ?? null,
      }]
  })

  return NextResponse.json({ subaccounts: out, continueToken: continueToken ?? null })
}

export async function POST(req: Request) {
  try {
    await requireStaff()
  } catch (e: unknown) {
    const msg = e instanceof Error ? e.message : String(e ?? "")
    console.warn("admin/subaccounts: unauthorized access attempt", msg)
    return NextResponse.json({ error: "UNAUTHORIZED" }, { status: 401 })
  }

  const body = await req.json().catch(() => null)
  const action = String(body?.action ?? "")

  try {
    if (action === "add") {
      const fullname = String(body?.fullname ?? "").trim()
      const limit = body?.limit ? Number(body.limit) : undefined
      const cnpj = body?.cnpj ? String(body.cnpj).trim() : ""
      const domain = body?.domain ? String(body.domain).trim().toLowerCase() : ""
      if (!fullname) {
        return NextResponse.json({ error: "MISSING_FULLNAME" }, { status: 400 })
      }
      if (!cnpj) {
        return NextResponse.json({ error: "MISSING_CNPJ" }, { status: 400 })
      }
      if (!domain) {
        return NextResponse.json({ error: "MISSING_DOMAIN" }, { status: 400 })
      }
      const data = await addSubaccount({
        fullname,
        limit,
        dedicated_ip: Boolean(body?.dedicated_ip ?? false),
        archiving: Boolean(body?.archiving ?? false),
        enforce_2fa: Boolean(body?.enforce_2fa ?? false),
        enable_sms: Boolean(body?.enable_sms ?? false),
        sms_limit: body?.sms_limit ? Number(body.sms_limit) : undefined,
      })

      const createdSubaccountId = extractSubaccountId(data)
      if (!createdSubaccountId) {
        return NextResponse.json({ error: "SUBACCOUNT_CREATED_WITHOUT_ID" }, { status: 502 })
      }

      try {
        await addSenderDomain({
          domain,
          subaccount_id: createdSubaccountId,
          auto_verify: true,
        })
      } catch (e: unknown) {
        const msg = e instanceof Error ? e.message : String(e ?? "")
        let rollback: string | null = null
        try {
          await closeSubaccount({ id: createdSubaccountId })
          rollback = "SUBACCOUNT_ROLLED_BACK"
        } catch {
          rollback = "SUBACCOUNT_ROLLBACK_FAILED"
        }
        return NextResponse.json(
          {
            error: `SUBACCOUNT_CREATED_BUT_DOMAIN_ADD_FAILED: ${msg}`,
            subaccountId: createdSubaccountId,
            rollback,
          },
          { status: 502 }
        )
      }

      await prisma.subaccountMeta.upsert({
        where: { subaccountId: createdSubaccountId },
        update: {
          name: fullname,
          cnpj,
          domain,
        },
        create: {
          subaccountId: createdSubaccountId,
          name: fullname,
          cnpj,
          domain,
        },
      })
      return NextResponse.json({ ok: true, data })
    }

    if (action === "edit") {
      const id = String(body?.id ?? "").trim()
      const cnpj = body?.cnpj !== undefined ? String(body.cnpj ?? "").trim() : undefined
      const domain = body?.domain !== undefined ? String(body.domain ?? "").trim().toLowerCase() : undefined
      if (!id) {
        return NextResponse.json({ error: "MISSING_ID" }, { status: 400 })
      }
      const data = await editSubaccount({
        id,
        fullname: body?.fullname ? String(body.fullname).trim() : undefined,
        limit: body?.limit ? Number(body.limit) : undefined,
        dedicated_ip: body?.dedicated_ip !== undefined ? Boolean(body.dedicated_ip) : undefined,
        archiving: body?.archiving !== undefined ? Boolean(body.archiving) : undefined,
        enforce_2fa: body?.enforce_2fa !== undefined ? Boolean(body.enforce_2fa) : undefined,
        enable_sms: body?.enable_sms !== undefined ? Boolean(body.enable_sms) : undefined,
        sms_limit: body?.sms_limit ? Number(body.sms_limit) : undefined,
      })

      const updates: Record<string, unknown> = {}
      if (body?.fullname !== undefined) updates.name = String(body.fullname ?? "").trim() || null
      if (cnpj !== undefined) updates.cnpj = cnpj || null
      if (domain !== undefined) updates.domain = domain || null

      if (domain) {
        try {
          await addSenderDomain({
            domain,
            subaccount_id: id,
            auto_verify: true,
          })
        } catch {
          // keep update in DB even if domain already exists or provider refuses duplicate
        }
      }

      if (Object.keys(updates).length > 0) {
        await prisma.subaccountMeta.upsert({
          where: { subaccountId: id },
          update: updates,
          create: {
            subaccountId: id,
            name: (updates.name as string | null | undefined) ?? null,
            cnpj: (updates.cnpj as string | null | undefined) ?? null,
            domain: (updates.domain as string | null | undefined) ?? null,
          },
        })
      }
      return NextResponse.json({ ok: true, data })
    }

    if (action === "close") {
      const id = String(body?.id ?? "").trim()
      if (!id) {
        return NextResponse.json({ error: "MISSING_ID" }, { status: 400 })
      }
      const data = await closeSubaccount({ id, email: body?.email ? String(body.email) : undefined })
      return NextResponse.json({ ok: true, data })
    }

    if (action === "reopen") {
      const id = String(body?.id ?? "").trim()
      if (!id) {
        return NextResponse.json({ error: "MISSING_ID" }, { status: 400 })
      }
      const data = await reopenSubaccount({ id, email: body?.email ? String(body.email) : undefined })
      return NextResponse.json({ ok: true, data })
    }

    return NextResponse.json({ error: "INVALID_ACTION" }, { status: 400 })
  } catch (e: unknown) {
    const msg = e instanceof Error ? e.message : String(e ?? "")
    return NextResponse.json({ error: msg || "SMTP2GO_ERROR" }, { status: 500 })
  }
}
