// Copyright 2022-2025 The sacloud/iaas-api-go Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License 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 naked

import (
	"encoding/json"
	"sort"
	"strings"
	"time"
)

// MonitorValues アクティビティモニタのレスポンス向け汎用エンベロープ
type MonitorValues struct {
	// CPU CPU-TIME
	CPU MonitorCPUTimeValues
	// Disk Read/Write
	Disk MonitorDiskValues
	// Interface Send/Receive
	Interface MonitorInterfaceValues
	// Router In/Out
	Router MonitorRouterValues
	// Database データベース
	Database MonitorDatabaseValues
	// FreeDiskSize 空きディスクサイズ(NFS)
	FreeDiskSize MonitorFreeDiskSizeValues
	// ResponseTimeSec 応答時間(シンプル監視)
	ResponseTimeSec MonitorResponseTimeSecValues
	// Link UplinkBPS/DownlinkBPS
	Link MonitorLinkValues
	// Connection 接続数
	Connection MonitorConnectionValues
	// LocalRouter Receive/Send bytes per sec
	LocalRouter MonitorLocalRouterValues
}

// UnmarshalJSON アクティビティモニタ向けUnmarshalJSON実装
func (m *MonitorValues) UnmarshalJSON(data []byte) error {
	v := MonitorValues{}

	// CPU
	if err := json.Unmarshal(data, &v.CPU); err != nil {
		return nil
	}
	if len(v.CPU) > 0 {
		*m = v
		return nil
	}

	//	Disk
	if err := json.Unmarshal(data, &v.Disk); err != nil {
		return nil
	}
	if len(v.Disk) > 0 {
		*m = v
		return nil
	}

	//	Interface
	if err := json.Unmarshal(data, &v.Interface); err != nil {
		return nil
	}
	if len(v.Interface) > 0 {
		*m = v
		return nil
	}

	//	Router
	if err := json.Unmarshal(data, &v.Router); err != nil {
		return nil
	}
	if len(v.Router) > 0 {
		*m = v
		return nil
	}

	//	Database
	if err := json.Unmarshal(data, &v.Database); err != nil {
		return nil
	}
	if len(v.Database) > 0 {
		*m = v
		return nil
	}

	//	FreeDiskSize
	if err := json.Unmarshal(data, &v.FreeDiskSize); err != nil {
		return nil
	}
	if len(v.FreeDiskSize) > 0 {
		*m = v
		return nil
	}

	//	ResponseTimeSec
	if err := json.Unmarshal(data, &v.ResponseTimeSec); err != nil {
		return nil
	}
	if len(v.ResponseTimeSec) > 0 {
		*m = v
		return nil
	}

	//	Link
	if err := json.Unmarshal(data, &v.Link); err != nil {
		return nil
	}
	if len(v.Link) > 0 {
		*m = v
		return nil
	}

	//	Connection
	if err := json.Unmarshal(data, &v.Connection); err != nil {
		return nil
	}
	if len(v.Connection) > 0 {
		*m = v
		return nil
	}

	//	LocalRouter
	if err := json.Unmarshal(data, &v.LocalRouter); err != nil {
		return nil
	}
	if len(v.LocalRouter) > 0 {
		*m = v
		return nil
	}
	return nil
}

/************************************************
 * CPU-TIME
************************************************/

// MonitorCPUTimeValue CPU-TIMEアクティビティモニタ
type MonitorCPUTimeValue struct {
	Time    time.Time // 対象時刻
	CPUTime float64
}

// MonitorCPUTimeValues CPU-TIMEアクティビティモニタ
type MonitorCPUTimeValues []*MonitorCPUTimeValue

// UnmarshalJSON アクティビティモニタ向けUnmarshalJSON実装
func (m *MonitorCPUTimeValues) UnmarshalJSON(data []byte) error {
	targetData := strings.ReplaceAll(strings.ReplaceAll(string(data), " ", ""), "\n", "")
	if targetData == `[]` {
		return nil
	}

	var rawMonitorValues rawMonitorValues
	if err := json.Unmarshal(data, &rawMonitorValues); err != nil {
		return err
	}
	values, err := rawMonitorValues.monitorCPUTimeValues()
	if err != nil {
		return err
	}

	*m = values
	return nil
}

/************************************************
 * Disk(Read/Write)
************************************************/

