export function requiredFields(name, row, fields, update) {
  fields.forEach(field => {
    if (update) {
      if (row.hasOwnProperty(field) && row[field] == null) {
        throw new Error(`${name} is missing field ${field}`);
      }
    } else {
      if (!row.hasOwnProperty(field) || row[field] == null) {
        throw new Error(`${name} is missing field ${field}`);
      }
    }
  });
}

export function toDateRepr(str) {
  if (typeof str !== 'string') {
    throw new Error('toDateRepr not passed a string: ' + str);
  }

  return parseInt(str.replace(/-/g, ''));
}

export function fromDateRepr(number) {
  if (typeof number !== 'number') {
    throw new Error('fromDateRepr not passed a number: ' + number);
  }

  const dateString = number.toString();
  return (
    dateString.slice(0, 4) +
    '-' +
    dateString.slice(4, 6) +
    '-' +
    dateString.slice(6)
  );
}

export const accountModel = {
  validate(account, { update }: { update?: boolean } = {}) {
    requiredFields(
      'account',
      account,
      update ? ['name', 'offbudget', 'closed'] : ['name'],
      update,
    );

    return account;
  },
};

export const categoryModel = {
  validate(category, { update }: { update?: boolean } = {}) {
    requiredFields(
      'category',
      category,
      update ? ['name', 'is_income', 'cat_group'] : ['name', 'cat_group'],
      update,
    );

    let { sort_order, ...rest } = category;
    return rest;
  },
};

export const categoryGroupModel = {
  validate(categoryGroup, { update }: { update?: boolean } = {}) {
    requiredFields(
      'categoryGroup',
      categoryGroup,
      update ? ['name', 'is_income'] : ['name'],
      update,
    );

    let { sort_order, ...rest } = categoryGroup;
    return rest;
  },
};

export const payeeModel = {
  validate(payee, { update }: { update?: boolean } = {}) {
    requiredFields('payee', payee, ['name'], update);
    return payee;
  },
};

export const transactionModel = {
  validate(trans, { update }: { update?: boolean } = {}) {
    requiredFields('transaction', trans, ['date', 'acct'], update);

    if ('date' in trans) {
      // Make sure it's the right format, and also do a sanity check.
      // Really old dates can mess up the system and can happen by
      // accident
      if (
        trans.date.match(/^\d{4}-\d{2}-\d{2}$/) == null ||
        trans.date < '2000-01-01'
      ) {
        throw new Error('Invalid transaction date: ' + trans.date);
      }
    }

    return trans;
  },

  toJS(row) {
    // Check a non-important field that typically wouldn't be passed in
    // manually, and use it as a smoke test to see if this is a
    // fully-formed transaction or not.
    if (!('location' in row)) {
      throw new Error(
        'A full transaction is required to be passed to `toJS`. Instead got: ' +
          JSON.stringify(row),
      );
    }

    let trans = { ...row };
    trans.error = row.error ? JSON.parse(row.error) : null;
    trans.isParent = row.isParent === 1 ? true : false;
    trans.isChild = row.isChild === 1 ? true : false;
    trans.starting_balance_flag =
      row.starting_balance_flag === 1 ? true : false;
    trans.cleared = row.cleared === 1 ? true : false;
    trans.pending = row.pending === 1 ? true : false;
    trans.date = trans.date && fromDateRepr(trans.date);
    return trans;
  },

  fromJS(trans) {
    let row = { ...trans };
    if ('error' in row) {
      row.error = trans.error ? JSON.stringify(trans.error) : null;
    }
    if ('isParent' in row) {
      row.isParent = trans.isParent ? 1 : 0;
    }
    if ('isChild' in row) {
      row.isChild = trans.isChild ? 1 : 0;
    }
    if ('cleared' in row) {
      row.cleared = trans.cleared ? 1 : 0;
    }
    if ('pending' in row) {
      row.pending = trans.pending ? 1 : 0;
    }
    if ('starting_balance_flag' in row) {
      row.starting_balance_flag = trans.starting_balance_flag ? 1 : 0;
    }
    if ('date' in row) {
      row.date = toDateRepr(trans.date);
    }

    return row;
  },
};