package api

import (
	"encoding/json"
	"strings"

	"github.com/sacloud/libsacloud/sacloud"
)

//HACK: さくらのAPI側仕様: CommonServiceItemsの内容によってJSONフォーマットが異なるため
//      DNS/GSLB/シンプル監視それぞれでリクエスト/レスポンスデータ型を定義する。

// SearchDNSResponse DNS検索レスポンス
type SearchDNSResponse struct {
	// Total 総件数
	Total int `json:",omitempty"`
	// From ページング開始位置
	From int `json:",omitempty"`
	// Count 件数
	Count int `json:",omitempty"`
	// CommonServiceDNSItems DNSリスト
	CommonServiceDNSItems []sacloud.DNS `json:"CommonServiceItems,omitempty"`
}
type dnsRequest struct {
	CommonServiceDNSItem *sacloud.DNS           `json:"CommonServiceItem,omitempty"`
	From                 int                    `json:",omitempty"`
	Count                int                    `json:",omitempty"`
	Sort                 []string               `json:",omitempty"`
	Filter               map[string]interface{} `json:",omitempty"`
	Exclude              []string               `json:",omitempty"`
	Include              []string               `json:",omitempty"`
}
type dnsResponse struct {
	*sacloud.ResultFlagValue
	*sacloud.DNS `json:"CommonServiceItem,omitempty"`
}

// DNSAPI DNS API
type DNSAPI struct {
	*baseAPI
}

// NewDNSAPI DNS API作成
func NewDNSAPI(client *Client) *DNSAPI {
	return &DNSAPI{
		&baseAPI{
			client: client,
			FuncGetResourceURL: func() string {
				return "commonserviceitem"
			},
			FuncBaseSearchCondition: func() *sacloud.Request {
				res := &sacloud.Request{}
				res.AddFilter("Provider.Class", "dns")
				return res
			},
		},
	}
}

// Find 検索
func (api *DNSAPI) Find() (*SearchDNSResponse, error) {

	data, err := api.client.newRequest("GET", api.getResourceURL(), api.getSearchState())
	if err != nil {
		return nil, err
	}
	var res SearchDNSResponse
	if err := json.Unmarshal(data, &res); err != nil {
		return nil, err
	}
	return &res, nil
}

func (api *DNSAPI) request(f func(*dnsResponse) error) (*sacloud.DNS, error) {
	res := &dnsResponse{}
	err := f(res)
	if err != nil {
		return nil, err
	}
	return res.DNS, nil
}

func (api *DNSAPI) createRequest(value *sacloud.DNS) *dnsRequest {
	req := &dnsRequest{}
	req.CommonServiceDNSItem = value
	return req
}

// Create 新規作成
func (api *DNSAPI) Create(value *sacloud.DNS) (*sacloud.DNS, error) {
	return api.request(func(res *dnsResponse) error {
		return api.create(api.createRequest(value), res)
	})
}

// New 新規作成用パラメーター作成
func (api *DNSAPI) New(zoneName string) *sacloud.DNS {
	return sacloud.CreateNewDNS(zoneName)
}

// Read 読み取り
func (api *DNSAPI) Read(id int64) (*sacloud.DNS, error) {
	return api.request(func(res *dnsResponse) error {
		return api.read(id, nil, res)
	})
}

// Update 更新
func (api *DNSAPI) Update(id int64, value *sacloud.DNS) (*sacloud.DNS, error) {
	return api.request(func(res *dnsResponse) error {
		return api.update(id, api.createRequest(value), res)
	})
}

// Delete 削除
func (api *DNSAPI) Delete(id int64) (*sacloud.DNS, error) {
	return api.request(func(res *dnsResponse) error {
		return api.delete(id, nil, res)
	})
}

// SetupDNSRecord DNSレコード作成
func (api *DNSAPI) SetupDNSRecord(zoneName string, hostName string, ip string) ([]string, error) {

	dnsItem, err := api.findOrCreateBy(zoneName)
	if err != nil {
		return nil, err
	}

	if strings.HasSuffix(hostName, zoneName) {
		hostName = strings.Replace(hostName, zoneName, "", -1)
	}

	dnsItem.Settings.DNS.AddDNSRecordSet(hostName, ip)

	res, err := api.updateDNSRecord(dnsItem)
	if err != nil {
		return nil, err
	}

	if dnsItem.ID == sacloud.EmptyID {
		return res.Status.NS, nil
	}

	return nil, nil

}

// DeleteDNSRecord DNSレコード削除
func (api *DNSAPI) DeleteDNSRecord(zoneName string, hostName string, ip string) error {
	dnsItem, err := api.findOrCreateBy(zoneName)
	if err != nil {
		return err
	}
	dnsItem.Settings.DNS.DeleteDNSRecordSet(hostName, ip)

	if dnsItem.HasDNSRecord() {
		_, err = api.updateDNSRecord(dnsItem)
		if err != nil {
			return err
		}

	} else {
		_, err = api.Delete(dnsItem.ID)
		if err != nil {
			return err
		}

	}
	return nil
}

func (api *DNSAPI) findOrCreateBy(zoneName string) (*sacloud.DNS, error) {

	res, err := api.Reset().WithNameLike(zoneName).Limit(1).Find()
	if err != nil {
		return nil, err
	}

	//すでに登録されている場合
	var dnsItem *sacloud.DNS
	if res.Count > 0 {
		dnsItem = &res.CommonServiceDNSItems[0]
	} else {
		dnsItem = sacloud.CreateNewDNS(zoneName)
	}

	return dnsItem, nil
}

func (api *DNSAPI) updateDNSRecord(dnsItem *sacloud.DNS) (*sacloud.DNS, error) {

	var item *sacloud.DNS
	var err error

	if dnsItem.ID == sacloud.EmptyID {
		item, err = api.Create(dnsItem)
	} else {
		item, err = api.Update(dnsItem.ID, dnsItem)
	}

	if err != nil {
		return nil, err
	}

	return item, nil
}