// MonitorDiskValue アクティビティモニタ
type MonitorDiskValue struct {
	Time  time.Time
	Write float64
	Read  float64
}

// MonitorDiskValues アクティビティモニタ
type MonitorDiskValues []*MonitorDiskValue

// UnmarshalJSON アクティビティモニタ向けUnmarshalJSON実装
func (m *MonitorDiskValues) UnmarshalJSON(data []byte) error {
	targetData := strings.ReplaceAll(strings.ReplaceAll(string(data), " ", ""), "\n", "")
	if targetData == `[]` {
		return nil
	}

	var rawMonitorValues rawMonitorValues
	if err := json.Unmarshal(data, &rawMonitorValues); err != nil {
		return err
	}
	values, err := rawMonitorValues.monitorDiskValues()
	if err != nil {
		return err
	}

	*m = values
	return nil
}

/************************************************
 * Interface(Send/Receive)
************************************************/

// MonitorInterfaceValue アクティビティモニタ
type MonitorInterfaceValue struct {
	Time    time.Time
	Send    float64
	Receive float64
}

// MonitorInterfaceValues アクティビティモニタ
type MonitorInterfaceValues []*MonitorInterfaceValue

// UnmarshalJSON アクティビティモニタ向けUnmarshalJSON実装
func (m *MonitorInterfaceValues) UnmarshalJSON(data []byte) error {
	targetData := strings.ReplaceAll(strings.ReplaceAll(string(data), " ", ""), "\n", "")
	if targetData == `[]` {
		return nil
	}

	var rawMonitorValues rawMonitorValues
	if err := json.Unmarshal(data, &rawMonitorValues); err != nil {
		return err
	}
	values, err := rawMonitorValues.monitorInterfaceValues()
	if err != nil {
		return err
	}

	*m = values
	return nil
}

/************************************************
 * Router(In/Out)
************************************************/

// MonitorRouterValue アクティビティモニタ
type MonitorRouterValue struct {
	Time time.Time
	In   float64
	Out  float64
}

// MonitorRouterValues アクティビティモニタ
type MonitorRouterValues []*MonitorRouterValue

// UnmarshalJSON アクティビティモニタ向けUnmarshalJSON実装
func (m *MonitorRouterValues) UnmarshalJSON(data []byte) error {
	targetData := strings.ReplaceAll(strings.ReplaceAll(string(data), " ", ""), "\n", "")
	if targetData == `[]` {
		return nil
	}

	var rawMonitorValues rawMonitorValues
	if err := json.Unmarshal(data, &rawMonitorValues); err != nil {
		return err
	}
	values, err := rawMonitorValues.monitorRouterValues()
	if err != nil {
		return err
	}

	*m = values
	return nil
}

/************************************************
 * Database
************************************************/

// MonitorDatabaseValue アクティビティモニタ
type MonitorDatabaseValue struct {
	Time              time.Time // 対象時刻
	TotalMemorySize   float64
	UsedMemorySize    float64
	TotalDisk1Size    float64
	UsedDisk1Size     float64
	TotalDisk2Size    float64
	UsedDisk2Size     float64
	BinlogUsedSizeKiB float64
	DelayTimeSec      float64
}

// MonitorDatabaseValues アクティビティモニタ
type MonitorDatabaseValues []*MonitorDatabaseValue

// UnmarshalJSON アクティビティモニタ向けUnmarshalJSON実装
func (m *MonitorDatabaseValues) UnmarshalJSON(data []byte) error {
	targetData := strings.ReplaceAll(strings.ReplaceAll(string(data), " ", ""), "\n", "")
	if targetData == `[]` {
		return nil
	}

	var rawMonitorValues rawMonitorValues
	if err := json.Unmarshal(data, &rawMonitorValues); err != nil {
		return err
	}
	values, err := rawMonitorValues.monitorDatabaseValues()
	if err != nil {
		return err
	}

	*m = values
	return nil
}

/************************************************
 * FreeDiskSize
************************************************/

// MonitorFreeDiskSizeValue アクティビティモニタ
type MonitorFreeDiskSizeValue struct {
	Time         time.Time // 対象時刻
	FreeDiskSize float64
}

// MonitorFreeDiskSizeValues アクティビティモニタ
type MonitorFreeDiskSizeValues []*MonitorFreeDiskSizeValue

