package statuscake

import (
	"encoding/json"
	"fmt"
	"net/url"
	"strings"
	"strconv"
	
	"github.com/google/go-querystring/query"
)

//Ssl represent the data received by the API with GET
type Ssl struct {
	ID             string              `json:"id"                 url:"id,omitempty"`
	Domain         string              `json:"domain"             url:"domain,omitempty"`
	Checkrate      int                 `json:"checkrate"          url:"checkrate,omitempty"`
	ContactGroupsC string              `                          url:"contact_groups,omitempty"`
	AlertAt        string              `json:"alert_at"           url:"alert_at,omitempty"`
	AlertReminder  bool                `json:"alert_reminder"     url:"alert_expiry,omitempty"`
	AlertExpiry    bool                `json:"alert_expiry"       url:"alert_reminder,omitempty"`
	AlertBroken    bool                `json:"alert_broken"       url:"alert_broken,omitempty"`
	AlertMixed     bool                `json:"alert_mixed"        url:"alert_mixed,omitempty"`
	Paused         bool                `json:"paused"`
	IssuerCn       string              `json:"issuer_cn"`
	CertScore      string              `json:"cert_score"`
	CipherScore    string              `json:"cipher_score"`
	CertStatus     string              `json:"cert_status"`
	Cipher         string              `json:"cipher"`
	ValidFromUtc   string              `json:"valid_from_utc"`
	ValidUntilUtc  string              `json:"valid_until_utc"`
	MixedContent   []map[string]string `json:"mixed_content"`
	Flags          map[string]bool     `json:"flags"`
	ContactGroups  []string            `json:"contact_groups"`
	LastReminder   int                 `json:"last_reminder"`
	LastUpdatedUtc string              `json:"last_updated_utc"`
}

//PartialSsl represent  a ssl test creation or modification
type PartialSsl struct {
	ID             int
	Domain         string
	Checkrate      string
	ContactGroupsC string
	AlertAt        string
	AlertExpiry    bool
	AlertReminder  bool
	AlertBroken    bool
	AlertMixed     bool
}

type createSsl struct {
	ID             int    `url:"id,omitempty"`
	Domain         string `url:"domain"         json:"domain"`
	Checkrate      string `url:"checkrate"      json:"checkrate"`
	ContactGroupsC string `url:"contact_groups" json:"contact_groups"`
	AlertAt        string `url:"alert_at"       json:"alert_at"`
	AlertExpiry    bool   `url:"alert_expiry"   json:"alert_expiry"`
	AlertReminder  bool   `url:"alert_reminder" json:"alert_reminder"`
	AlertBroken    bool   `url:"alert_broken"   json:"alert_broken"`
	AlertMixed     bool   `url:"alert_mixed"    json:"alert_mixed"`
}

type updateSsl struct {
	ID             int    `url:"id"`
	Domain         string `url:"domain"         json:"domain"`
	Checkrate      string `url:"checkrate"      json:"checkrate"`
	ContactGroupsC string `url:"contact_groups" json:"contact_groups"`
	AlertAt        string `url:"alert_at"       json:"alert_at"`
	AlertExpiry    bool   `url:"alert_expiry"   json:"alert_expiry"`
	AlertReminder  bool   `url:"alert_reminder" json:"alert_reminder"`
	AlertBroken    bool   `url:"alert_broken"   json:"alert_broken"`
	AlertMixed     bool   `url:"alert_mixed"    json:"alert_mixed"`
}


type sslUpdateResponse struct {
	Success bool   `json:"Success"`
	Message interface{} `json:"Message"`
}

type sslCreateResponse struct {
	Success bool   `json:"Success"`
	Message interface{} `json:"Message"`
	Input createSsl `json:"Input"`
}

//Ssls represent the actions done wit the API
type Ssls interface {
	All() ([]*Ssl, error)
	completeSsl(*PartialSsl) (*Ssl, error)
	Detail(string) (*Ssl, error)
	Update(*PartialSsl) (*Ssl, error)
	UpdatePartial(*PartialSsl) (*PartialSsl, error)
	Delete(ID string) error
	CreatePartial(*PartialSsl) (*PartialSsl, error)
	Create(*PartialSsl) (*Ssl, error)
}

func consolidateSsl(s *Ssl) {
	(*s).ContactGroupsC = strings.Trim(strings.Join(strings.Fields(fmt.Sprint((*s).ContactGroups)), ","), "[]")
}

func findSsl(responses []*Ssl, id string) (*Ssl, error) {
	var response *Ssl
	for _, elem := range responses {
		if (*elem).ID == id {
			return elem, nil
		}
	}
	return response, fmt.Errorf("%s Not found", id)
}

