import type {
  SecretService,
} from '@pikku/core'
import type { KyselyDB } from './database-types.js'
import {
  JsonConsoleLogger,
  LocalSecretService,
  LocalVariablesService,
} from '@pikku/core/services'
import { CFWorkerSchemaService } from '@pikku/schema-cfworker'
import { LibsqlWebDialect } from '@pikku/kysely-sqlite'
import { Kysely, CamelCasePlugin } from 'kysely'
import './middleware.js'
import { ProjectEmailService } from './services/email-service.js'
import { MailgunEmailService } from './services/mailgun/mailgun.js'
import { RecognitionDossierService } from './services/recognition-dossier-service.js'
import { TranslationService } from './services/translation-service.js'
import {
  pikkuServices,
  pikkuWireServices,
} from '#pikku/function/pikku-function-types.gen.js'

export const createSingletonServices = pikkuServices(async (config, existingServices) => {
  const variables = existingServices?.variables ?? new LocalVariablesService()
  const logger = existingServices?.logger ?? new JsonConsoleLogger()
  const secrets: SecretService = existingServices?.secrets ?? new LocalSecretService(variables)

  if (config.logLevel) {
    logger.setLevel(config.logLevel)
  }

  const schema = existingServices?.schema ?? new CFWorkerSchemaService(logger)

  // On CF Workers Fabric injects DATABASE_URL as a variable but does NOT inject a
  // kysely service (the SQL dialect is project-specific), so construct our own —
  // mirroring the green germantax app. pikku-dev / sandbox still inject kysely.
  let kysely = existingServices?.kysely as Kysely<KyselyDB> | undefined
  if (!kysely) {
    const databaseUrl = await variables.get('DATABASE_URL')
    if (!databaseUrl) {
      throw new Error(
        'DATABASE_URL not set — in dev enable `dev.db` in the pikku config; in prod Fabric injects it',
      )
    }
    kysely = new Kysely<KyselyDB>({
      dialect: new LibsqlWebDialect({ url: databaseUrl }),
      plugins: [new CamelCasePlugin()],
    })
  }

  if (!existingServices?.content) {
    throw new Error('content service was not injected by the runtime (pikku dev / fabric)')
  }
  const content = existingServices.content

  const emailDelegate =
    existingServices?.emailService ??
    new MailgunEmailService(
      variables.get('NODE_ENV') === 'production' ? await secrets.getSecret(config.secrets.mailgunApiKey) : undefined,
      config.domain
    )
  const emailService = new ProjectEmailService(emailDelegate)
  const recognitionDossierService =
    existingServices?.recognitionDossierService ??
    new RecognitionDossierService(kysely, content, logger)

  const translation = existingServices?.translation ?? new TranslationService(kysely)
  if (!existingServices?.translation) {
    await translation.init()
  }

  return {
    ...(existingServices ?? {}),
    config,
    variables,
    secrets,
    schema,
    logger,
    kysely,
    content,
    emailService,
    recognitionDossierService,
    translation,
  }
})

export const createWireServices = pikkuWireServices(async () => ({
}))
