import { i18n } from '../i18n'
import { sdk } from '../sdk'
import { DATA_MOUNT_PATH, IMAGE_ID } from '../utils'

/**
 * "Configure Digest SMTP" action.
 *
 * The CRM sends a daily activity digest by email. This is the package's OWN
 * dedicated SMTP account (per-package custom SMTP) — it is independent of any
 * StartOS system-wide SMTP account, so the digest can go through a mailbox of
 * its own without one being configured server-wide.
 *
 * Mirrors setAnthropicApiKey: the credentials are written to files under
 *   /data/secrets/smtp/{host,port,from,username,password,security}
 * and docker_entrypoint.sh reads them ONCE at boot and exports SMTP_* into the
 * server process. The backend only ever reads SMTP_* env, so dev (.env) and the
 * box share one code path. A service RESTART is required to pick up changes.
 *
 * Security: the password is piped over STDIN (never argv/env) so it can't appear
 * in a process listing — exactly the property setAnthropicApiKey relies on. The
 * other fields (host/port/from/username/security) are not secret and go via env.
 * Each field lands in its own file so the entrypoint reads it with a plain `cat`
 * (no shell-eval of operator-supplied values).
 */

const { InputSpec, Value } = sdk

const SMTP_DIR = `${DATA_MOUNT_PATH}/secrets/smtp`

// Always (re)writes host/port/from/username/security from env. The password is
// rewritten only when one was entered (piped on stdin), so re-saving to change
// the host doesn't wipe a previously stored password.
function writeScript(includePassword: boolean): string {
  // umask 077 makes every file owner-only at creation; the explicit chmods are a
  // belt-and-suspenders that name each file (matches setAnthropicApiKey, and
  // avoids a glob that could mis-expand on an empty dir).
  const steps = [
    'set -eu',
    'umask 077',
    'mkdir -p "$DATA_DIR/secrets/smtp"',
    'chmod 700 "$DATA_DIR/secrets" "$DATA_DIR/secrets/smtp" 2>/dev/null || true',
    'printf %s "$SMTP_HOST"     > "$DATA_DIR/secrets/smtp/host"',
    'printf %s "$SMTP_PORT"     > "$DATA_DIR/secrets/smtp/port"',
    'printf %s "$SMTP_FROM"     > "$DATA_DIR/secrets/smtp/from"',
    'printf %s "$SMTP_USERNAME" > "$DATA_DIR/secrets/smtp/username"',
    'printf %s "$SMTP_SECURITY" > "$DATA_DIR/secrets/smtp/security"',
    'chmod 600 "$DATA_DIR/secrets/smtp/host" "$DATA_DIR/secrets/smtp/port" "$DATA_DIR/secrets/smtp/from" "$DATA_DIR/secrets/smtp/username" "$DATA_DIR/secrets/smtp/security"',
  ]
  if (includePassword) {
    // Password from stdin, stripped of stray CR/LF a paste may carry.
    steps.push('tr -d \'\\n\\r\' > "$DATA_DIR/secrets/smtp/password"')
    steps.push('chmod 600 "$DATA_DIR/secrets/smtp/password"')
  }
  return steps.join('; ')
}

