/**
 * ITA e-invoice query helpers — ita-einvoice (wave-10 leaf 5).
 *
 * Covers:
 *  - getEinvoiceConfig      — load + decrypt ITA credentials for a tenant
 *  - updateEinvoiceConfig   — encrypt + store ITA API key + business number
 *  - getEinvoiceStatus      — check if invoice has been submitted to ITA
 *  - markInvoiceAsSubmitted — record ITA acknowledgment number
 *
 * Config is stored in adapter_credentials using adapter_id = 'einvoice:ita'.
 * Encryption uses INTEGRATION_ENCRYPTION_KEY (env) via encryptCredential /
 * decryptCredential from @zync/utils.
 *
 * Route files MUST NOT import raw Drizzle tables — they call these helpers.
 */
import { and, eq, desc } from 'drizzle-orm'
import { z } from 'zod'
import type { Db } from '../client'
import { adapterCredentials } from '../schema/communications'
import { einvoiceSubmissions } from '../schema/einvoice'
import { encryptCredential, decryptCredential } from '@zync/utils'
import { auditLog } from './_audit-forward'

// ── Constants ─────────────────────────────────────────────────────────────────

const ITA_ADAPTER_ID = 'einvoice:ita'

// ── Zod schemas ───────────────────────────────────────────────────────────────

export const updateEinvoiceConfigSchema = z.object({
  businessNumber: z.string().min(1).max(50),
  apiKey: z.string().min(1).max(500),
  autoSubmit: z.boolean().optional().default(false),
})

export type UpdateEinvoiceConfigInput = z.infer<typeof updateEinvoiceConfigSchema>

// ── Public interfaces ─────────────────────────────────────────────────────────

export interface EinvoiceConfig {
  businessNumber: string
  apiKey: string // plaintext — only returned by getEinvoiceConfig
  autoSubmit: boolean
  configured: true
}

export interface EinvoiceConfigSummary {
  businessNumber: string
  autoSubmit: boolean
  configured: boolean
}

export interface EinvoiceStatus {
  submitted: boolean
  ackNumber: string | null
  submittedAt: string | null
  status: string | null
}

// ── Helpers ────────────────────────────────────────────────────────────────────

function getEncryptionKey(): string {
  const key = process.env['INTEGRATION_ENCRYPTION_KEY']
  if (!key) throw new Error('INTEGRATION_ENCRYPTION_KEY env not set')
  return key
}

// ── Queries ───────────────────────────────────────────────────────────────────

/**
 * Load and decrypt ITA credentials for a tenant.
 * Returns null if not configured.
 */
export async function getEinvoiceConfig(
  db: Db,
  tenantId: string,
): Promise<EinvoiceConfig | null> {
  const rows = await db
    .select()
    .from(adapterCredentials)
    .where(
      and(
        eq(adapterCredentials.tenantId, tenantId),
        eq(adapterCredentials.adapterId, ITA_ADAPTER_ID),
      ),
    )
    .limit(1)

  const row = rows[0]
  if (!row) return null

  const encryptionKey = getEncryptionKey()
  const plaintext = await decryptCredential(
    { ciphertext: row.ciphertext, iv: row.iv, authTag: row.authTag },
    encryptionKey,
  )
  const secrets = JSON.parse(plaintext) as { apiKey: string }
  const meta = row.metadata as { businessNumber?: string; autoSubmit?: boolean }

  return {
    configured: true,
    businessNumber: meta.businessNumber ?? '',
    apiKey: secrets.apiKey,
    autoSubmit: meta.autoSubmit ?? false,
  }
}

/**
 * Return non-secret summary (no API key) for GET settings endpoint.
 */
