// +-------------------------------------------------------------------------
// | Copyright (C) 2016 Yunify, Inc.
// +-------------------------------------------------------------------------
// | Licensed under the Apache License, Version 2.0 (the "License");
// | you may not use this work except in compliance with the License.
// | You may obtain a copy of the License in the LICENSE file, or at:
// |
// | http://www.apache.org/licenses/LICENSE-2.0
// |
// | Unless required by applicable law or agreed to in writing, software
// | distributed under the License is distributed on an "AS IS" BASIS,
// | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// | See the License for the specific language governing permissions and
// | limitations under the License.
// +-------------------------------------------------------------------------

package request

import (
	"bytes"
	"fmt"
	"net/http"
	"reflect"

	"github.com/yunify/qingcloud-sdk-go/logger"
	"github.com/yunify/qingcloud-sdk-go/request/data"
	"github.com/yunify/qingcloud-sdk-go/request/errors"
	"github.com/yunify/qingcloud-sdk-go/utils"
)

// Unpacker is the response unpacker.
type Unpacker struct {
	operation *data.Operation

	httpResponse *http.Response
	output       *reflect.Value
}

// UnpackHTTPRequest unpack the http response with an operation, http response and an output.
func (u *Unpacker) UnpackHTTPRequest(o *data.Operation, r *http.Response, x *reflect.Value) error {
	u.operation = o
	u.httpResponse = r
	u.output = x

	err := u.parseResponse()
	if err != nil {
		return err
	}

	err = u.parseError()
	if err != nil {
		return err
	}

	return nil
}

func (u *Unpacker) parseResponse() error {
	if u.httpResponse.StatusCode == 200 {
		if u.httpResponse.Header.Get("Content-Type") == "application/json" {
			buffer := &bytes.Buffer{}
			buffer.ReadFrom(u.httpResponse.Body)
			u.httpResponse.Body.Close()

			logger.Info(fmt.Sprintf(
				"Response json string: [%d] %s",
				utils.StringToUnixInt(u.httpResponse.Header.Get("Date"), "RFC 822"),
				string(buffer.Bytes())))

			_, err := utils.JSONDecode(buffer.Bytes(), u.output.Interface())
			if err != nil {
				return err
			}

			if !u.output.IsValid() {
				return fmt.Errorf("API Gateway output format error")
			}
		}
	} else {
		u.httpResponse.Body.Close()
		err := fmt.Errorf("Response StatusCode: %d", u.httpResponse.StatusCode)
		logger.Error(err.Error())
		return err
	}

	return nil
}

func (u *Unpacker) parseError() error {
	if u.output.IsNil() {
		return fmt.Errorf("nil returned")
	}
	retCodeValue := u.output.Elem().FieldByName("RetCode")
	messageValue := u.output.Elem().FieldByName("Message")

	if retCodeValue.IsValid() && retCodeValue.Type().String() == "*int" &&
		retCodeValue.Elem().IsValid() {
		if retCodeValue.Elem().Int() == 0 {
			return nil
		}
		err := &errors.QingCloudError{
			RetCode: int(retCodeValue.Elem().Int()),
		}
		if messageValue.IsValid() && messageValue.Type().String() == "*string" {
			if messageValue.Elem().IsValid() {
				err.Message = messageValue.Elem().String()
			} else {
				err.Message = "null"
			}
		}
		return err
	}

	return fmt.Errorf("invalid retCodeValue %v returned", retCodeValue)
}
