const lapk = (text)=>`„${text}“`;
const rand = function(a, b){
    if(Array.isArray(a)){
      return a[Math.random()*a.length|0];
    }

    a = Math.ceil(a);
    b = Math.floor(b);
    return Math.floor(Math.random() * (b - a + 1)) + a;

    return (r-a)*(b-a)|0;
  },
  probabilityRand = function(items) {
    let sum = 0;
    for( let i = 0, _i = items.length; i < _i; i++ ){
      const item = items[ i ];
      sum += item.probability |0;
    }
    const theRandom = Math.random();
    let total = 0;

    for( let i = 0, _i = items.length; i < _i; i++ ){
      const item = items[ i ];
      total += item.probability/sum;
      if(theRandom < total)
        return item;
    }
  };

const normalizeText = function(text) {
  return text.trim().toLowerCase().replace(/[^а-я]/g,'').replace(/[аоуеэюёиыя]+/g,'о')
};

const quizTypes = {
  checkbox: [],
  checkboxPhoto: {

  },
  radio: [
    {
      questionCmpAmount: 4,
      probability: 10,
      type: 'Product contains 4 components',
      products: {from: 4, to: 8},
      minSimilarTags: 1,
      from: qB.prebuild.similarTaggedProducts.fn,
      question(income, log){
        return 'В какой из продуктов входят данные ингредиенты: '+income.uniq.map(a=> textFormat(a.name)).join(', ');//products.map(p=>p.title)
      },
      answer( income, log ){
        return shuffle(
          [new Answer.Correct(textFormat(income.correct.title))]
            .concat(income.wrong.map(p=>new Answer.Wrong(textFormat(p.title)))))
      }
    },
    {
      questionCmpAmount: 2,
      probability: 1011,
      type: 'Component in product',
      products: {from: 4, to: 6},
      minSimilarTags: 1,
      from: qB.prebuild.similarTaggedProducts.fn,
      question(income, log){
        return `Выберите один ингредиент, который входит в продукт ${lapk(income.correct.title)}`
      },
      answer( income, log ){

        return shuffle(
          [new Answer.Correct(textFormat(shuffle(income.uniq)[0].name))]
            .concat(
              shuffle(
                qB.subdivideComponents( qB.getUniqComponents(income.wrong), income.correct.getComponents() )
              )
                .slice(0, rand(this.products.from-1, this.products.to-1))
                .map(c=>new Answer.Wrong(textFormat(c.name)))

            )
        )
      }
    },
    {
      questionCmpAmount: 2,
      probability: 10,
      type: 'Select product description',
      products: {from: 3, to: 5},
      minSimilarTags: 1,
      from: qB.prebuild.similarTaggedProducts.fn,
      question(income, log){
        return `Какое из этих описаний относится к продукту ${lapk(income.correct.title)}`
      },
      answer( income, log ){

        return shuffle(
          [new Answer.Correct(textFormat(income.correct.description))]
            .concat(
                income.wrong.map(
                  p => new Answer.Wrong(textFormat(p.description))
                )
            ))
      }
    },
  {
    probability: 10,
    answers: {from: 3, to: 6},

    question: (product)=> `Выберите лишний ингредиент, НЕ входящий в продукт "${product.title}"`,
    type: 'Do not contain',
    from: (log)=>qB.randomProduct({ minComponents: 2, single: true }, log),

    answer( product, log ){

      const out =
          shuffle(
            dP.Product.getComponents(product)
              .map(i=>({correct: false, text: i.name}))
          )
            .slice(0, rand(this.answers.from - 1, this.answers.to - 1)),

        // get tags
        tags = product.tags,

        donors = [];

      log.push(`Have ${dP.Product.getComponents(product).length} components. Took ${out.length}:`);
      out.forEach(function(a) {
        log.push('  > '+ a.text)
      });

      log.push('\nTags: '+dP.Product.getTags(product).map(t=>t.name).join(', '));

      Object.keys(dP.products)
        .map(k=>dP.products[k])
        .forEach(item => {
          if(item.id === product.id)
            return;

          const matchedCount = tags.filter(tid=>item.tags.indexOf(tid)>-1).length;
          if(matchedCount>0){
            donors.push({
              count: matchedCount,
              item,
              title: item.title,
              component: dP.Product.getComponents(item).map(a=>a.name)
            });
          }
        });

      log.push('Matched '+donors.length+' component donors.');

      donors.sort((a,b)=>b.count-a.count);
      if(!donors.length){
        log.push('Not enough');
        return false;
      }

      const maxCount = donors[0].count;
      const minCount = Math.round(maxCount - maxCount/4);
      const alreadyExist = out.reduce((store, i)=>{
        store[normalizeText(i.text)] = true;
        return store;
      }, {'соль': true,'сольпощово': true, 'сохор': true});

      log.push('Nearest candidate have got '+ maxCount +' similar tag.');

      const may = ([].concat.apply(
        [],
        donors
          .filter(i=>i.count > 0 && i.count >= minCount)
          .map(i=>
            i.component
              .filter(c=>{
                if(!c.trim())
                  return false;
                const normalized = normalizeText(c);
                if(normalized in alreadyExist)
                  return false;
                alreadyExist[normalized] = true;
                return true;
              })
              .map(x => ({count: i.count, item: i.item, text: x}))
            )
      ));

      log.push('Filtered candidates with ['+ (minCount===maxCount? minCount : minCount+' - '+ maxCount) +'] similar tags.');

      if(may.length<2)
        return false;
//console.clear()
      //const addAnswersCount = Math.min(rand(this.answers.from, this.answers.to)-out.length, may.length);

      const answer = rand(may);

      log.push('Use '+ answer.item.title +' as donor.');
      log.push('Because it has '+ answer.count +' similar tags: '+ dP.Product.getTags(answer.item).map(t=>t.name).join(', '));

      out.push({correct: true, text: answer.text});

      log.push('\n << '+ answer.text);


      /*
            for(let i = 0; i < addAnswersCount; i++){
              if(may.length-i<0)
                break;
              const idx = Math.random()*(may.length-i)|0;
              if(idx>=may.length || idx < 0)debugger
              console.log(i, may[idx], may)
              out.push({correct: true, text: may[idx]});
              may[idx] = may[may.length - 1 - i]
            }*/

      console.log(dP.Product.getTags(product), donors)
      return out;
    }
  }],
  radioPhoto: {

  }
};
const shuffle = function (a) {
  for (let i = a.length - 1; i > 0; i--) {
    const j = Math.floor(Math.random() * (i + 1));
    [a[i], a[j]] = [a[j], a[i]];
  }
  return a;
}
const quizGenerator = function(type, photo, subType) {
  const types = quizTypes[type+(photo?'Photo':'')];

  const cfg = probabilityRand(types);
  const log = ['Quiz generate '+type+(photo ? ' with photo':'') +' '+(cfg?`${lapk(cfg.type)}`:'. FAIL')];

  if(!cfg)
    return {answers: [], log};
  const source = cfg.from.call(cfg, log);
  if(source === false){
    // давай по новой
    return false;
  }

  let answers = shuffle(cfg.answer.call(cfg, source, log));
  if(answers === false){
    // давай по новой
    return quizGenerator( type, photo );
  }
  return {question: cfg.question.call(cfg, source, log), answers, log}
};


/*
К какому из продуктов относится это описание: "Важно знать"
В какой из продуктов входят данные ингредиенты: Ингредиент 1, Ингредиент 2, Ингредиент 3, Ингредиент 4
Выберите один ингредиент, который входит в продукт "Название продукта"
Какое из этих описаний относится к продукту "Название продукта"
Какие ингредиенты входят в продукт "Название продукта"
Какие ингредиенты НЕ входят в продукт "Название продукта"
В какие продукты входит следующий ингредиент "Название ингредиента"
Какой продукт изображен на картинке
Какое из этих описаний относится к продукту, изображенному на картинке
Выберите один ингредиент, который входит в продукт, изображенный на картинке
Какие ингредиенты входят в продукт, изображенный на картинке?
Какие ингредиенты НЕ входят в продукт, изображенный на картинке
 */