export const inputSpec = InputSpec.of({
  host: Value.text({
    name: i18n('SMTP Host'),
    description: i18n('SMTP server hostname, e.g. smtp.gmail.com or email-smtp.us-east-1.amazonaws.com.'),
    warning: null,
    required: true,
    default: null,
    masked: false,
    placeholder: 'smtp.example.com',
  }),
  port: Value.number({
    name: i18n('SMTP Port'),
    description: i18n('Usually 587 for STARTTLS, 465 for TLS/SSL, or 25 for an unauthenticated relay.'),
    warning: null,
    required: true,
    default: 587,
    integer: true,
    min: 1,
    max: 65535,
    placeholder: '587',
  }),
  security: Value.select({
    name: i18n('Connection Security'),
    description: i18n('STARTTLS (port 587) or TLS/SSL (port 465) for hosted providers; None only for a trusted LAN relay.'),
    default: 'starttls',
    values: {
      starttls: i18n('STARTTLS'),
      tls: i18n('TLS / SSL'),
      none: i18n('None (plaintext)'),
    },
  }),
  fromAddress: Value.text({
    name: i18n('From Address'),
    description: i18n('The address the digest is sent from. Defaults to the username if left blank.'),
    warning: null,
    required: false,
    default: null,
    masked: false,
    placeholder: 'crm-digest@example.com',
  }),
  username: Value.text({
    name: i18n('SMTP Username'),
    description: i18n('Login for the SMTP account. Leave blank only for an unauthenticated relay.'),
    warning: null,
    required: false,
    default: null,
    masked: false,
    placeholder: 'crm-digest@example.com',
  }),
  password: Value.text({
    name: i18n('SMTP Password'),
    description: i18n(
      'Password or app-password for the SMTP account. Stored only on this server ' +
        'at /data/secrets/smtp/password (owner-only, 0600). Leave blank to keep the ' +
        'currently saved password unchanged.',
    ),
    warning: null,
    required: false,
    default: null,
    masked: true,
    placeholder: '••••••••',
  }),
})

export const configureDigestSmtp = sdk.Action.withInput(
  // id
  'configure-digest-smtp',

  // metadata
  async ({ effects }) => ({
    name: i18n('Configure Digest SMTP'),
    description: i18n(
      'Set the SMTP account the CRM uses to email the daily activity digest. ' +
        'This is a dedicated mailbox for this service — it does not require a ' +
        'StartOS system-wide SMTP account. Credentials are stored only on this ' +
        'server under /data/secrets/smtp/ (owner-only). Restart the service after ' +
        'saving for the change to take effect.',
    ),
    warning: i18n(
      'After saving, restart Ten31 Database (Stop, then Start) so the new SMTP ' +
        'settings are loaded. The running service does not pick them up until it restarts.',
    ),
    allowedStatuses: 'any',
    group: null,
    visibility: 'enabled',
  }),

  // form input specification
  inputSpec,

  // pre-fill: never read the secret back out — always present a blank form.
  async ({ effects, prefill }) => null,

  // execution
  async ({ effects, input }) => {
    const includePassword = !!(input.password && input.password.length > 0)

    const subcontainer = await sdk.SubContainer.of(
      effects,
      { imageId: IMAGE_ID },
      sdk.Mounts.of().mountVolume({
        volumeId: 'main',
        subpath: null,
        mountpoint: DATA_MOUNT_PATH,
        readonly: false,
      }),
      'ten31-database-configure-digest-smtp',
    )

    try {
      const options: { env: Record<string, string>; input?: string } = {
        env: {
          DATA_DIR: DATA_MOUNT_PATH,
          SMTP_HOST: input.host,
          SMTP_PORT: String(input.port),
          SMTP_FROM: input.fromAddress ?? '',
          SMTP_USERNAME: input.username ?? '',
          SMTP_SECURITY: input.security,
        },
      }
      // The password is piped in over stdin — never argv/env — so it cannot
      // appear in a process listing.
      if (includePassword) options.input = input.password ?? ''

      await subcontainer.execFail(
        ['/bin/sh', '-c', writeScript(includePassword)],
        options,
        60 * 1000,
      )
    } finally {
      await subcontainer.destroy()
    }

    return {
      version: '1',
      title: i18n('Digest SMTP settings saved'),
      message: i18n(
        `SMTP settings were written to ${SMTP_DIR} (owner-only). ` +
          (includePassword ? '' : 'The existing password was kept unchanged. ') +
          'IMPORTANT: restart Ten31 Database now (Stop, then Start) so the digest ' +
          'mailer picks up the new settings, then send a test (admin API: POST ' +
          '/api/admin/digest/test-email) to verify delivery.',
      ),
      result: null,
    }
  },
)