// UnmarshalJSON アクティビティモニタ向けUnmarshalJSON実装
func (m *MonitorFreeDiskSizeValues) UnmarshalJSON(data []byte) error {
	targetData := strings.ReplaceAll(strings.ReplaceAll(string(data), " ", ""), "\n", "")
	if targetData == `[]` {
		return nil
	}

	var rawMonitorValues rawMonitorValues
	if err := json.Unmarshal(data, &rawMonitorValues); err != nil {
		return err
	}
	values, err := rawMonitorValues.monitorFreeDiskSizeValues()
	if err != nil {
		return err
	}

	*m = values
	return nil
}

/************************************************
 * ResponseTimeSec
************************************************/

// MonitorResponseTimeSecValue アクティビティモニタ
type MonitorResponseTimeSecValue struct {
	Time            time.Time // 対象時刻
	ResponseTimeSec float64
}

// MonitorResponseTimeSecValues アクティビティモニタ
type MonitorResponseTimeSecValues []*MonitorResponseTimeSecValue

// UnmarshalJSON アクティビティモニタ向けUnmarshalJSON実装
func (m *MonitorResponseTimeSecValues) UnmarshalJSON(data []byte) error {
	targetData := strings.ReplaceAll(strings.ReplaceAll(string(data), " ", ""), "\n", "")
	if targetData == `[]` {
		return nil
	}

	var rawMonitorValues rawMonitorValues
	if err := json.Unmarshal(data, &rawMonitorValues); err != nil {
		return err
	}
	values, err := rawMonitorValues.monitorResponseTimeSecValues()
	if err != nil {
		return err
	}

	*m = values
	return nil
}

/************************************************
 * Link(up/down)
************************************************/

// MonitorLinkValue アクティビティモニタ
type MonitorLinkValue struct {
	Time        time.Time
	UplinkBPS   float64
	DownlinkBPS float64
}

// MonitorLinkValues アクティビティモニタ
type MonitorLinkValues []*MonitorLinkValue

// UnmarshalJSON アクティビティモニタ向けUnmarshalJSON実装
func (m *MonitorLinkValues) UnmarshalJSON(data []byte) error {
	targetData := strings.ReplaceAll(strings.ReplaceAll(string(data), " ", ""), "\n", "")
	if targetData == `[]` {
		return nil
	}

	var rawMonitorValues rawMonitorValues
	if err := json.Unmarshal(data, &rawMonitorValues); err != nil {
		return err
	}
	values, err := rawMonitorValues.monitorLinkValues()
	if err != nil {
		return err
	}

	*m = values
	return nil
}

/************************************************
 * Connection(ProxyLB)
************************************************/

// MonitorConnectionValue アクティビティモニタ
type MonitorConnectionValue struct {
	Time              time.Time
	ActiveConnections float64
	ConnectionsPerSec float64
}

// MonitorConnectionValues アクティビティモニタ
type MonitorConnectionValues []*MonitorConnectionValue

// UnmarshalJSON アクティビティモニタ向けUnmarshalJSON実装
func (m *MonitorConnectionValues) UnmarshalJSON(data []byte) error {
	targetData := strings.ReplaceAll(strings.ReplaceAll(string(data), " ", ""), "\n", "")
	if targetData == `[]` {
		return nil
	}

	var rawMonitorValues rawMonitorValues
	if err := json.Unmarshal(data, &rawMonitorValues); err != nil {
		return err
	}
	values, err := rawMonitorValues.monitorConnectionValues()
	if err != nil {
		return err
	}

	*m = values
	return nil
}

/************************************************
 * LocalRouter(Receive/Send BytesPerSec)
************************************************/

// MonitorLocalRouterValue アクティビティモニタ
type MonitorLocalRouterValue struct {
	Time               time.Time
	ReceiveBytesPerSec float64
	SendBytesPerSec    float64
}

// MonitorLocalRouterValues アクティビティモニタ
type MonitorLocalRouterValues []*MonitorLocalRouterValue

