/**
 * SEPA mandate — shared server helpers for both flows:
 *   (a) annex of the contract package (signed inside the /sign portal)
 *   (b) standalone send/fill/sign from the partner or lead page (/sepa portal)
 *
 * The creditor identity (LogYou, Gläubiger-ID, pre-notification days) comes
 * from runtimeConfig.sepa. The signed mandate is attached to the record it was
 * sent from (C_BPartner → table C_BPartner, lead → Lead_User) with the service
 * token, exactly like the signed contract.
 */
import { buildSepaMandatePdf, sepaFilename, type SepaFilled } from './sepaPdf'
import { buildMandateReference, sepaAcceptanceTexts, type SepaMandateData, type SepaScheme, type SepaLang } from './sepaTexts'
import { attachToStrapi } from '../inbox/strapiAttach'
import { logContactActivity, strapiPublicFileUrl } from './contactActivity'
import { patchOfferConditionsSection } from './offerConditions'
import { writeSigningFile, readSigningFile, markSigningRequestSigned, sha256Hex, type SigningRequest, type SigningDocumentMeta } from './contractSigningDb'
import { mailTransport, brandedEmail, escapeHtml, formatDateTimeHuman, recordLink } from './contractSigning'
import { validateIban, validateBic, normalizeIban, formatIbanDisplay } from '../../../app/utils/iban'

export const APP_BASE_URL = 'https://app.logship.de'
export const NOTIFY_EMAIL = 'info@logyou.de'
export const sepaLink = (token: string) => `${APP_BASE_URL}/sepa/${token}`
export const sepaDocumentLink = (token: string, key: string) => `${APP_BASE_URL}/api/public/sepa/${token}/document/${key}`

export const creditorFromConfig = () => {
  const cfg: any = (useRuntimeConfig() as any).sepa || {}
  return {
    name: String(cfg.creditorName || 'LogYou GmbH'),
    street: String(cfg.creditorStreet || 'Mühlenweg 4'),
    zipCity: String(cfg.creditorZipCity || '35510 Butzbach'),
    country: String(cfg.creditorCountry || 'Deutschland'),
    creditorId: String(cfg.creditorId || '').trim(),
    prenotificationDays: Number(cfg.prenotificationDays || 5)
  }
}

export interface SepaSetup {
  scheme?: SepaScheme
  recurring?: boolean
  mandateReference?: string
  prenotificationDays?: number
}

/** Mandate data for a record (reference generated when not given). */
export const buildMandateData = (source: 'lead' | 'partner', recordId: number, setup: SepaSetup = {}, debtor: SepaMandateData['debtor'] = {}): SepaMandateData => {
  const creditor = creditorFromConfig()
  if (setup.prenotificationDays && setup.prenotificationDays > 0) creditor.prenotificationDays = Number(setup.prenotificationDays)
  const ref = String(setup.mandateReference || '').trim().toUpperCase().replace(/[^A-Z0-9\-/]/g, '').slice(0, 35)
  return {
    scheme: setup.scheme === 'CORE' ? 'CORE' : 'B2B',
    recurring: setup.recurring !== false,
    mandateReference: ref || buildMandateReference(source, recordId, Math.random().toString(36).slice(2, 6)),
    creditor,
    debtor: debtor || {}
  }
}