func (tt *ssls) completeSsl(s *PartialSsl) (*Ssl, error) {
	full, err := tt.Detail(strconv.Itoa((*s).ID))
	if err != nil {
		return nil, err
	}
	(*full).ContactGroups = strings.Split((*s).ContactGroupsC,",")
	return full, nil
}

//Partial return a PartialSsl corresponding to the Ssl
func Partial(s *Ssl) (*PartialSsl,error) {
	if s==nil {
		return nil,fmt.Errorf("s is nil")
	}
	id,err:=strconv.Atoi(s.ID)
	if(err!=nil){
		return nil,err
	}
	return &PartialSsl{
		ID: id,
		Domain: s.Domain,
		Checkrate: strconv.Itoa(s.Checkrate),
		ContactGroupsC: s.ContactGroupsC,
		AlertReminder: s.AlertReminder,
		AlertExpiry: s.AlertExpiry,
		AlertBroken: s.AlertBroken,
		AlertMixed: s.AlertMixed,
		AlertAt: s.AlertAt,
	},nil
	
}

type ssls struct {
	client apiClient
}

//NewSsls return a new ssls
func NewSsls(c apiClient) Ssls {
	return &ssls{
		client: c,
	}
}

//All return a list of all the ssl from the API
func (tt *ssls) All() ([]*Ssl, error) {
	rawResponse, err := tt.client.get("/SSL", nil)
	if err != nil {
		return nil, fmt.Errorf("Error getting StatusCake Ssl: %s", err.Error())
	}
	var getResponse []*Ssl
	err = json.NewDecoder(rawResponse.Body).Decode(&getResponse)
	if err != nil {
		return nil, err
	}
	
	for ssl := range getResponse {
		consolidateSsl(getResponse[ssl])
	}
	
	return getResponse, err
}

//Detail return the ssl corresponding to the id 
func (tt *ssls) Detail(id string) (*Ssl, error) {
	responses, err := tt.All()
	if err != nil {
		return nil, err
	}
	mySsl, errF := findSsl(responses, id)
	if errF != nil {
		return nil, errF
	}
	return mySsl, nil
}

//Update update the API with s and create one if s.ID=0 then return the corresponding Ssl
func (tt *ssls) Update(s *PartialSsl) (*Ssl, error) {
	var err error
	s, err = tt.UpdatePartial(s)
	if err!= nil {
		return nil, err
	}
	return tt.completeSsl(s)
}

//UpdatePartial update the API with s and create one if s.ID=0 then return the corresponding PartialSsl
func (tt *ssls) UpdatePartial(s *PartialSsl) (*PartialSsl, error) {

	if((*s).ID == 0){
		return tt.CreatePartial(s)
	}
	var v url.Values

	v, _ = query.Values(updateSsl(*s))
	
	rawResponse, err := tt.client.put("/SSL/Update", v)
	if err != nil {
		return nil, fmt.Errorf("Error creating StatusCake Ssl: %s", err.Error())
	}
	
	var updateResponse sslUpdateResponse
	err = json.NewDecoder(rawResponse.Body).Decode(&updateResponse)
	if err != nil {
		return nil, err
	}
	
	if !updateResponse.Success {
		return nil, fmt.Errorf("%s", updateResponse.Message.(string))
	}


	return s, nil
}

//Delete delete the ssl which ID is id
func (tt *ssls) Delete(id string) error {
	_, err := tt.client.delete("/SSL/Update", url.Values{"id": {fmt.Sprint(id)}})
	if err != nil {
		return err
	}

	return nil
}

//Create create the ssl whith the data in s and return the Ssl created
func (tt *ssls) Create(s *PartialSsl) (*Ssl, error) {
	var err error
	s, err = tt.CreatePartial(s)
	if err!= nil {
		return nil, err
	}
	return tt.completeSsl(s)
}

//CreatePartial create the ssl whith the data in s and return the PartialSsl created
func (tt *ssls) CreatePartial(s *PartialSsl) (*PartialSsl, error) {
	(*s).ID=0
	var v url.Values
	v, _ = query.Values(createSsl(*s))
	
	rawResponse, err := tt.client.put("/SSL/Update", v)
	if err != nil {
		return nil, fmt.Errorf("Error creating StatusCake Ssl: %s", err.Error())
	}

	var createResponse sslCreateResponse
	err = json.NewDecoder(rawResponse.Body).Decode(&createResponse)
	if err != nil {
		return nil, err
	}

	if !createResponse.Success {
		return nil, fmt.Errorf("%s", createResponse.Message.(string))
	}
	*s = PartialSsl(createResponse.Input)
	(*s).ID = int(createResponse.Message.(float64))
	
	return s,nil
}