// UnmarshalJSON アクティビティモニタ向けUnmarshalJSON実装
func (m *MonitorLocalRouterValues) UnmarshalJSON(data []byte) error {
	targetData := strings.ReplaceAll(strings.ReplaceAll(string(data), " ", ""), "\n", "")
	if targetData == `[]` {
		return nil
	}

	var rawMonitorValues rawMonitorValues
	if err := json.Unmarshal(data, &rawMonitorValues); err != nil {
		return err
	}
	values, err := rawMonitorValues.monitorLocalRouterValues()
	if err != nil {
		return err
	}

	*m = values
	return nil
}

// rawMonitorValue アクティビティモニター
type rawMonitorValue struct {
	CPUTime            *float64 `json:"CPU-TIME,omitempty" yaml:"cpu_timme,omitempty" structs:",omitempty"`                       // CPU時間
	Write              *float64 `json:",omitempty" yaml:"write,omitempty" structs:",omitempty"`                                   // ディスク書き込み
	Read               *float64 `json:",omitempty" yaml:"read,omitempty" structs:",omitempty"`                                    // ディスク読み取り
	Receive            *float64 `json:",omitempty" yaml:"receive,omitempty" structs:",omitempty"`                                 // パケット受信
	Send               *float64 `json:",omitempty" yaml:"send,omitempty" structs:",omitempty"`                                    // パケット送信
	In                 *float64 `json:",omitempty" yaml:"in,omitempty" structs:",omitempty"`                                      // パケット受信
	Out                *float64 `json:",omitempty" yaml:"out,omitempty" structs:",omitempty"`                                     // パケット送信
	TotalMemorySize    *float64 `json:"Total-Memory-Size,omitempty" yaml:"total_memory_size,omitempty" structs:",omitempty"`      // 総メモリサイズ
	UsedMemorySize     *float64 `json:"Used-Memory-Size,omitempty" yaml:"used_memory_size,omitempty" structs:",omitempty"`        // 使用済みメモリサイズ
	TotalDisk1Size     *float64 `json:"Total-Disk1-Size,omitempty" yaml:"total_disk1_size,omitempty" structs:",omitempty"`        // 総ディスクサイズ
	UsedDisk1Size      *float64 `json:"Used-Disk1-Size,omitempty" yaml:"used_disk1_size,omitempty" structs:",omitempty"`          // 使用済みディスクサイズ
	TotalDisk2Size     *float64 `json:"Total-Disk2-Size,omitempty" yaml:"total_disk2_size,omitempty" structs:",omitempty"`        // 総ディスクサイズ
	UsedDisk2Size      *float64 `json:"Used-Disk2-Size,omitempty" yaml:"used_disk2_size,omitempty" structs:",omitempty"`          // 使用済みディスクサイズ
	BinlogUsedSizeKiB  *float64 `json:"binlogUsedSizeKiB,omitempty" yaml:"binlog_used_size_kib,omitempty" structs:",omitempty"`   // バイナリログのサイズ(レプリケーション有効時のみ、master/slave両方で利用可能)
	DelayTimeSec       *float64 `json:"delayTimeSec,omitempty" yaml:"delay_time_sec,omitempty" structs:",omitempty"`              // レプリケーション遅延時間(レプリケーション有効時のみ、slave側のみ)
	FreeDiskSize       *float64 `json:"Free-Disk-Size,omitempty" yaml:"free_disk_size,omitempty" structs:",omitempty"`            // 空きディスクサイズ(NFS)
	ResponseTimeSec    *float64 `json:"responsetimesec,omitempty" yaml:"response_time_sec,omitempty" structs:",omitempty"`        // レスポンスタイム(シンプル監視)
	UplinkBPS          *float64 `json:"UplinkBps,omitempty" yaml:"uplink_bps,omitempty" structs:",omitempty"`                     // 上り方向トラフィック
	DownlinkBPS        *float64 `json:"DownlinkBps,omitempty" yaml:"downlink_bps,omitempty" structs:",omitempty"`                 // 下り方向トラフィック
	ActiveConnections  *float64 `json:"activeConnections,omitempty" yaml:"active_connections,omitempty" structs:",omitempty"`     // アクティブコネクション(プロキシLB)
	ConnectionsPerSec  *float64 `json:"connectionsPerSec,omitempty" yaml:"connections_per_sec,omitempty" structs:",omitempty"`    // 秒間コネクション数
	ReceiveBytesPerSec *float64 `json:"receiveBytesPerSec,omitempty" yaml:"receive_bytes_per_sec,omitempty" structs:",omitempty"` // 秒間受信バイト数
	SendBytesPerSec    *float64 `json:"sendBytesPerSec,omitempty" yaml:"send_bytes_per_sec,omitempty" structs:",omitempty"`       // 秒間送信バイト数
}

