// License: LGPL-3.0-or-later
// npm
const h = require('snabbdom/h')
const R = require('ramda')
const validatedForm = require('ff-core/validated-form')
const button = require('ff-core/button')
const flyd = require('flyd')
flyd.flatMap = require('flyd/module/flatmap')
flyd.filter = require('flyd/module/filter')
flyd.mergeAll = require('flyd/module/mergeall')
const scanMerge = require('flyd/module/scanmerge')
// local
const request = require('../common/request')
const formatErr = require('../common/format_response_error')
const createCardStream = require('../cards/create-frp.es6')
const serializeForm = require('form-serialize')
const luhnCheck = require('../common/credit-card-validator.js')

// A component for filling out card data, validating it, saving the card to
// stripe, and then saving a tokenized copy to our servers.

// Form validation constraints, validator functions, and error messages:
var constraints = {
  address_zip: {required: true}
, name: {required: true}
, number: {required: true, cardNumber: true}
, exp_month: {required: true, format: /\d\d?/}
, exp_year: {required: true, format: /\d\d?/}
, cvc: {required: true, format: /\d\d\d\d?/}
}
var validators = { cardNumber: luhnCheck }
var messages = {
  number: {
    required: I18n.t('nonprofits.donate.payment.card.errors.number.presence')
  , cardNumber:  I18n.t('nonprofits.donate.payment.card.errors.number.format')
  }
  , required: I18n.t('nonprofits.donate.payment.card.errors.field.presence')
  , email: I18n.t('nonprofits.donate.payment.card.errors.email.format')
  , format: I18n.t('nonprofits.donate.payment.card.errors.field.format')
}

//  You can pass in the .hideButton boolean if you want to control whether the submit button is shown/hidden
//  Pass in a .card object, which can have default objects for the card form (name, number, cvc, exp_month, etc)
//  Pass in .path to set the endpoint for saving the card
//  Pass in .payload for default data to send to the server for every card save request (such as a request token)
const init = (state) => {
  state = state || {}
  // set defaults
  state = R.merge({
    payload$: flyd.stream(state.payload || {})
  , path$: flyd.stream(state.path || '/cards')
  }, state)

  state.form = validatedForm.init({constraints, validators, messages})
  state.card$ = flyd.merge(flyd.stream(state.card || {}), state.form.validData$)

  // streams of stripe tokenization responses
  const stripeResp$ = flyd.flatMap(createCardStream, state.form.validData$)
  state.stripeRespOk$  = flyd.filter(r => !r.error, stripeResp$)
  const stripeError$ = flyd.map(r => r.error.message, flyd.filter(r =>  r.error, stripeResp$))
 
  // Save the card as a card table on our own db
  // streams of responses
  state.resp$ = flyd.flatMap(
    resp => saveCard(state.payload$(), state.path$(), resp) // cheating on the streams here..
  , state.stripeRespOk$ )

  const ccError$ = flyd.map(R.prop('error'), flyd.filter(resp => resp.error, state.resp$))
  state.saved$ = flyd.filter(resp => !resp.error, state.resp$) 
  state.error$ = flyd.merge(stripeError$, ccError$)

  state.loading$ = scanMerge([
    [state.form.validSubmit$, R.always(true)]
  , [state.error$, R.always(false)]
  , [state.saved$, R.always(false)]
  ], false)

  return state
}


// -- Stream-related functions

// Save the card to our own servers, and return a response stream
const saveCard = (send, path, resp) => {
  send.card = R.merge(send.card, {
     cardholders_name: resp.name
   , name: `${resp.card.brand} *${resp.card.last4}`
   , stripe_card_token: resp.id
   , stripe_card_id: resp.card.id
  })
  return flyd.map(R.prop('body'), request({ path, send, method: 'post' }).load)
}


// -- Virtual DOM

const view = state => {
  var field = validatedForm.field(state.form)
  return validatedForm.form(state.form, h('form.cardForm', [
    h('div.u-background--grey.group.u-padding--8', [
      nameInput(field, state.card$().name)
    , numberInput(field)
    , cvcInput(field)
    , expMonthInput(field)
    , expYearInput(field)
    , zipInput(field, state.card$().address_zip)
    , profileInput(field, app.profile_id) // XXX global
    ])
  , h('div.u-centered.u-marginTop--20', [
      state.hideButton ? '' : button({
        error$: state.hideErrors ? flyd.stream() : state.error$
      , loading$: state.loading$
      , buttonText: I18n.t('nonprofits.donate.payment.card.submit')
      , loadingText: ` ${I18n.t('nonprofits.donate.payment.card.loading')}`
      })
     , h('p.u-fontSize--12.u-marginBottom--0.u-marginTop--10.u-color--grey', [ h('i.fa.fa-lock'), ` ${I18n.t('nonprofits.donate.payment.card.secure_info')}`])
    ])
  ]) )
}


const nameInput = (field, name) => 
  h('fieldset', [ field(h('input', { props: { name: 'name' , value: name || '', placeholder: I18n.t('nonprofits.donate.payment.card.name') } })) ])


const numberInput = field =>
  h('fieldset.col-8', [ field(h('input', {props: { type: 'text' , name: 'number' , placeholder: I18n.t('nonprofits.donate.payment.card.number') } })) ])


const cvcInput = field =>
  h('fieldset.col-right-4.u-relative', [
    field(h('input', { props: { name: 'cvc' , placeholder: I18n.t('nonprofits.donate.payment.card.cvc') } } ))
  , h('img.security-code-image', {
      src: `${app.asset_path}/graphics/cc-security-code.png`
    })
  ])


const expMonthInput = field => {
  var options = R.prepend(
    h('option.default', {props: {value: undefined, selected: true}}, I18n.t('nonprofits.donate.payment.card.month'))
  , R.range(1, 13).map(n => h('option', String(n)))
  )
  return h('fieldset.col-3.u-margin--0', [
    field(h('select.select'
  , { props: {name: 'exp_month'} }
  , options))
  ])
}


const expYearInput = field => {
  var yearRange = R.range(new Date().getFullYear(), new Date().getFullYear() + 15)
  var options = R.prepend(
    h('option.default', {props: {value: undefined, selected: true}}, I18n.t('nonprofits.donate.payment.card.year'))
  , R.map(y => h('option', String(y)), yearRange)
  )
  return h('fieldset.col-left-3.u-margin--0', [
    field(h('select.select'
  , {props: {name: 'exp_year'}}
  , options)) 
  ])
}


const zipInput = (field, zip) => 
  h('fieldset.col-right-6.u-margin--0', [
    field(h('input'
    , { props: {
        type: 'text'
      , name: 'address_zip'
      , value: zip || ''
      , placeholder: I18n.t('nonprofits.donate.payment.card.postal_code')
      }} 
    ))
  ])


const profileInput = (field, profile_id) =>
  field(h('input'
  , { props: {
      type: 'hidden'
    , name: 'profile_id'
    , value: profile_id || ''
    }} 
  ))

module.exports = {view, init}