package api

import (
	"encoding/csv"
	"encoding/json"
	"fmt"
	"io"
	"strings"
	"time"

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

// BillAPI 請求情報API
type BillAPI struct {
	*baseAPI
}

// NewBillAPI 請求情報API作成
func NewBillAPI(client *Client) *BillAPI {
	return &BillAPI{
		&baseAPI{
			client:        client,
			apiRootSuffix: sakuraBillingAPIRootSuffix,
			FuncGetResourceURL: func() string {
				return "bill"
			},
		},
	}
}

// BillResponse 請求情報レスポンス
type BillResponse struct {
	*sacloud.ResultFlagValue
	// Count 件数
	Count int `json:",omitempty"`
	// ResponsedAt 応答日時
	ResponsedAt *time.Time `json:",omitempty"`
	// Bills 請求情報 リスト
	Bills []*sacloud.Bill
}

// BillDetailResponse 請求明細レスポンス
type BillDetailResponse struct {
	*sacloud.ResultFlagValue
	// Count 件数
	Count int `json:",omitempty"`
	// ResponsedAt 応答日時
	ResponsedAt *time.Time `json:",omitempty"`
	// BillDetails 請求明細 リスト
	BillDetails []*sacloud.BillDetail
}

// BillDetailCSVResponse 請求明細CSVレスポンス
type BillDetailCSVResponse struct {
	*sacloud.ResultFlagValue
	// Count 件数
	Count int `json:",omitempty"`
	// ResponsedAt 応答日時
	ResponsedAt *time.Time `json:",omitempty"`
	// Filename ファイル名
	Filename string `json:",omitempty"`
	// RawBody ボディ(未加工)
	RawBody string `json:"Body,omitempty"`
	// HeaderRow ヘッダ行
	HeaderRow []string
	// BodyRows ボディ(各行/各列での配列)
	BodyRows [][]string
}

func (res *BillDetailCSVResponse) buildCSVBody() {

	if res == nil || res.RawBody == "" {
		return
	}

	//CSV分割(先頭行/それ以降)、
	reader := csv.NewReader(strings.NewReader(res.RawBody))
	reader.LazyQuotes = true

	isFirst := true
	res.BodyRows = [][]string{}
	for {
		record, err := reader.Read()
		if err == io.EOF {
			break
		} else if err != nil {
			panic(err)
		}

		if isFirst {
			res.HeaderRow = record
			isFirst = false
		} else {
			res.BodyRows = append(res.BodyRows, record)
		}
	}
}

// ByContract アカウントIDごとの請求取得
func (api *BillAPI) ByContract(accountID int64) (*BillResponse, error) {

	uri := fmt.Sprintf("%s/by-contract/%d", api.getResourceURL(), accountID)
	return api.getContract(uri)
}

// ByContractYear 年指定での請求取得
func (api *BillAPI) ByContractYear(accountID int64, year int) (*BillResponse, error) {
	uri := fmt.Sprintf("%s/by-contract/%d/%d", api.getResourceURL(), accountID, year)
	return api.getContract(uri)
}

// ByContractYearMonth 年月指定での請求指定
func (api *BillAPI) ByContractYearMonth(accountID int64, year int, month int) (*BillResponse, error) {
	uri := fmt.Sprintf("%s/by-contract/%d/%d/%d", api.getResourceURL(), accountID, year, month)
	return api.getContract(uri)
}

// Read 読み取り
func (api *BillAPI) Read(billNo int64) (*BillResponse, error) {
	uri := fmt.Sprintf("%s/id/%d/", api.getResourceURL(), billNo)
	return api.getContract(uri)

}

func (api *BillAPI) getContract(uri string) (*BillResponse, error) {

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

}

// GetDetail 請求明細取得
func (api *BillAPI) GetDetail(memberCD string, billNo int64) (*BillDetailResponse, error) {

	oldFunc := api.FuncGetResourceURL
	defer func() { api.FuncGetResourceURL = oldFunc }()
	api.FuncGetResourceURL = func() string {
		return "billdetail"
	}

	uri := fmt.Sprintf("%s/%s/%d", api.getResourceURL(), memberCD, billNo)
	data, err := api.client.newRequest("GET", uri, nil)
	if err != nil {
		return nil, err
	}
	var res BillDetailResponse
	if err := json.Unmarshal(data, &res); err != nil {
		return nil, err
	}
	return &res, nil

}

// GetDetailCSV 請求明細CSV取得
func (api *BillAPI) GetDetailCSV(memberCD string, billNo int64) (*BillDetailCSVResponse, error) {

	oldFunc := api.FuncGetResourceURL
	defer func() { api.FuncGetResourceURL = oldFunc }()
	api.FuncGetResourceURL = func() string {
		return "billdetail"
	}

	uri := fmt.Sprintf("%s/%s/%d/csv", api.getResourceURL(), memberCD, billNo)
	data, err := api.client.newRequest("GET", uri, nil)
	if err != nil {
		return nil, err
	}
	var res BillDetailCSVResponse
	if err := json.Unmarshal(data, &res); err != nil {
		return nil, err
	}

	// build HeaderRow and BodyRows from RawBody
	res.buildCSVBody()

	return &res, nil

}
