/**
 * Automatic C_ContactActivity entries for offer/contract interactions.
 *
 * Every customer-facing step of the sales flow leaves a trace on the record's
 * activity timeline (the same `c_contactactivity` rows the lead page's manual
 * "Aktivität" modal creates — see server/api/admin/users/[id]/activity):
 *   - quote sent            (EM)  quote/send.post.ts
 *   - contract sent         (EM)  contract/send.post.ts
 *   - contract signed       (EM)  contractSigning.ts → finalizeSignature()
 *   - quote confirmed       (EM)  quoteConfirmation.ts → finalizeQuoteConfirmation()
 *
 * Documents are referenced INSIDE `Comments` in a machine-readable block the
 * timeline turns into buttons (see app/components/admin/LeadDetailRecordActivity.vue):
 *
 *   …free text…
 *
 *   [Dokumente]
 *   <title> | <url>
 *   <title> | <url>
 *
 * `Description` (255 chars in iDempiere, mandatory) carries the short headline.
 * `Comments` holds the details + the document block.
 *
 * ALWAYS fail-soft: an activity must never break the send/sign/confirm flow.
 * Callers wrap in try/catch anyway; this helper also never throws by itself
 * except when awaited inside a try (it returns null on any failure).
 */
import { string } from 'alga-js'
import fetchHelper from '../fetchHelper'

export const ACTIVITY_DOCS_MARKER = '[Dokumente]'

export type ActivityType = 'EM' | 'ME' | 'PC' | 'TA'

export interface ActivityDocumentRef {
  title: string
  url: string
}

export interface ContactActivityInput {
  source: 'lead' | 'partner'
  recordId: number
  type?: ActivityType
  description: string
  comments?: string
  documents?: ActivityDocumentRef[]
  /** Optional override; otherwise resolved from cookie → record. */
  organizationId?: number | null
  /** Optional override; otherwise the logship_user_id cookie (none for public routes). */
  salesRepId?: number | null
  /** Prefer the AD_User whose e-mail matches (partner source only). */
  contactEmail?: string
  isComplete?: boolean
}

const trim = (s: any, max: number) => {
  const v = String(s ?? '').trim()
  return v.length > max ? v.slice(0, max - 1) + '…' : v
}

/** Public URL of a Strapi upload (`/uploads/x.pdf`) the way AttachmentModal links it. */
export const strapiPublicFileUrl = (fileUrl: string): string => {
  const u = String(fileUrl || '')
  if (!u) return ''
  if (/^https?:\/\//i.test(u)) return u
  const config: any = useRuntimeConfig()
  const base = String(config.public?.strapi || '').replace(/\/$/, '')
  return `${base}/files-api/${u.replace(/^\/?uploads\//, '').replace(/^\//, '')}`
}

/** Compose the Comments text: free text + the parseable document block. */
export const buildActivityComments = (text: string, documents: ActivityDocumentRef[] = []): string => {
  const docs = (documents || []).filter((d) => d && d.url)
  const head = String(text || '').trim()
  if (!docs.length) return trim(head, 1900)
  const block = `${ACTIVITY_DOCS_MARKER}\n${docs.map((d) => `${String(d.title || 'Dokument').replace(/\|/g, '/').trim()} | ${d.url}`).join('\n')}`
  // Comments is a 2000-char column — keep the document block intact, cut the prose.
  const room = 1990 - block.length - 2
  return `${trim(head, Math.max(room, 200))}\n\n${block}`
}

/** Resolve the AD_User the activity hangs on (+ its org) for a lead or partner record. */
const resolveContact = async (event: any, token: string, input: ContactActivityInput): Promise<{ userId: number; orgId: number } | null> => {
  if (!(input.recordId > 0)) return null
  if (input.source === 'lead') {
    const user: any = await fetchHelper(event, `models/ad_user/${input.recordId}`, 'GET', token, null)
    if (!user?.id) return null
    return { userId: Number(user.id), orgId: Number(user?.AD_Org_ID?.id || 0) }
  }
  // Partner: the contact user — matching e-mail first, else the first active one.
  const res: any = await fetchHelper(event, `models/ad_user?$filter=${string.urlEncode(`C_BPartner_ID eq ${input.recordId} AND IsActive eq true`)}&$orderby=Created asc&$top=50`, 'GET', token, null)
  const users: any[] = Array.isArray(res?.records) ? res.records : []
  if (!users.length) return null
  const mail = String(input.contactEmail || '').trim().toLowerCase()
  const pick = (mail && users.find((u) => String(u?.EMail || '').trim().toLowerCase() === mail)) || users[0]
  return { userId: Number(pick.id), orgId: Number(pick?.AD_Org_ID?.id || 0) }
}

/**
 * Write one activity. Returns the created record (or null on any failure).
 * `token` may be a session token (send routes) or the service token (public routes).
 */
export const logContactActivity = async (event: any, token: string, input: ContactActivityInput): Promise<any | null> => {
  try {
    if (!token) return null
    const contact = await resolveContact(event, token, input)
    if (!contact) {
      console.warn('[Activity] no contact user for', input.source, input.recordId, '— activity skipped')
      return null
    }
    let orgId = Number(input.organizationId || 0)
    if (!(orgId > 0)) orgId = Number(getCookie(event, 'logship_organization_id') || 0)
    if (!(orgId > 0)) orgId = contact.orgId
    let salesRepId = Number(input.salesRepId || 0)
    if (!(salesRepId > 0)) salesRepId = Number(getCookie(event, 'logship_user_id') || 0)

    const now = new Date().toJSON().replace(/\.\d{3}Z$/, 'Z')
    const body: any = {
      AD_Org_ID: { id: orgId, tableName: 'AD_Org' },
      AD_User_ID: { id: contact.userId, tableName: 'AD_User' },
      ContactActivityType: { id: input.type || 'EM' },
      description: trim(input.description, 250),
      comments: buildActivityComments(input.comments || '', input.documents || []),
      StartDate: now,
      EndDate: now,
      isComplete: input.isComplete !== false,
      tableName: 'c_contactactivity'
    }
    if (salesRepId > 0) body.SalesRep_ID = { id: salesRepId, tableName: 'AD_User' }
    const res: any = await fetchHelper(event, 'models/c_contactactivity', 'POST', token, body)
    return res?.id ? res : null
  } catch (err: any) {
    console.error('[Activity] create failed:', err?.data?.detail || err?.message || err)
    return null
  }
}
