/**
 * Onboarding business-info + progress persistence.
 *
 * applyBusinessInfo mirrors PATCH /api/settings/business (settings-module):
 * shallow-merge business fields into tenants.settings JSONB and set tenants.name.
 * That route is not yet implemented; this helper is the canonical write path until it lands.
 */
import { eq } from '@zync/db'
import type { Db, DbTx } from '@zync/db'
import { auditLog, tenants } from '@zync/db/schema'
import type { Env } from '@zync/types'
import type { OnboardingProgressBody } from '../routes/onboarding.schema'
import { applyModuleSelection } from './onboarding-modules'
import { applyEmailAdapter } from './onboarding-email-adapter'
import { sendOnboardingInvites } from './onboarding-invites'

type DbOrTx = Db | DbTx

export type BusinessInfoInput = {
  business_name: string
  business_type: 'osek_morshe' | 'company_ltd' | 'osek_patur'
  tax_id: string
  logo_url?: string
  phone?: string
  address?: string
}

/** Shallow-merge business profile keys into tenants.settings (settings-module contract). */
export function buildBusinessSettingsPatch(
  data: BusinessInfoInput,
): Record<string, string> {
  const patch: Record<string, string> = {
    business_name: data.business_name,
    business_type: data.business_type,
    tax_id: data.tax_id,
  }
  if (data.logo_url !== undefined) patch.logo_url = data.logo_url
  if (data.phone !== undefined) patch.phone = data.phone
  if (data.address !== undefined) patch.address = data.address
  return patch
}

export async function applyBusinessInfo(
  db: DbOrTx,
  tenantId: string,
  data: BusinessInfoInput,
): Promise<void> {
  const [row] = await db
    .select({ settings: tenants.settings })
    .from(tenants)
    .where(eq(tenants.id, tenantId))
    .limit(1)

  const current = (row?.settings ?? {}) as Record<string, unknown>
  const patch = buildBusinessSettingsPatch(data)
  const newSettings = { ...current, ...patch }

  await db
    .update(tenants)
    .set({
      name: data.business_name,
      settings: newSettings,
    })
    .where(eq(tenants.id, tenantId))
}

export interface PersistOnboardingProgressInput {
  tenantId: string
  actorId: string
  step: OnboardingProgressBody
  currentStep: number
  newStep: number
  markCompleted: boolean
  env: Env
  actorIp?: string | null
  requestId?: string | null
}

async function runStepSideEffect(
  db: Db,
  tx: DbTx,
  tenantId: string,
  actorId: string,
  step: OnboardingProgressBody,
  env: Env,
  actorIp?: string | null,
  requestId?: string | null,
): Promise<void> {
  switch (step.step) {
    case 1:
      await applyBusinessInfo(tx, tenantId, step.data)
      break
    case 2:
      await applyModuleSelection(tx, tenantId, step.data.modules, actorId)
      break
    case 3:
      await applyEmailAdapter(
        tx,
        tenantId,
        step.data.email_adapter,
        env.INTEGRATION_ENCRYPTION_KEY,
        step.data.email_adapter === 'smtp' ? step.data.smtp : undefined,
      )
      break
    case 4:
      await sendOnboardingInvites(tx, tenantId, actorId, step.data.invites ?? [], env, {
        actorIp,
        requestId,
      })
      break
    case 5:
      break
  }
}

export async function persistOnboardingProgress(
  db: Db,
  input: PersistOnboardingProgressInput,
): Promise<void> {
  const { tenantId, actorId, step, currentStep, newStep, markCompleted, env, actorIp, requestId } =
    input

  await db.transaction(async (tx) => {
    await runStepSideEffect(db, tx, tenantId, actorId, step, env, actorIp, requestId)

    await tx
      .update(tenants)
      .set({
        onboardingStep: newStep,
        ...(markCompleted ? { onboardingCompleted: true } : {}),
      })
      .where(eq(tenants.id, tenantId))

    await tx.insert(auditLog).values({
      tenantId,
      actorId,
      actorType: 'user',
      entityType: 'tenant',
      entityId: tenantId,
      action: 'onboarding.progress',
      changes: {
        onboarding_step: [currentStep, newStep],
        ...(markCompleted ? { onboarding_completed: [false, true] } : {}),
      },
      ip: actorIp ?? null,
      requestId: requestId ?? null,
    })
  })
}