// UnmarshalJSON JSONアンマーシャル(配列、オブジェクトが混在するためここで対応)
func (m *rawMonitorValue) UnmarshalJSON(data []byte) error {
	targetData := strings.ReplaceAll(strings.ReplaceAll(string(data), " ", ""), "\n", "")
	if targetData == `[]` {
		return nil
	}

	tmp := &struct {
		CPUTime            *float64 `json:"CPU-TIME,omitempty" yaml:"cpu_timme,omitempty" structs:",omitempty"`
		Write              *float64 `json:",omitempty" yaml:"write,omitempty" structs:",omitempty"`
		Read               *float64 `json:",omitempty" yaml:"read,omitempty" structs:",omitempty"`
		Receive            *float64 `json:",omitempty" yaml:"receive,omitempty" structs:",omitempty"`
		Send               *float64 `json:",omitempty" yaml:"send,omitempty" structs:",omitempty"`
		In                 *float64 `json:",omitempty" yaml:"in,omitempty" structs:",omitempty"`
		Out                *float64 `json:",omitempty" yaml:"out,omitempty" structs:",omitempty"`
		TotalMemorySize    *float64 `json:"Total-Memory-Size,omitempty" yaml:"total_memory_size,omitempty" structs:",omitempty"`
		UsedMemorySize     *float64 `json:"Used-Memory-Size,omitempty" yaml:"used_memory_size,omitempty" structs:",omitempty"`
		TotalDisk1Size     *float64 `json:"Total-Disk1-Size,omitempty" yaml:"total_disk1_size,omitempty" structs:",omitempty"`
		UsedDisk1Size      *float64 `json:"Used-Disk1-Size,omitempty" yaml:"used_disk1_size,omitempty" structs:",omitempty"`
		TotalDisk2Size     *float64 `json:"Total-Disk2-Size,omitempty" yaml:"total_disk2_size,omitempty" structs:",omitempty"`
		UsedDisk2Size      *float64 `json:"Used-Disk2-Size,omitempty" yaml:"used_disk2_size,omitempty" structs:",omitempty"`
		BinlogUsedSizeKiB  *float64 `json:"binlogUsedSizeKiB,omitempty" yaml:"binlog_used_size_kib,omitempty" structs:",omitempty"`
		DelayTimeSec       *float64 `json:"delayTimeSec,omitempty" yaml:"delay_time_sec,omitempty" structs:",omitempty"`
		FreeDiskSize       *float64 `json:"Free-Disk-Size,omitempty" yaml:"free_disk_size,omitempty" structs:",omitempty"`
		ResponseTimeSec    *float64 `json:"responsetimesec,omitempty" yaml:"response_time_sec,omitempty" structs:",omitempty"`
		UplinkBPS          *float64 `json:"UplinkBps,omitempty" yaml:"uplink_bps,omitempty" structs:",omitempty"`
		DownlinkBPS        *float64 `json:"DownlinkBps,omitempty" yaml:"downlink_bps,omitempty" structs:",omitempty"`
		ActiveConnections  *float64 `json:"activeConnections,omitempty" yaml:"active_connections,omitempty" structs:",omitempty"`
		ConnectionsPerSec  *float64 `json:"connectionsPerSec,omitempty" yaml:"connections_per_sec,omitempty" structs:",omitempty"`
		ReceiveBytesPerSec *float64 `json:"receiveBytesPerSec,omitempty" yaml:"receive_bytes_per_sec,omitempty" structs:",omitempty"`
		SendBytesPerSec    *float64 `json:"sendBytesPerSec,omitempty" yaml:"send_bytes_per_sec,omitempty" structs:",omitempty"`
	}{}
	if err := json.Unmarshal(data, &tmp); err != nil {
		return err
	}

	m.CPUTime = tmp.CPUTime
	m.Write = tmp.Write
	m.Read = tmp.Read
	m.Receive = tmp.Receive
	m.Send = tmp.Send
	m.In = tmp.In
	m.Out = tmp.Out
	m.TotalMemorySize = tmp.TotalMemorySize
	m.UsedMemorySize = tmp.UsedMemorySize
	m.TotalDisk1Size = tmp.TotalDisk1Size
	m.UsedDisk1Size = tmp.UsedDisk1Size
	m.TotalDisk2Size = tmp.TotalDisk2Size
	m.UsedDisk2Size = tmp.UsedDisk2Size
	m.BinlogUsedSizeKiB = tmp.BinlogUsedSizeKiB
	m.DelayTimeSec = tmp.DelayTimeSec
	m.FreeDiskSize = tmp.FreeDiskSize
	m.ResponseTimeSec = tmp.ResponseTimeSec
	m.UplinkBPS = tmp.UplinkBPS
	m.DownlinkBPS = tmp.DownlinkBPS
	m.ActiveConnections = tmp.ActiveConnections
	m.ConnectionsPerSec = tmp.ConnectionsPerSec
	m.ReceiveBytesPerSec = tmp.ReceiveBytesPerSec
	m.SendBytesPerSec = tmp.SendBytesPerSec

	return nil
}

