// ═══════════════════════════════════════════════════════════════════════════════════════
//  ERP-ONBOARDING  —  FEATURE-FLAGGED MODULE, OFF BY DEFAULT
//  ─────────────────────────────────────────────────────────────────────────────────────
//  Server-to-server call into the Blut24/LogShip ERP once a registration has been
//  confirmed. It performs exactly two POSTs against the existing ERP routes:
//
//    1. POST {ERP_ONBOARDING_URL}/api/admin/merchant-onboarding/process
//         → creates C_BPartner, C_Location, C_BPartner_Location, AD_Org, AD_User,
//           M_Warehouse, M_Locator, AD_Role, AD_User_Roles and cleans up org access.
//           Returns { status, report, ctx: { bpartnerId, newOrgId, userId, … } }.
//
//    2. POST {ERP_ONBOARDING_URL}/api/admin/merchant-onboarding/send-credentials
//         with resetPassword: true → generates + persists a fresh password and mails
//         the credentials to the contact address.
//
//  ACTIVATION: set BOTH env vars. While either is missing this module logs a single
//  line and no-ops — the ERP-side authentication gate for machine-to-machine calls
//  does not exist yet, so nothing here may ever throw into the confirmation flow.
//
//      ERP_ONBOARDING_URL=https://app.blut24.com      (no trailing slash)
//      ERP_SERVICE_TOKEN=<bearer token accepted by the ERP gate>
//
//  Optional: ERP_ORGANIZATION_ID (operator org the records are created under — the ERP
//  route reads it from the logship_organization_id cookie, which we forward),
//  ERP_COUNTRY_ID, ERP_PRICE_LIST_ID, ERP_GROUP_CLINIC, ERP_GROUP_COURIER.
// ═══════════════════════════════════════════════════════════════════════════════════════
import crypto from 'node:crypto'
import type { RegistrationRow } from './registrationDb'
import { isCourierType, registrationTypeLabelDe } from './registrationTypes'

export type ErpResult = {
  enabled: boolean
  ok: boolean
  status: 'disabled' | 'success' | 'error'
  message: string
  credentialsSent: boolean
  ctx: any
  report: any
}

const env = (key: string, configKey: string): string => {
  const fromEnv = process.env[key]
  if (fromEnv && String(fromEnv).trim()) return String(fromEnv).trim()
  const value = (useRuntimeConfig() as any)[configKey]
  return value ? String(value).trim() : ''
}

export const erpSettings = () => ({
  url: env('ERP_ONBOARDING_URL', 'erpOnboardingUrl').replace(/\/+$/, ''),
  token: env('ERP_SERVICE_TOKEN', 'erpServiceToken'),
  organizationId: env('ERP_ORGANIZATION_ID', 'erpOrganizationId'),
  countryId: env('ERP_COUNTRY_ID', 'erpCountryId'),
  priceListId: env('ERP_PRICE_LIST_ID', 'erpPriceListId'),
  groupClinic: env('ERP_GROUP_CLINIC', 'erpGroupClinic'),
  groupCourier: env('ERP_GROUP_COURIER', 'erpGroupCourier'),
})

export const isErpOnboardingEnabled = (): boolean => {
  const s = erpSettings()
  return !!(s.url && s.token)
}

/** ASCII slug used as AD_Org.Value / AD_User.Value (the login name). */
export const buildShortName = (orgName: string, stoid: string | null | undefined): string => {
  const slug = String(orgName || '')
    .toLowerCase()
    .replace(/ä/g, 'ae').replace(/ö/g, 'oe').replace(/ü/g, 'ue').replace(/ß/g, 'ss')
    .normalize('NFD').replace(/[̀-ͯ]/g, '')
    .replace(/[^a-z0-9]+/g, '-')
    .replace(/^-+|-+$/g, '')
    .slice(0, 24)
    .replace(/-+$/g, '')
  const suffix = stoid ? String(stoid) : crypto.randomBytes(3).toString('hex')
  return `${slug || 'blut24'}-${suffix}`
}

const randomPassword = (len = 12): string => {
  const charset = 'ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnpqrstuvwxyz23456789'
  let out = ''
  for (let i = 0; i < len; i++) out += charset[crypto.randomInt(0, charset.length)]
  return out
}

/** Splits "Dr. Anna Muster" into the ERP's ceoName field (used verbatim). */
const contactName = (reg: RegistrationRow) => String(reg.contact_name || '').trim()

