import { sdk } from '../sdk'
import { configFile } from '../file-models/config.json'
import { randomBytes, scryptSync } from 'crypto'

const { InputSpec, Value } = sdk

const SCRYPT_KEYLEN = 64

const inputSpec = InputSpec.of({
  recap_admin_username: Value.text({
    name: 'Admin Username',
    description:
      'Username required at the login screen. Defaults to "admin".',
    required: true,
    default: 'admin',
    minLength: 1,
    maxLength: 64,
  }),
  recap_admin_password: Value.text({
    name: 'Admin Password',
    description:
      'Password required at the login screen. Must be at least 8 characters. Leave blank to disable the login gate.',
    required: false,
    default: null,
    masked: true,
    minLength: 0,
    maxLength: 256,
  }),
  recap_admin_password_confirm: Value.text({
    name: 'Confirm Password',
    description: 'Re-enter the password to confirm.',
    required: false,
    default: null,
    masked: true,
    minLength: 0,
    maxLength: 256,
  }),
})

export const setAdminPassword = sdk.Action.withInput(
  'set-admin-password',

  async ({ effects }) => ({
    name: 'Set Admin Password',
    description:
      'Set a username and password that gate the Recaps web UI. Anyone visiting the site (LAN or clearnet) must log in before reaching the activation screen. Leave the password blank to disable the gate.',
    warning: null,
    allowedStatuses: 'any',
    group: 'Setup',
    visibility: 'enabled',
  }),

  inputSpec,

  async ({ effects }) => {
    const config = await configFile.read().once()
    return {
      recap_admin_username: config?.recap_admin_username || 'admin',
      recap_admin_password: undefined,
      recap_admin_password_confirm: undefined,
    }
  },

  async ({ effects, input }) => {
    const username = (input.recap_admin_username || '').trim()
    const password = input.recap_admin_password || ''
    const confirm = input.recap_admin_password_confirm || ''

    if (!username) {
      throw new Error('Username is required.')
    }

    if (password === '' && confirm === '') {
      // Disable the gate: clear hash + salt, keep username for next time.
      await configFile.merge(effects, {
        recap_admin_username: username,
        recap_admin_password_hash: '',
        recap_admin_password_salt: '',
      })
      return null
    }

    if (password !== confirm) {
      throw new Error('Password and confirmation do not match.')
    }
    if (password.length < 8) {
      throw new Error('Password must be at least 8 characters.')
    }

    const salt = randomBytes(16).toString('hex')
    const hash = scryptSync(password, salt, SCRYPT_KEYLEN).toString('hex')

    const existing = await configFile.read().once()
    const sessionSecret =
      existing?.recap_admin_session_secret && existing.recap_admin_session_secret.length > 0
        ? existing.recap_admin_session_secret
        : randomBytes(32).toString('hex')

    await configFile.merge(effects, {
      recap_admin_username: username,
      recap_admin_password_hash: hash,
      recap_admin_password_salt: salt,
      recap_admin_session_secret: sessionSecret,
    })

    return null
  },
)
