import $ from 'jquery';
import { scheduleOnce } from '@ember/runloop';
import EmberObject, {
  observer, computed, get, set, setProperties
} from '@ember/object';
import { alias, equal } from '@ember/object/computed';
import { inject as service } from '@ember/service';
import Component from '@ember/component';
import NodeDriver from 'shared/mixins/node-driver';
import layout from './template';
import { INSTANCE_TYPES, nameFromResource, tagsFromResource, REGIONS } from 'shared/utils/amazon';
import { randomStr } from 'shared/utils/util';

let RANCHER_GROUP         = 'rancher-nodes';


export default Component.extend(NodeDriver, {
  prefs: service(),
  intl:  service(),

  layout,
  model: null,

  driverName:               'amazonec2',
  clients:                  null,
  allSubnets:               null,
  allSecurityGroups:        null,
  selectedSecurityGroup:    null,
  defaultSecurityGroup:     null,
  defaultSecurityGroupName: RANCHER_GROUP,
  whichSecurityGroup:       'default',
  instanceTypes:            INSTANCE_TYPES,
  regionChoices:            REGIONS,
  step:                     1,
  tags:                     null,
  editing:                  false,

  config: alias('model.amazonec2Config'),

  isCustomSecurityGroup:    equal('whichSecurityGroup', 'custom'),

  init() {
    this._super(...arguments);

    setProperties(this, {
      clients:    EmberObject.create(),
      allSubnets: []
    })

    let cur = get(this, 'config.securityGroup');

    if ( cur === '' ) { // TODO 2.0 should this be null 403 Vince/Wes/Daishan
      setProperties(this, {
        whichSecurityGroup:    'default',
        selectedSecurityGroup: null,
      });
    } else {
      setProperties(this, {
        whichSecurityGroup:    'custom',
        selectedSecurityGroup: cur,
      });
    }

    const tagsString = get(this, 'config.tags');

    if ( tagsString ) {
      const array = tagsString.split(',');
      const tags = {};

      for (let i = 0; i < array.length - 1; i = i + 2) {
        tags[array[i]] = array[i + 1];
      }

      set(this, 'tags', tags);
    }
  },

  willDestroyElement() {
    setProperties(this, {
      step:               1,
      machineId:          null,
      clients:            null,
      allSubnets:         null,
      allSecurityGroups:  null,
      whichSecurityGroup: 'default',
    });
  },

  actions: {
    finishAndSelectCloudCredential(cred) {
      if (cred) {
        set(this, 'primaryResource.cloudCredentialId', get(cred, 'id'));

        this.send('awsLogin');
      }
    },

    awsLogin(cb) {
      let self = this;

      set(this, 'errors', null);

      let subnets = [];
      let rName = get(this, 'config.region');

      // have to have something in there before we describe the request even though we are going to replace with the actual cred id
      let ec2 = new AWS.EC2({
        accessKeyId:         randomStr(),
        secretAccessKey:     randomStr(),
        region:              rName,
        httpOptions:         { cloudCredentialId: get(this, 'model.cloudCredentialId') },
      });

      let vpcNames = {};
      let vpcTags = {};

      ec2.describeVpcs({}, (err, vpcs) => {
        if ( err ) {
          let errors = get(self, 'errors') || [];

          errors.pushObject(err);
          set(this, 'errors', errors);
          if (cb && typeof cb === 'function') {
            cb();
          }

          return;
        }

        vpcs.Vpcs.forEach((vpc) => {
          vpcNames[vpc.VpcId] = nameFromResource(vpc, 'VpcId');
          vpcTags[vpc.VpcId] = tagsFromResource(vpc);
        });

        ec2.describeSubnets({}, (err, data) => {
          if ( err ) {
            let errors = get(self, 'errors') || [];

            errors.pushObject(err);
            set(this, 'errors', errors);
            if (cb && typeof cb === 'function') {
              cb();
            }

            return;
          }

          set(this, `clients.${ rName }`, ec2);

          data.Subnets.forEach((subnet) => {
            if ( (subnet.State || '').toLowerCase() !== 'available' ) {
              return;
            }

            subnets.pushObject(EmberObject.create({
              subnetName: nameFromResource(subnet, 'SubnetId'),
              subnetId:   subnet.SubnetId,
              subnetTags: tagsFromResource(subnet),
              vpcName:    vpcNames[subnet.VpcId] || subnet.VpcId,
              vpcId:      subnet.VpcId,
              vpcTags:    vpcTags[subnet.VpcId] || [],
              zone:       subnet.AvailabilityZone,
              region:     rName
            }));
          });

          setProperties(this, {
            'allSubnets': subnets,
            'step':       2,
          });
          if (cb && typeof cb === 'function') {
            cb();
          }
        });
      });
    },

    selectSubnet(cb) {
      set(this, 'errors', null);

      if ( !get(this, 'selectedZone') ) {
        set(this, 'errors', ['Select an Availability Zone']);
        if (cb && typeof cb === 'function') {
          cb();
        }

        return;
      }

      if ( !get(this, 'selectedSubnet') ) {
        set(this, 'errors', ['Select a VPC or Subnet']);
        if (cb && typeof cb === 'function') {
          cb();
        }

        return;
      }

      let ec2    = get(this, `clients.${ get(this, 'config.region') }`);
      let filter = {
        Name:   'vpc-id',
        Values: [get(this, 'config.vpcId')]
      };

      ec2.describeSecurityGroups({ Filters: [filter] }, (err, data) => {
        if ( err ) {
          set(this, 'errors', [err]);
          if (cb && typeof cb === 'function') {
            cb();
          }

          return;
        }

        let groups = [];

        data.SecurityGroups.forEach((group) => {
          let tags = {};

          // Skip launch-wizard groups
          if ( (group.GroupName || '').match(/^launch-wizard-.*$/) ) {
            return;
          }

          (group.Tags || []).forEach((tag) => {
            tags[tag.Key] = tag.Value;
          });

          let obj = {
            id:          group.GroupId,
            name:        group.GroupName,
            description: group.Description,
          };

          groups.push(obj);
        });

        setProperties(this, {
          allSecurityGroups: groups,
          step:              3,
        });
      });
    },

    multiSecurityGroupSelect() {
      let options = Array.prototype.slice.call($('.existing-security-groups')[0], 0);
      let selectedOptions = [];

      options.filterBy('selected', true).forEach((cap) => {
        return selectedOptions.push(cap.value);
      });

      set(this, 'selectedSecurityGroup', selectedOptions);
    },

    selectSecurityGroup(cb) {
      set(this, 'errors', null);

      if ( get(this, 'isCustomSecurityGroup') ) {
        set(this, 'config.securityGroup', get(this, 'selectedSecurityGroup'));
      } else {
        set(this, 'config.securityGroup', '');
      }

      setProperties(this, { step: 4, });

      if (cb && typeof cb === 'function') {
        cb();
      }
    },
  },

  tagsDidChange: observer('tags', function() {
    const array = [];
    const tags = get(this, 'tags') || {};

    Object.keys(tags).forEach((key) => {
      array.push(key);
      array.push(tags[key]);
    });

    set(this, 'config.tags', array.join(','));
  }),

  stepDidChange: observer('context.step', function() {
    scheduleOnce('afterRender', this, () => {
      document.body.scrollTop = document.body.scrollHeight;
    });
  }),

  selectedZone: computed('config.{region,zone}', {
    get() {
      let config = get(this, 'config');

      if ( get(config, 'region') && get(config, 'zone') ) {
        return get(config, 'region') + get(config, 'zone');
      } else {
        return null;
      }
    },

    set(key, val) {
      let config = get(this, 'config');

      setProperties(config, {
        region: val.substr(0, val.length - 1),
        zone:   val.substr(val.length - 1),
      });

      let selectedSubnet = get(this, 'selectedSubnet');

      if ( get(this, 'subnetChoices').filterBy('value', selectedSubnet).length === 0 ) {
        setProperties(config, {
          region:   val.substr(0, val.length - 1),
          zone:     val.substr(val.length - 1),
          vpcId:    null,
          subnetId: null,
        });
      }

      if ( get(config, 'region') && get(config, 'zone') ) {
        return get(config, 'region') + get(config, 'zone');
      } else {
        return null;
      }
    }
  }),

  zoneChoices: computed('allSubnets.@each.{zone}', function() {
    const choices = (get(this, 'allSubnets') || []).map((subnet) => {
      return get(subnet, 'zone');
    }).sort().uniq();

    if ( choices.length ) {
      if ( get(this, 'config.zone.length') ) {
        set(this, 'selectedZone', `${ get(this, 'config.region') }${ get(this, 'config.zone') }`);
      } else {
        set(this, 'selectedZone', choices[0]);
      }
    }

    return choices;
  }),

  subnetChoices: computed('selectedZone', 'allSubnets.@each.{subnetId,vpcId,zone}', function() {
    let out      = [];
    let seenVpcs = [];

    (get(this, 'allSubnets') || []).filterBy('zone', get(this, 'selectedZone')).forEach((subnet) => {
      let vpcName    = get(subnet, 'vpcName');
      let vpcId      = get(subnet, 'vpcId');
      let vpcTags    = get(subnet, 'vpcTags');
      let subnetId   = get(subnet, 'subnetId');
      let subnetName = get(subnet, 'subnetName');
      let subnetTags = get(subnet, 'subnetTags');

      if ( seenVpcs.indexOf(vpcId) === -1 ) {
        seenVpcs.pushObject(vpcId);
        out.pushObject({
          sortKey: vpcId,
          label:   vpcName,
          value:   vpcId,
          isVpc:   true,
          tags:    vpcTags
        });
      }

      out.pushObject({
        sortKey: `${ vpcId } ${ subnetName }`,
        label:   subnetName,
        value:   subnetId,
        isVpc:   false,
        tags:    subnetTags
      });
    });

    return out.sortBy('sortKey');
  }),

  selectedSubnet: computed('config.{subnetId,vpcId}', {
    set(key, val) {
      let config = get(this, 'config');

      if ( arguments.length > 1 ) {
        if ( val && val.length ) {
          if ( val.indexOf('vpc-') === 0 ) {
            setProperties(config, {
              vpcId:    val,
              subnetId: null,
            });
          } else {
            let subnet = this.subnetById(val);

            setProperties(config, {
              vpcId:    subnet.vpcId,
              subnetId: subnet.subnetId,
            });
          }
        } else {
          setProperties(config, {
            vpcId:    null,
            subnetId: null,
          });
        }
      }

      return get(config, 'subnetId') || get(config, 'vpcId');
    },

    get() {
      let config = get(this, 'config');

      return get(config, 'subnetId') || get(config, 'vpcId');
    },
  }),

  bootstrap() {
    let config = get(this, 'globalStore').createRecord({
      type:          'amazonec2Config',
      region:        'us-west-2',
      instanceType:  't2.medium',
      securityGroup: '',
      zone:          'a',
      rootSize:      '16',
    });

    set(this, 'model.amazonec2Config', config);
  },

  validate() {
    let errors = [];

    if ( !get(this, 'model.name') ) {
      errors.push(this.intl.t('nodeDriver.nameError'));
    }

    if (!this.validateCloudCredentials()) {
      errors.push(this.intl.t('nodeDriver.cloudCredentialError'))
    }

    set(this, 'errors', errors);

    return errors.length === 0;
  },

  subnetById(id) {
    return (get(this, 'allSubnets') || []).filterBy('subnetId', id)[0];
  },

});