/** Debtor input from a portal (validated + normalised). Returns { ok, message, values }. */
export const validateDebtorInput = (raw: any, lang: SepaLang): { ok: boolean; message: string; values: SepaFilled } => {
  const en = lang === 'en'
  const s = (v: any, n: number) => String(v ?? '').trim().slice(0, n)
  const values: SepaFilled = {
    company: s(raw?.company, 200),
    accountHolder: s(raw?.accountHolder, 200),
    street: s(raw?.street, 200),
    zipCity: s(raw?.zipCity, 200),
    country: s(raw?.country, 80),
    iban: normalizeIban(raw?.iban),
    bic: s(raw?.bic, 11).toUpperCase().replace(/\s+/g, ''),
    bank: s(raw?.bank, 120),
    place: s(raw?.place, 120),
    date: s(raw?.date, 20)
  }
  if (!values.accountHolder) return { ok: false, message: en ? 'Account holder is required' : 'Kontoinhaber ist ein Pflichtfeld', values }
  const iban = validateIban(values.iban)
  if (!iban.valid) {
    const why = iban.reason === 'length' ? (en ? `an ${iban.country} IBAN has ${iban.expectedLength} characters` : `eine ${iban.country}-IBAN hat ${iban.expectedLength} Zeichen`)
      : iban.reason === 'checksum' ? (en ? 'check digits do not match' : 'Prüfziffer stimmt nicht')
      : iban.reason === 'country' ? (en ? 'unknown country code' : 'unbekanntes Länderkürzel')
      : (en ? 'invalid format' : 'ungültiges Format')
    return { ok: false, message: en ? `Invalid IBAN (${why})` : `Ungültige IBAN (${why})`, values }
  }
  if (!iban.sepa) return { ok: false, message: en ? 'The IBAN is not from a SEPA country' : 'Die IBAN stammt nicht aus einem SEPA-Land', values }
  if (values.bic && !validateBic(values.bic)) return { ok: false, message: en ? 'Invalid BIC' : 'Ungültiger BIC', values }
  return { ok: true, message: '', values }
}

const maskIban = (iban: string) => {
  const n = normalizeIban(iban)
  if (n.length < 8) return n
  return formatIbanDisplay(n.slice(0, 4) + '*'.repeat(n.length - 8) + n.slice(-4))
}

export interface SepaFinalizeResult {
  signedAt: string
  signedDocuments: SigningDocumentMeta[]
  signedFilename: string
  attachedUrl: string
  warnings: string[]
}

/**
 * Render the filled + signed mandate, store it under the signing token and
 * attach it to the record. Shared by finalizeSignature (contract annex) and
 * the standalone /sepa portal. Never throws after the PDF exists — every
 * downstream step is fail-soft and reported in `warnings`.
 */
export const renderAndAttachSignedMandate = async (event: any, req: SigningRequest, mandate: SepaMandateData, values: SepaFilled, signature: { imageDataUrl: string; signedAt: string; signerName: string; ip: string }, blankSha256?: string): Promise<{ meta: SigningDocumentMeta; buffer: Buffer; attachedUrl: string; warnings: string[] }> => {
  const warnings: string[] = []
  const lang: SepaLang = req.language === 'en' ? 'en' : 'de'
  const buffer = await buildSepaMandatePdf({
    data: mandate,
    language: lang,
    filled: { ...values, iban: formatIbanDisplay(values.iban || ''), date: values.date || new Date(signature.signedAt).toLocaleDateString(lang === 'en' ? 'en-GB' : 'de-DE', { timeZone: 'Europe/Berlin' }) },
    signature: { imageDataUrl: signature.imageDataUrl, signedAt: signature.signedAt, signerName: signature.signerName, ip: signature.ip, token: req.token, sha256OfBlank: blankSha256 }
  })
  const filename = sepaFilename(lang, mandate.mandateReference, lang === 'en' ? '_signed' : '_unterschrieben')
  writeSigningFile(req.token, filename, buffer)
  const meta: SigningDocumentMeta = { key: 'sepa', title: lang === 'en' ? 'SEPA Direct Debit Mandate (signed)' : 'SEPA-Lastschriftmandat (unterschrieben)', filename, sha256: sha256Hex(buffer), size: buffer.length, signed: true }

  let attachedUrl = ''
  try {
    if (!(req.recordId > 0)) throw new Error('no record id')
    const config: any = useRuntimeConfig()
    const up = await attachToStrapi(event, config.api?.idempieretoken || '', {
      tableName: req.source === 'lead' ? 'Lead_User' : 'C_BPartner',
      recordId: req.recordId,
      recordUu: req.recordUu || undefined,
      buffer,
      filename,
      mimeType: 'application/pdf'
    })
    attachedUrl = strapiPublicFileUrl(up.fileUrl)
  } catch (err: any) {
    console.error('[SEPA] attach signed mandate failed:', err?.message || err)
    warnings.push('attach')
  }
  return { meta, buffer, attachedUrl, warnings }
}