type rawMonitorValues map[string]*rawMonitorValue

func (m *rawMonitorValues) monitorCPUTimeValues() (MonitorCPUTimeValues, error) {
	var values MonitorCPUTimeValues

	for k, v := range *m {
		if v.CPUTime == nil {
			continue
		}
		time, err := time.Parse(time.RFC3339, k) // RFC3339 ≒ ISO8601
		if err != nil {
			return nil, err
		}
		values = append(values, &MonitorCPUTimeValue{
			Time:    time,
			CPUTime: *v.CPUTime,
		})
	}

	sort.Slice(values, func(i, j int) bool { return values[i].Time.Before(values[j].Time) })
	return values, nil
}

func (m *rawMonitorValues) monitorDiskValues() (MonitorDiskValues, error) {
	var values MonitorDiskValues

	for k, v := range *m {
		if v.Read == nil || v.Write == nil {
			continue
		}
		time, err := time.Parse(time.RFC3339, k) // RFC3339 ≒ ISO8601
		if err != nil {
			return nil, err
		}
		values = append(values, &MonitorDiskValue{
			Time:  time,
			Read:  *v.Read,
			Write: *v.Write,
		})
	}

	sort.Slice(values, func(i, j int) bool { return values[i].Time.Before(values[j].Time) })
	return values, nil
}

func (m *rawMonitorValues) monitorInterfaceValues() (MonitorInterfaceValues, error) {
	var values MonitorInterfaceValues

	for k, v := range *m {
		if v.Send == nil || v.Receive == nil {
			continue
		}
		time, err := time.Parse(time.RFC3339, k) // RFC3339 ≒ ISO8601
		if err != nil {
			return nil, err
		}
		values = append(values, &MonitorInterfaceValue{
			Time:    time,
			Send:    *v.Send,
			Receive: *v.Receive,
		})
	}

	sort.Slice(values, func(i, j int) bool { return values[i].Time.Before(values[j].Time) })
	return values, nil
}

func (m *rawMonitorValues) monitorRouterValues() (MonitorRouterValues, error) {
	var values MonitorRouterValues

	for k, v := range *m {
		if v.In == nil || v.Out == nil {
			continue
		}
		time, err := time.Parse(time.RFC3339, k) // RFC3339 ≒ ISO8601
		if err != nil {
			return nil, err
		}
		values = append(values, &MonitorRouterValue{
			Time: time,
			In:   *v.In,
			Out:  *v.Out,
		})
	}

	sort.Slice(values, func(i, j int) bool { return values[i].Time.Before(values[j].Time) })
	return values, nil
}

func (m *rawMonitorValues) monitorDatabaseValues() (MonitorDatabaseValues, error) {
	var values MonitorDatabaseValues

	for k, v := range *m {
		if v.TotalMemorySize == nil || v.UsedMemorySize == nil ||
			v.TotalDisk1Size == nil || v.UsedDisk1Size == nil ||
			v.TotalDisk2Size == nil || v.UsedDisk2Size == nil {
			continue
		}
		time, err := time.Parse(time.RFC3339, k) // RFC3339 ≒ ISO8601
		if err != nil {
			return nil, err
		}
		value := &MonitorDatabaseValue{
			Time:            time,
			TotalMemorySize: *v.TotalMemorySize,
			UsedMemorySize:  *v.UsedMemorySize,
			TotalDisk1Size:  *v.TotalDisk1Size,
			UsedDisk1Size:   *v.UsedDisk1Size,
			TotalDisk2Size:  *v.TotalDisk2Size,
			UsedDisk2Size:   *v.UsedDisk2Size,
		}
		if v.BinlogUsedSizeKiB != nil {
			value.BinlogUsedSizeKiB = *v.BinlogUsedSizeKiB
		}
		if v.DelayTimeSec != nil {
			value.DelayTimeSec = *v.DelayTimeSec
		}
		values = append(values, value)
	}

	sort.Slice(values, func(i, j int) bool { return values[i].Time.Before(values[j].Time) })
	return values, nil
}

