package mapping

import (
	"bytes"
	"encoding/json"
	"errors"
	"io"
	"io/ioutil"
	"net/http"
	"os"

	log "github.com/sirupsen/logrus"
	"github.com/spf13/viper"
	"iwarma.ru/console/correlator/config"
)

type Component struct {
	Version  int                    `json:"version"`
	Template map[string]interface{} `json:"template"`
}

type ComponentTemplate struct {
	Name  string    `json:"name"`
	Value Component `json:"component_template"`
}

type ComponentResponse struct {
	Templates []ComponentTemplate `json:"component_templates"`
}

type UpdateResponse struct {
	Acknowledged bool `json:"acknowledged"`
}

func ReadComponent(reader io.Reader) (Component, error) {
	byteValue, err := ioutil.ReadAll(reader)
	if err != nil {
		log.Errorf("Can't read component: %v", err)
		return Component{}, err
	}

	var result Component
	err = json.Unmarshal(byteValue, &result)
	if err != nil {
		log.Errorf("Can't unmarshall component: %v", err)
		return Component{}, err
	}

	return result, nil
}

func ReadComponentFromFile(fileName string) (Component, error) {
	file, err := os.Open(fileName)
	if err != nil {
		log.Errorf("Can't open file: %v", err)
		return Component{}, err
	}
	defer file.Close()

	return ReadComponent(file)
}

func ReadComponentResponse(reader io.Reader) (ComponentResponse, error) {
	byteValue, err := ioutil.ReadAll(reader)
	if err != nil {
		log.Errorf("Can't read component response: %v", err)
		return ComponentResponse{}, err
	}

	var result ComponentResponse
	err = json.Unmarshal(byteValue, &result)
	if err != nil {
		log.Errorf("Can't unmarshall component response: %v", err)
		return ComponentResponse{}, err
	}

	return result, nil
}

func ReadUpdateResponse(reader io.Reader) (UpdateResponse, error) {
	byteValue, err := ioutil.ReadAll(reader)
	if err != nil {
		log.Errorf("Can't read response: %v", err)
		return UpdateResponse{}, err
	}

	log.Debugf("Got server response: %v", string(byteValue))

	var result UpdateResponse
	err = json.Unmarshal(byteValue, &result)
	if err != nil {
		log.Errorf("Can't unmarshall update response: %v", err)
		return UpdateResponse{}, err
	}

	return result, nil
}

func CheckComponentUpdate(url string, component Component) (bool, error) {
	// Get current mapping for component
	http_client := &http.Client{}
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		log.Errorf("Can't create request to obtain component mapping version from server: %v", err)
		return false, err
	}

	// Set auth
	req.SetBasicAuth(viper.GetString(config.ElasticUsername), viper.GetString(config.ElasticPassword))

	resp, err := http_client.Do(req)
	if err != nil {
		log.Errorf("Can't send request, to check component mapping on server: %v", err)
		return false, err
	}
	defer resp.Body.Close()

	responseComponent, err := ReadComponentResponse(resp.Body)
	if err != nil {
		log.Errorf("Can't parse component mapping response from server: %v", err)
		return false, err
	}

	// Now, check version
	max_version := 0
	for _, cur := range responseComponent.Templates {
		if cur.Value.Version > max_version {
			max_version = cur.Value.Version
		}
	}

	log.Debugf("After checking %v versions, found max version %v, component version is %v", len(responseComponent.Templates), max_version, component.Version)

	return component.Version > max_version, nil
}

func UpdateComponent(url string, component Component) error {

	byteValue, err := json.Marshal(component)
	if err != nil {
		log.Errorf("Can't marshall component: %v", err)
		return err
	}

	buffer := bytes.NewBuffer(byteValue)

	req, err := http.NewRequest("PUT", url, buffer)
	if err != nil {
		log.Errorf("Can't create copmonent update request: %v", err)
		return err
	}

	req.SetBasicAuth(viper.GetString(config.ElasticUsername), viper.GetString(config.ElasticPassword))
	req.Header.Add("Content-Type", "application/json")

	http_client := &http.Client{}
	resp, err := http_client.Do(req)
	if err != nil {
		log.Errorf("Can't send component update request: %v", err)
		return err
	}
	defer resp.Body.Close()

	response, err := ReadUpdateResponse(resp.Body)
	if err != nil {
		log.Errorf("Can't read server response: %v", err)
		return err
	}

	if response.Acknowledged != true {
		return errors.New("Server didn't accept component")
	}

	return nil
}