/** Summary persisted on offer_conditions.sepa (never the full IBAN). */
export const mandateSummary = (mandate: SepaMandateData, values: SepaFilled, signedAt: string, filename: string, token: string, attachedUrl: string) => ({
  signedAt,
  mandateReference: mandate.mandateReference,
  scheme: mandate.scheme,
  recurring: mandate.recurring,
  accountHolder: values.accountHolder || '',
  company: values.company || '',
  ibanMasked: maskIban(values.iban || ''),
  ibanCountry: normalizeIban(values.iban || '').slice(0, 2),
  bic: values.bic || '',
  bank: values.bank || '',
  filename,
  token,
  attachedUrl
})

/**
 * Standalone flow: the customer completed the /sepa portal. Renders, attaches,
 * marks the request signed, patches offer_conditions.sepa, logs an activity and
 * mails both sides.
 */
export const finalizeSepaMandate = async (event: any, req: SigningRequest, input: { debtor: SepaFilled; signerName: string; email: string }, signatureDataUrl: string, meta: { ip: string; userAgent: string }, acceptances: Array<{ key: string; text: string; acceptedAt: string }>): Promise<SepaFinalizeResult> => {
  const signedAt = new Date().toISOString()
  const lang: SepaLang = req.language === 'en' ? 'en' : 'de'
  const en = lang === 'en'
  const mandate: SepaMandateData = req.payload?.mandate
  if (!mandate) throw new Error('mandate payload missing')
  const blank = req.documents.find((d) => d.key === 'sepa')

  const rendered = await renderAndAttachSignedMandate(event, req, mandate, input.debtor, { imageDataUrl: signatureDataUrl, signedAt, signerName: input.signerName, ip: meta.ip }, blank?.sha256)
  const warnings = [...rendered.warnings]
  const signedDocuments: SigningDocumentMeta[] = [rendered.meta]
  const signerRecord = { contactName: input.signerName, company: input.debtor.company || req.company, email: input.email, accountHolder: input.debtor.accountHolder, ibanMasked: maskIban(input.debtor.iban || ''), bic: input.debtor.bic || '', bank: input.debtor.bank || '', signedAt, ip: meta.ip, userAgent: meta.userAgent, acceptances }
  markSigningRequestSigned(req.token, signerRecord, signedDocuments, signedAt)

  const config: any = useRuntimeConfig()
  const serviceToken: string = config.api?.idempieretoken || ''

  // offer_conditions.sepa summary (masked IBAN only)
  try {
    if (!(req.recordId > 0)) throw new Error('no record id')
    await patchOfferConditionsSection(event, serviceToken, req.source, req.recordId, 'sepa', mandateSummary(mandate, input.debtor, signedAt, rendered.meta.filename, req.token, rendered.attachedUrl))
  } catch (err: any) {
    console.error('[SEPA] offer_conditions save skipped:', err?.message || err)
    warnings.push('conditions')
  }

  // activity
  try {
    const created = await logContactActivity(event, serviceToken, {
      source: req.source,
      recordId: req.recordId,
      type: 'EM',
      contactEmail: req.email,
      description: en ? `SEPA mandate granted online: ${input.debtor.accountHolder}` : `SEPA-Lastschriftmandat online erteilt: ${input.debtor.accountHolder}`,
      comments: en
        ? `${input.signerName} granted the SEPA ${mandate.scheme} direct debit mandate ${mandate.mandateReference} on ${formatDateTimeHuman(signedAt, 'en')} via the online portal.\nAccount holder: ${input.debtor.accountHolder} · IBAN: ${maskIban(input.debtor.iban || '')}${input.debtor.bic ? ` · BIC: ${input.debtor.bic}` : ''}${input.debtor.bank ? ` · Bank: ${input.debtor.bank}` : ''}\nE-mail: ${input.email}`
        : `${input.signerName} hat das SEPA-${mandate.scheme === 'B2B' ? 'Firmenlastschrift' : 'Basislastschrift'}mandat ${mandate.mandateReference} am ${formatDateTimeHuman(signedAt, 'de')} über das Online-Portal erteilt.\nKontoinhaber: ${input.debtor.accountHolder} · IBAN: ${maskIban(input.debtor.iban || '')}${input.debtor.bic ? ` · BIC: ${input.debtor.bic}` : ''}${input.debtor.bank ? ` · Bank: ${input.debtor.bank}` : ''}\nE-Mail: ${input.email}`,
      documents: [{ title: en ? 'SEPA mandate (signed, PDF)' : 'SEPA-Mandat (unterschrieben, PDF)', url: rendered.attachedUrl || sepaDocumentLink(req.token, 'sepa') }]
    })
    if (!created) warnings.push('activity')
  } catch (err: any) {
    console.error('[SEPA] activity failed:', err?.message || err)
    warnings.push('activity')
  }

  // mails: copy to the customer, notice to LogYou
  const content = readSigningFile(req.token, rendered.meta.filename)
  const attachments = content ? [{ filename: rendered.meta.filename, content, contentType: 'application/pdf' }] : []
  try {
    await mailTransport().sendMail({
      from: NOTIFY_EMAIL,
      to: input.email || req.email,
      subject: en ? `Your SEPA direct debit mandate ${mandate.mandateReference}` : `Ihr SEPA-Lastschriftmandat ${mandate.mandateReference}`,
      html: brandedEmail(
        en ? 'SEPA mandate granted' : 'SEPA-Mandat erteilt',
        en
          ? `<p>Dear ${escapeHtml(input.signerName)},</p><p>thank you — your SEPA direct debit mandate <strong>${escapeHtml(mandate.mandateReference)}</strong> (account holder ${escapeHtml(input.debtor.accountHolder || '')}, IBAN ${escapeHtml(maskIban(input.debtor.iban || ''))}) was granted on ${escapeHtml(formatDateTimeHuman(signedAt, 'en'))}. The signed mandate is attached for your records.</p>`
          : `<p>Guten Tag ${escapeHtml(input.signerName)},</p><p>vielen Dank – Ihr SEPA-Lastschriftmandat <strong>${escapeHtml(mandate.mandateReference)}</strong> (Kontoinhaber ${escapeHtml(input.debtor.accountHolder || '')}, IBAN ${escapeHtml(maskIban(input.debtor.iban || ''))}) wurde am ${escapeHtml(formatDateTimeHuman(signedAt, 'de'))} erteilt. Das unterschriebene Mandat finden Sie im Anhang.</p>`,
        en ? 'You can revoke the mandate in writing at any time.' : 'Sie können das Mandat jederzeit schriftlich widerrufen.'
      ),
      attachments
    })
  } catch (err: any) {
    console.error('[SEPA] customer mail failed:', err?.message || err)
    warnings.push('mail')
  }
  try {
    await mailTransport().sendMail({
      from: NOTIFY_EMAIL,
      to: NOTIFY_EMAIL,
      subject: `SEPA-Mandat erteilt: ${req.company} (${mandate.mandateReference})`,
      html: brandedEmail('SEPA-Mandat erteilt', `<p><strong>${escapeHtml(req.company)}</strong> – ${escapeHtml(input.signerName)} hat das Mandat ${escapeHtml(mandate.mandateReference)} (${mandate.scheme}) am ${escapeHtml(formatDateTimeHuman(signedAt, 'de'))} erteilt.</p><p>Kontoinhaber: ${escapeHtml(input.debtor.accountHolder || '')}<br>IBAN: ${escapeHtml(formatIbanDisplay(input.debtor.iban || ''))}${input.debtor.bic ? `<br>BIC: ${escapeHtml(input.debtor.bic)}` : ''}${input.debtor.bank ? `<br>Bank: ${escapeHtml(input.debtor.bank)}` : ''}</p><p><a href="${recordLink(req.source, req.recordId)}">Datensatz öffnen</a></p>`, `IP ${escapeHtml(meta.ip)}`),
      attachments
    })
  } catch (err: any) {
    console.error('[SEPA] internal mail failed:', err?.message || err)
  }

  return { signedAt, signedDocuments, signedFilename: rendered.meta.filename, attachedUrl: rendered.attachedUrl, warnings }
}

export { sepaAcceptanceTexts }