const post = async (url: string, body: any, timeoutMs = 45000): Promise<any> => {
  const s = erpSettings()
  const controller = new AbortController()
  const timer = setTimeout(() => controller.abort(), timeoutMs)
  try {
    return await $fetch(url, {
      method: 'POST',
      body,
      signal: controller.signal,
      headers: {
        'Content-Type': 'application/json',
        // The token MUST travel as the `logship_it` cookie. The ERP resolves its caller
        // through getTokenHelper(), which reads that cookie and nothing else — it never
        // looks at an Authorization header. The one middleware that does translate a
        // Bearer token into that cookie (server/middleware/00.app-auth.ts) fires only on
        // `X-Logship-App: 1`, the bundled Capacitor app's marker, which this is not.
        // Sent as a Bearer header alone the token was silently ignored and onboarding
        // failed on authentication instead of creating the clinic.
        //
        // This is a server-to-server call from the Nitro route — the token is read from
        // private runtime config and never reaches a browser.
        Cookie: [
          `logship_it=${s.token}`,
          ...(s.organizationId ? [`logship_organization_id=${s.organizationId}`] : []),
        ].join('; '),
        // Kept for request attribution in the ERP access log; carries no authority.
        'X-Blut24-Service': '1',
      },
    })
  } finally {
    clearTimeout(timer)
  }
}

/** Body for POST /api/admin/merchant-onboarding/process. */
export const buildProcessBody = (reg: RegistrationRow) => {
  const s = erpSettings()
  // READ PATH: normalised, so a row written under the retired three-type flow still lands
  // in the clinic group. The type selects nothing but the partner GROUP (the coarse
  // persona) — whether the clinic supplies, receives or does both is set in the ERP itself
  // at first sign-in, via C_BPartner.IsVendor / IsCustomer, which may both be true.
  const isCourier = isCourierType(reg.type)
  const body: any = {
    companyName: reg.org_name,
    ceoName: contactName(reg),
    shortName: buildShortName(reg.org_name, reg.stoid),
    street: reg.street,
    zip: reg.plz,
    city: reg.city,
    companyEmail: reg.contact_email,
    phoneWired: reg.contact_phone,
    password: randomPassword(12),
    isFulfillmentCustomer: false,
  }
  if (s.countryId) body.countryId = Number(s.countryId)
  if (s.priceListId) body.priceListId = Number(s.priceListId)
  const groupId = isCourier ? s.groupCourier : s.groupClinic
  if (groupId) body.partnerGroupId = Number(groupId)
  return body
}

/** Body for POST /api/admin/merchant-onboarding/send-credentials. */
export const buildCredentialsBody = (reg: RegistrationRow, ctx: any, username: string) => ({
  toEmail: reg.contact_email,
  username,
  userId: ctx?.userId,
  bpartnerId: ctx?.bpartnerId,
  name: contactName(reg) || reg.org_name,
  description: contactName(reg),
  resetPassword: true,
})

const disabled = (message: string): ErpResult => ({
  enabled: false, ok: false, status: 'disabled', message, credentialsSent: false, ctx: null, report: null,
})

/**
 * Runs the two-step ERP onboarding. NEVER throws: any failure is reported back so the
 * confirmation page still succeeds and operations can retry manually.
 */
export const runErpOnboarding = async (reg: RegistrationRow): Promise<ErpResult> => {
  const s = erpSettings()
  if (!s.url || !s.token) {
    const message = 'ERP-Onboarding übersprungen (ERP_ONBOARDING_URL / ERP_SERVICE_TOKEN nicht gesetzt).'
    console.info(`[blut24][erp] ${message} Registrierung #${reg.id} "${reg.org_name}" `
      + `(${registrationTypeLabelDe(reg.type)}) wartet auf manuelle Anlage.`)
    return disabled(message)
  }

  const processBody = buildProcessBody(reg)
  let processRes: any = null
  try {
    processRes = await post(`${s.url}/api/admin/merchant-onboarding/process`, processBody)
  } catch (err: any) {
    const message = err?.data?.message || err?.message || String(err)
    console.error('[blut24][erp] merchant-onboarding/process fehlgeschlagen:', message)
    return { enabled: true, ok: false, status: 'error', message, credentialsSent: false, ctx: null, report: null }
  }

  if (Number(processRes?.status) !== 200) {
    const message = processRes?.message || 'ERP-Onboarding wurde abgebrochen.'
    console.error('[blut24][erp] merchant-onboarding/process meldet Fehler:', message)
    return {
      enabled: true, ok: false, status: 'error', message, credentialsSent: false,
      ctx: processRes?.ctx ?? null, report: processRes?.report ?? null,
    }
  }

  let credentialsSent = false
  let message = ''
  try {
    const credRes: any = await post(
      `${s.url}/api/admin/merchant-onboarding/send-credentials`,
      buildCredentialsBody(reg, processRes?.ctx, processBody.shortName),
    )
    credentialsSent = Number(credRes?.status ?? 200) === 200
    if (!credentialsSent) message = credRes?.message || 'Zugangsdaten konnten nicht versendet werden.'
  } catch (err: any) {
    message = err?.data?.message || err?.message || String(err)
    console.error('[blut24][erp] send-credentials fehlgeschlagen:', message)
  }

  return {
    enabled: true,
    ok: true,
    status: 'success',
    message,
    credentialsSent,
    ctx: processRes?.ctx ?? null,
    report: processRes?.report ?? null,
  }
}
