-- vim: ft=terra
local pstr = lib.mem.ptr(int8)
local terra 
login_form(co: &lib.srv.convo, user: &lib.store.actor, creds: &lib.store.credset, msg: pstr)
	var doc = [lib.srv.convo.page] {
		title = 'instance logon';
		class = 'login';
		cache = false;
	}

	var how = co:ppostv('how')

	if user == nil then
		var form = data.view.login_username {
			loginmsg = msg;
		}
		if form.loginmsg.ptr == nil then
			form.loginmsg = 'identify yourself for access to this instance.'
		end
		doc.body = form:tostr()
	elseif creds:sz() == 0 then
		co:complain(403,'access denied','your host is not eligible to authenticate as this user')
		return
	elseif creds:sz() == 1 or how:ref() then
		var newcreds: lib.store.credset
		if how:ref() then
			if     how:cmp('pw')    then newcreds = creds and [lib.store.credset.bits.pw]
			elseif how:cmp('chlg')  then newcreds = creds and [lib.store.credset.bits.challenge]
			elseif how:cmp('otp')   then newcreds = creds and [lib.store.credset.bits.otp]
			elseif how:cmp('trust') then newcreds = creds and [lib.store.credset.bits.trust]
			else co:complain(400, 'bad request', 'the requested authentication method is not available') return end
			creds = &newcreds
		end

		if creds.trust() then
			-- TODO log in immediately
			return
		end

		var ch = data.view.login_challenge {
			handle = user.handle;
			name = lib.coalesce(user.nym, user.handle);
		}
		if creds.pw() then
			ch.challenge = 'enter the password associated with your account'
			ch.label = 'password'
			ch.method = 'pw'
			ch.inputfield = '<input type="password" autocomplete="current-password" name="response" id="response" autofocus required>';
		elseif creds.otp() then
			ch.challenge = 'enter a valid one-time password for your account'
			ch.label = 'OTP code'
			ch.method = 'otp'
			ch.inputfield = '<input type="text" autocomplete="one-time-code" name="response" id="response" autofocus required>';
		elseif creds.challenge() then
			var tok   = co:stra(128)
			var chlg  = co:stra(128)
			var input = co:stra(256)
			var time = lib.osclock.time(nil)

			lib.crypt.cryptogram(&tok,6)
			chlg:lpush 'sign the challenge token <code style="display:block;user-select: all">'
			    :push(tok.buf,tok.sz)
				:lpush '</code>'

			ch.challenge = chlg:finalize()
			ch.label = 'digest'
			ch.method = 'challenge'
			input:lpush '<textarea autocomplete="one-time-code" name="response" id="response" autofocus required></textarea><input type="hidden" name="time" value="'
			     :shpush(time)
				 :lpush '"><input type="hidden" name="token" value="'
			     :push(tok.buf,tok.sz)
			tok:shpush(time)
			var hmac = lib.crypt.hmacp(&co.srv.pool,
							lib.crypt.alg.sha256,
							co.srv.cfg.secret:blob(),
							tok:finalize())
			input:lpush '"><input type="hidden" name="vfy" value="'
				 :shpush(lib.math.truncate64(hmac.ptr, hmac.ct)) -- FIXME this is probably not very secure...
				 :lpush '">'
				 
			ch.inputfield = input:finalize()
		else
			co:complain(400,'login failure','no usable login methods are available')
			return
		end

		doc.body = ch:poolstr(&co.srv.pool)
	else -- pick a method
		var a = co:stra(400)
		var username = lib.html.sanitize(&co.srv.pool, pstr{user.handle,0}, true)
		a:lpush '<form action="/login" method="post" class="auth-select"><p>multiple authentication mechanisms are available. select one to continue.</p><menu><input type="hidden" name="user" value="':ppush(username):lpush'">'
		if creds.trust()     then a:lpush '<button name="how" value="trust">trust</button>'    end
		if creds.pw()        then a:lpush '<button name="how" value="pw">password</button>'    end
		if creds.otp()       then a:lpush '<button name="how" value="otp">TOTP code</button>'  end
		if creds.challenge() then a:lpush '<button name="how" value="chlg">challenge</button>' end
		a:lpush '</menu></form>'
		doc.body = a:finalize()
	end

	co:stdpage(doc)
	--doc.body:free()
end

return login_form