func (m *rawMonitorValues) monitorFreeDiskSizeValues() (MonitorFreeDiskSizeValues, error) {
	var values MonitorFreeDiskSizeValues

	for k, v := range *m {
		if v.FreeDiskSize == nil {
			continue
		}
		time, err := time.Parse(time.RFC3339, k) // RFC3339 ≒ ISO8601
		if err != nil {
			return nil, err
		}
		values = append(values, &MonitorFreeDiskSizeValue{
			Time:         time,
			FreeDiskSize: *v.FreeDiskSize,
		})
	}

	sort.Slice(values, func(i, j int) bool { return values[i].Time.Before(values[j].Time) })
	return values, nil
}

func (m *rawMonitorValues) monitorResponseTimeSecValues() (MonitorResponseTimeSecValues, error) {
	var values MonitorResponseTimeSecValues

	for k, v := range *m {
		if v.ResponseTimeSec == nil {
			continue
		}
		time, err := time.Parse(time.RFC3339, k) // RFC3339 ≒ ISO8601
		if err != nil {
			return nil, err
		}
		values = append(values, &MonitorResponseTimeSecValue{
			Time:            time,
			ResponseTimeSec: *v.ResponseTimeSec,
		})
	}

	sort.Slice(values, func(i, j int) bool { return values[i].Time.Before(values[j].Time) })
	return values, nil
}

func (m *rawMonitorValues) monitorLinkValues() (MonitorLinkValues, error) {
	var values MonitorLinkValues

	for k, v := range *m {
		if v.UplinkBPS == nil || v.DownlinkBPS == nil {
			continue
		}
		time, err := time.Parse(time.RFC3339, k) // RFC3339 ≒ ISO8601
		if err != nil {
			return nil, err
		}
		values = append(values, &MonitorLinkValue{
			Time:        time,
			UplinkBPS:   *v.UplinkBPS,
			DownlinkBPS: *v.DownlinkBPS,
		})
	}

	sort.Slice(values, func(i, j int) bool { return values[i].Time.Before(values[j].Time) })
	return values, nil
}

func (m *rawMonitorValues) monitorConnectionValues() (MonitorConnectionValues, error) {
	var values MonitorConnectionValues

	for k, v := range *m {
		if v.ActiveConnections == nil || v.ConnectionsPerSec == nil {
			continue
		}
		time, err := time.Parse(time.RFC3339, k) // RFC3339 ≒ ISO8601
		if err != nil {
			return nil, err
		}
		values = append(values, &MonitorConnectionValue{
			Time:              time,
			ActiveConnections: *v.ActiveConnections,
			ConnectionsPerSec: *v.ConnectionsPerSec,
		})
	}

	sort.Slice(values, func(i, j int) bool { return values[i].Time.Before(values[j].Time) })
	return values, nil
}

func (m *rawMonitorValues) monitorLocalRouterValues() (MonitorLocalRouterValues, error) {
	var values MonitorLocalRouterValues

	for k, v := range *m {
		if v.ReceiveBytesPerSec == nil || v.SendBytesPerSec == nil {
			continue
		}
		time, err := time.Parse(time.RFC3339, k) // RFC3339 ≒ ISO8601
		if err != nil {
			return nil, err
		}
		values = append(values, &MonitorLocalRouterValue{
			Time:               time,
			ReceiveBytesPerSec: *v.ReceiveBytesPerSec,
			SendBytesPerSec:    *v.SendBytesPerSec,
		})
	}

	sort.Slice(values, func(i, j int) bool { return values[i].Time.Before(values[j].Time) })
	return values, nil
}
