import { z } from 'zod'
import { pikkuFunc } from '#pikku/pikku-types.gen.js'
import { isManagingDirector } from '../../lib/auth-helpers.js'
import { getTemplate } from '../../templates/index.js'

export const PublishResolutionInput = z.object({ resolutionId: z.string() })
export const PublishResolutionOutput = z.object({ resolutionId: z.string(), pdfKey: z.string() })

export const publishResolution = pikkuFunc({
  expose: true,
  description:
    'Render the resolution to PDF, mark as published. Caller must be managing_director.',
  input: PublishResolutionInput,
  output: PublishResolutionOutput,
  permissions: { md: [isManagingDirector] },
  func: async ({ kysely, browser, content, audit }, { resolutionId }) => {
    const r = await kysely
      .selectFrom('resolution')
      .selectAll()
      .where('resolutionId', '=', resolutionId)
      .executeTakeFirstOrThrow()
    if (r.status !== 'draft') {
      throw new Error('Only draft resolutions can be published')
    }

    const template = getTemplate(r.templateKey)
    if (!template) throw new Error(`Unknown template: ${r.templateKey}`)

    const company = await kysely
      .selectFrom('company')
      .selectAll()
      .where('companyId', '=', r.companyId)
      .executeTakeFirstOrThrow()

    const members = await kysely
      .selectFrom('companyMember')
      .innerJoin('user', 'user.id', 'companyMember.userId')
      .select([
        'user.displayName as displayName',
        'companyMember.role as role',
        'companyMember.shares as shares',
      ])
      .where('companyMember.companyId', '=', r.companyId)
      .execute()

    const createdBy = await kysely
      .selectFrom('user')
      .select(['displayName', 'email'])
      .where('id', '=', r.createdBy)
      .executeTakeFirstOrThrow()

    const rawValues = r.fieldValues as unknown
    const values =
      typeof rawValues === 'string'
        ? (JSON.parse(rawValues || '{}') as Record<string, unknown>)
        : ((rawValues as Record<string, unknown>) ?? {})

    const html = template.render({
      values,
      company: {
        name: company.name,
        registryNo: company.registryNo,
        address: company.address,
      },
      members: members as any,
      createdBy,
      date: new Date().toISOString(),
    })

    // Render the resolution HTML to a PDF via the browser service (node
    // puppeteer locally / Cloudflare Browser Rendering on deploy — same API),
    // then store the bytes in R2 through the content service. Keep the browser
    // session warm for reuse; close only the page.
    const key = `${r.companyId}/${r.resolutionId}.pdf`
    const session = await browser.acquire({ keepAlive: 60_000 })
    const page = await session.browser.newPage()
    try {
      await page.setContent(html, { waitUntil: 'networkidle0' })
      const pdfBytes = await page.pdf({
        format: 'A4',
        margin: { top: '24mm', bottom: '24mm', left: '20mm', right: '20mm' },
        printBackground: true,
      })
      // Web ReadableStream (not node:stream) so this runs on CF Workers too.
      const stream = new Response(pdfBytes).body
      if (!stream) throw new Error('Failed to stream rendered PDF')
      await content.writeFile({ bucket: 'resolutions', key, stream })
    } finally {
      await page.close()
      await session.release()
    }

    const publishedAt = new Date().toISOString()
    await kysely
      .updateTable('resolution')
      .set({ status: 'published', pdfKey: key, publishedAt: publishedAt as any })
      .where('resolutionId', '=', resolutionId)
      .execute()

    await audit.onUpdate('resolution', resolutionId, {
      status: 'published',
      pdfKey: key,
      publishedAt,
    })
    return { resolutionId, pdfKey: key }
  },
})
