package restore

import (
	"time"

	"github.com/restic/restic/internal/ui"
)

type jsonPrinter struct {
	terminal  ui.Terminal
	verbosity uint
}

func NewJSONProgress(terminal ui.Terminal, verbosity uint) ProgressPrinter {
	return &jsonPrinter{
		terminal:  terminal,
		verbosity: verbosity,
	}
}

func (t *jsonPrinter) print(status interface{}) {
	t.terminal.Print(ui.ToJSONString(status))
}

func (t *jsonPrinter) error(status interface{}) {
	t.terminal.Error(ui.ToJSONString(status))
}

func (t *jsonPrinter) Update(p State, duration time.Duration) {
	status := statusUpdate{
		MessageType:    "status",
		SecondsElapsed: uint64(duration / time.Second),
		TotalFiles:     p.FilesTotal,
		FilesRestored:  p.FilesFinished,
		FilesSkipped:   p.FilesSkipped,
		FilesDeleted:   p.FilesDeleted,
		TotalBytes:     p.AllBytesTotal,
		BytesRestored:  p.AllBytesWritten,
		BytesSkipped:   p.AllBytesSkipped,
	}

	if p.AllBytesTotal > 0 {
		status.PercentDone = float64(p.AllBytesWritten) / float64(p.AllBytesTotal)
	}

	t.print(status)
}

func (t *jsonPrinter) Error(item string, err error) error {
	t.error(errorUpdate{
		MessageType: "error",
		Error:       errorObject{err.Error()},
		During:      "restore",
		Item:        item,
	})
	return nil
}

func (t *jsonPrinter) CompleteItem(messageType ItemAction, item string, size uint64) {
	if t.verbosity < 3 {
		return
	}

	var action string
	switch messageType {
	case ActionDirRestored:
		action = "restored"
	case ActionFileRestored:
		action = "restored"
	case ActionOtherRestored:
		action = "restored"
	case ActionFileUpdated:
		action = "updated"
	case ActionFileUnchanged:
		action = "unchanged"
	case ActionDeleted:
		action = "deleted"
	default:
		panic("unknown message type")
	}

	status := verboseUpdate{
		MessageType: "verbose_status",
		Action:      action,
		Item:        item,
		Size:        size,
	}
	t.print(status)
}

func (t *jsonPrinter) Finish(p State, duration time.Duration) {
	status := summaryOutput{
		MessageType:    "summary",
		SecondsElapsed: uint64(duration / time.Second),
		TotalFiles:     p.FilesTotal,
		FilesRestored:  p.FilesFinished,
		FilesSkipped:   p.FilesSkipped,
		FilesDeleted:   p.FilesDeleted,
		TotalBytes:     p.AllBytesTotal,
		BytesRestored:  p.AllBytesWritten,
		BytesSkipped:   p.AllBytesSkipped,
	}
	t.print(status)
}

type statusUpdate struct {
	MessageType    string  `json:"message_type"` // "status"
	SecondsElapsed uint64  `json:"seconds_elapsed,omitempty"`
	PercentDone    float64 `json:"percent_done"`
	TotalFiles     uint64  `json:"total_files,omitempty"`
	FilesRestored  uint64  `json:"files_restored,omitempty"`
	FilesSkipped   uint64  `json:"files_skipped,omitempty"`
	FilesDeleted   uint64  `json:"files_deleted,omitempty"`
	TotalBytes     uint64  `json:"total_bytes,omitempty"`
	BytesRestored  uint64  `json:"bytes_restored,omitempty"`
	BytesSkipped   uint64  `json:"bytes_skipped,omitempty"`
}

type errorObject struct {
	Message string `json:"message"`
}

type errorUpdate struct {
	MessageType string      `json:"message_type"` // "error"
	Error       errorObject `json:"error"`
	During      string      `json:"during"`
	Item        string      `json:"item"`
}

type verboseUpdate struct {
	MessageType string `json:"message_type"` // "verbose_status"
	Action      string `json:"action"`
	Item        string `json:"item"`
	Size        uint64 `json:"size"`
}

type summaryOutput struct {
	MessageType    string `json:"message_type"` // "summary"
	SecondsElapsed uint64 `json:"seconds_elapsed,omitempty"`
	TotalFiles     uint64 `json:"total_files,omitempty"`
	FilesRestored  uint64 `json:"files_restored,omitempty"`
	FilesSkipped   uint64 `json:"files_skipped,omitempty"`
	FilesDeleted   uint64 `json:"files_deleted,omitempty"`
	TotalBytes     uint64 `json:"total_bytes,omitempty"`
	BytesRestored  uint64 `json:"bytes_restored,omitempty"`
	BytesSkipped   uint64 `json:"bytes_skipped,omitempty"`
}