export async function getEinvoiceConfigSummary(
  db: Db,
  tenantId: string,
): Promise<EinvoiceConfigSummary | null> {
  const rows = await db
    .select({
      metadata: adapterCredentials.metadata,
    })
    .from(adapterCredentials)
    .where(
      and(
        eq(adapterCredentials.tenantId, tenantId),
        eq(adapterCredentials.adapterId, ITA_ADAPTER_ID),
      ),
    )
    .limit(1)

  const row = rows[0]
  if (!row) return null

  const meta = row.metadata as { businessNumber?: string; autoSubmit?: boolean }
  return {
    configured: true,
    businessNumber: meta.businessNumber ?? '',
    autoSubmit: meta.autoSubmit ?? false,
  }
}

/**
 * Encrypt + store ITA API key and business number.
 * Writes audit log in the same transaction.
 */
export async function updateEinvoiceConfig(
  db: Db,
  tenantId: string,
  userId: string,
  data: UpdateEinvoiceConfigInput,
): Promise<void> {
  const encryptionKey = getEncryptionKey()
  const secrets = JSON.stringify({ apiKey: data.apiKey })
  const blob = await encryptCredential(secrets, encryptionKey)

  await db.transaction(async (tx) => {
    await tx
      .insert(adapterCredentials)
      .values({
        tenantId,
        adapterId: ITA_ADAPTER_ID,
        ciphertext: blob.ciphertext,
        iv: blob.iv,
        authTag: blob.authTag,
        metadata: {
          businessNumber: data.businessNumber,
          autoSubmit: data.autoSubmit,
        },
      })
      .onConflictDoUpdate({
        target: [adapterCredentials.tenantId, adapterCredentials.adapterId],
        set: {
          ciphertext: blob.ciphertext,
          iv: blob.iv,
          authTag: blob.authTag,
          metadata: {
            businessNumber: data.businessNumber,
            autoSubmit: data.autoSubmit,
          },
          updatedAt: new Date(),
        },
      })

    await tx.insert(auditLog).values({
      tenantId,
      actorId: userId,
      actorType: 'user',
      entityType: 'einvoice_config',
      entityId: tenantId,
      action: 'settings.updated',
      changes: { field: ['einvoice_config', 'einvoice_config'] as [unknown, unknown] },
    })
  })
}

/**
 * Check if invoice has been submitted to ITA.
 */
export async function getEinvoiceStatus(
  db: Db,
  tenantId: string,
  invoiceId: string,
): Promise<EinvoiceStatus> {
  const rows = await db
    .select()
    .from(einvoiceSubmissions)
    .where(
      and(
        eq(einvoiceSubmissions.tenantId, tenantId),
        eq(einvoiceSubmissions.invoiceId, invoiceId),
      ),
    )
    .orderBy(desc(einvoiceSubmissions.submittedAt))
    .limit(1)

  const row = rows[0]
  if (!row) return { submitted: false, ackNumber: null, submittedAt: null, status: null }

  return {
    submitted: row.status === 'submitted',
    ackNumber: row.ackNumber,
    submittedAt: row.submittedAt.toISOString(),
    status: row.status,
  }
}

/**
 * Record ITA acknowledgment. Creates or updates the submission record.
 * Writes audit log in the same transaction.
 */
export async function markInvoiceAsSubmitted(
  db: Db,
  tenantId: string,
  userId: string,
  invoiceId: string,
  ackNumber: string,
): Promise<void> {
  await db.transaction(async (tx) => {
    await tx
      .insert(einvoiceSubmissions)
      .values({
        tenantId,
        invoiceId,
        ackNumber,
        status: 'submitted',
        submittedBy: userId,
      })
      .onConflictDoUpdate({
        target: [einvoiceSubmissions.tenantId, einvoiceSubmissions.invoiceId],
        set: {
          ackNumber,
          status: 'submitted',
          errorMessage: null,
          submittedBy: userId,
          submittedAt: new Date(),
        },
      })

    await tx.insert(auditLog).values({
      tenantId,
      actorId: userId,
      actorType: 'user',
      entityType: 'invoice',
      entityId: invoiceId,
      action: 'einvoice.submitted',
      changes: { ack_number: [null, ackNumber] as [unknown, unknown] },
    })
  })
}
