// AttachFileAction attaches a file to an alert at OpsGenie.
func AttachFileAction(c *gcli.Context) {
	cli, err := NewAlertClient(c)
	if err != nil {
		os.Exit(1)
	}

	req := alerts.AttachFileAlertRequest{}
	if val, success := getVal("id", c); success {
		req.ID = val
	}
	if val, success := getVal("alias", c); success {
		req.Alias = val
	}
	if val, success := getVal("attachment", c); success {
		f, err := os.Open(val)
		defer f.Close()
		if err != nil {
			fmt.Printf("%s\n", err.Error())
			os.Exit(1)
		}
		req.Attachment = f
	}

	if val, success := getVal("indexFile", c); success {
		req.IndexFile = val
	}

	req.User = grabUsername(c)
	if val, success := getVal("source", c); success {
		req.Source = val
	}
	if val, success := getVal("note", c); success {
		req.Note = val
	}

	printVerboseMessage("Attach request prepared from flags, sending request to OpsGenie..")

	_, err = cli.AttachFile(req)
	if err != nil {
		fmt.Printf("%s\n", err.Error())
		os.Exit(1)
	}

	printVerboseMessage("File attached to alert successfully.")
}
// AttachFile method attaches a file to an alert at OpsGenie.
func (cli *OpsGenieAlertClient) AttachFile(req alerts.AttachFileAlertRequest) (*alerts.AttachFileAlertResponse, error) {
	req.APIKey = cli.apiKey
	var b bytes.Buffer
	w := multipart.NewWriter(&b)

	path := req.Attachment.Name()
	file, err := os.Open(path)
	defer file.Close()
	if err != nil {
		message := "Attachment can not be opened for reading. " + err.Error()
		logging.Logger().Warn(message)
		return nil, errors.New(message)
	}
	// add the attachment
	fw, err := w.CreateFormFile("attachment", filepath.Base(path))
	if err != nil {
		message := "Can not build the request with the field attachment. " + err.Error()
		logging.Logger().Warn(message)
		return nil, errors.New(message)
	}
	if _, err := io.Copy(fw, file); err != nil {
		message := "Can not copy the attachment into the request. " + err.Error()
		logging.Logger().Warn(message)
		return nil, errors.New(message)
	}

	// Add the other fields
	// empty fields should not be placed into the request
	// otherwise it yields an incomplete boundary exception
	if req.APIKey != "" {
		if err = writeField(*w, "apiKey", req.APIKey); err != nil {
			return nil, err
		}
	}
	if req.ID != "" {
		if err = writeField(*w, "id", req.ID); err != nil {
			return nil, err
		}
	}
	if req.Alias != "" {
		if err = writeField(*w, "alias", req.Alias); err != nil {
			return nil, err
		}
	}
	if req.User != "" {
		if err = writeField(*w, "user", req.User); err != nil {
			return nil, err
		}
	}
	if req.Source != "" {
		if err = writeField(*w, "source", req.Source); err != nil {
			return nil, err
		}
	}
	if req.IndexFile != "" {
		if err = writeField(*w, "indexFile", req.IndexFile); err != nil {
			return nil, err
		}
	}
	if req.Note != "" {
		if err = writeField(*w, "note", req.Note); err != nil {
			return nil, err
		}
	}

	w.Close()
	httpReq, err := http.NewRequest("POST", cli.opsGenieAPIURL+attachFileAlertURL, &b)
	if err != nil {
		message := "Can not create the multipart/form-data request. " + err.Error()
		logging.Logger().Warn(message)
		return nil, errors.New(message)
	}
	httpReq.Header.Set("Content-Type", w.FormDataContentType())
	transport := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
		Proxy:           http.ProxyFromEnvironment,
		Dial: func(netw, addr string) (net.Conn, error) {
			conn, err := net.DialTimeout(netw, addr, cli.httpTransportSettings.ConnectionTimeout)
			if err != nil {
				message := "Error occurred while connecting: " + err.Error()
				logging.Logger().Warn(message)
				return nil, errors.New(message)
			}
			conn.SetDeadline(time.Now().Add(cli.httpTransportSettings.RequestTimeout))
			return conn, nil
		},
	}
	client := &http.Client{Transport: transport}
	// proxy settings
	if cli.proxy != nil {
		proxyURL, proxyErr := url.Parse(cli.proxy.toString())
		if proxyErr != nil {
			message := "Can not set the proxy configuration " + proxyErr.Error()
			logging.Logger().Warn(message)
			return nil, errors.New(message)
		}
		transport.Proxy = http.ProxyURL(proxyURL)
	}
	url := httpReq.URL.String()
	logging.Logger().Info("Executing OpsGenie request to [" + url + "] with multipart data.")
	var res *http.Response
	for i := 0; i < cli.httpTransportSettings.MaxRetryAttempts; i++ {
		res, err = client.Do(httpReq)
		if err == nil {
			defer res.Body.Close()
			break
		}
		if res != nil {
			logging.Logger().Info(fmt.Sprintf("Retrying request [%s] ResponseCode:[%d]. RetryCount: %d", url, res.StatusCode, (i + 1)))
		} else {
			logging.Logger().Info(fmt.Sprintf("Retrying request [%s] Reason:[%s]. RetryCount: %d", url, err.Error(), (i + 1)))
		}
		time.Sleep(timeSleepBetweenRequests)
	}

	if err != nil {
		message := "Can not attach the file, unable to send the request. " + err.Error()
		logging.Logger().Warn(message)
		return nil, errors.New(message)
	}

	httpStatusCode := res.StatusCode
	if httpStatusCode >= 400 {
		body, err := ioutil.ReadAll(res.Body)
		if err == nil {
			return nil, errorMessage(httpStatusCode, string(body[:]))
		}
		message := fmt.Sprint("Couldn't read the response, %s", err.Error())
		logging.Logger().Warn(message)
		return nil, errors.New(message)
	}

	attachFileAlertResp := alerts.AttachFileAlertResponse{Status: res.Status, Code: res.StatusCode}
	return &attachFileAlertResp, nil
}
func (cli *OpsGenieAlertClient) AttachFile(req alerts.AttachFileAlertRequest) (*alerts.AttachFileAlertResponse, error) {
	req.ApiKey = cli.apiKey
	// validate the mandatory parameters: apiKey, alertId/alias, attachment
	if req.ApiKey == "" {
		return nil, errors.New("ApiKey is a mandatory field and can not be empty.")
	}
	if req.Attachment == "" {
		return nil, errors.New("Attachment is a mandatory field and can not be empty.")
	}
	if req.AlertId == "" && req.Alias == "" {
		return nil, errors.New("At least either Alert Id or Alias should be set in the request.")
	}
	if req.AlertId != "" && req.Alias != "" {
		return nil, errors.New("Either Alert Id or Alias should be set in the request not both.")
	}

	var b bytes.Buffer
	w := multipart.NewWriter(&b)

	fileName := req.Attachment
	file, err := os.Open(fileName)
	if err != nil {
		return nil, errors.New("Attachment can not be opened for reading.")
	}
	// add the attachment
	fw, err := w.CreateFormFile("attachment", fileName)
	if err != nil {
		return nil, errors.New("Can not build the request with the field attachment.")
	}
	if _, err := io.Copy(fw, file); err != nil {
		return nil, errors.New("Can not copy the attachment into the request.")
	}

	defer file.Close()

	// Add the other fields
	// empty fields should not be placed into the request
	// otherwise it yields an incomplete boundary exception
	if req.ApiKey != "" {
		if fw, err = w.CreateFormField("apiKey"); err != nil {
			return nil, errors.New("Can not build the request with the field apiKey.")
		}
		if _, err = fw.Write([]byte(req.ApiKey)); err != nil {
			return nil, errors.New("Can not write the ApiKey value into the request.")
		}
	}
	if req.AlertId != "" {
		if fw, err = w.CreateFormField("alertId"); err != nil {
			return nil, errors.New("Can not build the request with the field alertId.")
		}
		if _, err = fw.Write([]byte(req.AlertId)); err != nil {
			return nil, errors.New("Can not write the AlertId value into the request.")
		}
	}
	if req.Alias != "" {
		if fw, err = w.CreateFormField("alias"); err != nil {
			return nil, errors.New("Can not build the request with the field alias.")
		}
		if _, err = fw.Write([]byte(req.Alias)); err != nil {
			return nil, errors.New("Can not write the Alias value into the request.")
		}
	}
	if req.User != "" {
		if fw, err = w.CreateFormField("user"); err != nil {
			return nil, errors.New("Can not build the request with the field user.")
		}
		if _, err = fw.Write([]byte(req.User)); err != nil {
			return nil, errors.New("Can not write the User value into the request.")
		}
	}
	if req.Source != "" {
		if fw, err = w.CreateFormField("source"); err != nil {
			return nil, errors.New("Can not build the request with the field source.")
		}
		if _, err = fw.Write([]byte(req.Source)); err != nil {
			return nil, errors.New("Can not write the Source value into the request.")
		}
	}
	if req.IndexFile != "" {
		if fw, err = w.CreateFormField("indexFile"); err != nil {
			return nil, errors.New("Can not build the request with the field indexFile.")
		}
		if _, err = fw.Write([]byte(req.IndexFile)); err != nil {
			return nil, errors.New("Can not write the IndexFile value into the request.")
		}
	}
	if req.Note != "" {
		if fw, err = w.CreateFormField("note"); err != nil {
			return nil, errors.New("Can not build the request with the field note.")
		}
		if _, err = fw.Write([]byte(req.Note)); err != nil {
			return nil, errors.New("Can not write the Note value into the request.")
		}
	}

	w.Close()

	httpReq, err := http.NewRequest("POST", ATTACH_FILE_ALERT_URL, &b)
	if err != nil {
		return nil, errors.New("Can not create the multipart/form-data request.")
	}
	httpReq.Header.Set("Content-Type", w.FormDataContentType())
	client := &http.Client{}
	// proxy settings
	if cli.proxy != "" {
		proxyUrl, proxyErr := url.Parse(cli.proxy)
		if proxyErr != nil {
			return nil, errors.New("Can not set the proxy configuration")
		}
		client.Transport = &http.Transport{Proxy: http.ProxyURL(proxyUrl)}
	}
	var res *http.Response
	for i := 0; i < cli.retries; i++ {
		res, err = client.Do(httpReq)
		if err == nil {
			break
		}
		time.Sleep(TIME_SLEEP_BETWEEN_REQUESTS)
	}

	if err != nil {
		return nil, errors.New("Can not attach the file, unable to send the request.")
	}

	// check the returning HTTP status code
	httpStatusCode := res.StatusCode
	if httpStatusCode >= 400 && httpStatusCode < 500 {
		return nil, errors.New(fmt.Sprintf("Client error %d returned", httpStatusCode))
	}
	if httpStatusCode >= 500 {
		return nil, errors.New(fmt.Sprintf("Server error %d returned", httpStatusCode))
	}

	attachFileAlertResp := alerts.AttachFileAlertResponse{Status: res.Status, Code: res.StatusCode}
	return &attachFileAlertResp, nil
}