package schema

import (
	"maps"
	"time"
)

// Summary is the top-level summary block written into status JSON.
//
// Field order matters for wire compatibility — declare in the canonical order.
type Summary struct {
	UpdatedAt  time.Time `json:"updatedAt"`
	Total      int       `json:"total"`
	Imported   int       `json:"imported"`
	Verified   int       `json:"verified"`
	Failed     int       `json:"failed"`
	Pending    int       `json:"pending"`
	TotalBytes int64     `json:"totalBytes"`
}

// Verification holds per-shard verification details written by verify.
//
// The maps are omitempty so absent blocks serialize as omitted instead of
// null.
type Verification struct {
	OriginCounts    map[string]int64 `json:"originCounts"`
	CandidateCounts map[string]int64 `json:"candidateCounts,omitempty"`
	Diffs           map[string]int64 `json:"diffs,omitempty"`
}

// Shard mirrors one entry in status.shards[].
//
// Phase is typed (schema.Phase) so illegal transitions are caught at compile
// time when callers use the Phase.Transition helper.
type Shard struct {
	ShardID      string        `json:"shardID"`
	Tenant       string        `json:"tenant"`
	TimeStart    string        `json:"timeStart"`
	TimeEnd      string        `json:"timeEnd"`
	Phase        Phase         `json:"phase"`
	Verified     bool          `json:"verified"`
	Attempts     int           `json:"attempts"`
	Bytes        *int64        `json:"bytes"`
	StartedAt    *time.Time    `json:"startedAt"`
	FinishedAt   *time.Time    `json:"finishedAt"`
	LastError    *string       `json:"lastError"`
	VerifiedAt   *time.Time    `json:"verifiedAt"`
	Verification *Verification `json:"verification"`
}

// Document is the top-level status JSON.
type Document struct {
	ManifestRef string  `json:"manifestRef"`
	Region      string  `json:"region,omitempty"`
	Namespace   string  `json:"namespace,omitempty"`
	Vmcluster   string  `json:"vmcluster,omitempty"`
	Shards      []Shard `json:"shards"`
	Summary     Summary `json:"summary"`
}

// CloneDocument returns a deep copy of d. Nil pointer fields and nil maps
// stay nil — Verification maps are omitempty and an empty-map clone would
// change the serialized output.
func CloneDocument(d *Document) *Document {
	if d == nil {
		return nil
	}
	out := new(Document)
	*out = *d
	out.Shards = make([]Shard, len(d.Shards))
	for i := range d.Shards {
		s := &d.Shards[i]
		c := &out.Shards[i]
		*c = *s
		c.Bytes = clonePtr(s.Bytes)
		c.StartedAt = clonePtr(s.StartedAt)
		c.FinishedAt = clonePtr(s.FinishedAt)
		c.VerifiedAt = clonePtr(s.VerifiedAt)
		c.LastError = clonePtr(s.LastError)
		c.Verification = cloneVerification(s.Verification)
	}
	return out
}

func clonePtr[T any](p *T) *T {
	if p == nil {
		return nil
	}
	v := *p
	return &v
}

func cloneVerification(v *Verification) *Verification {
	if v == nil {
		return nil
	}
	return &Verification{
		OriginCounts:    maps.Clone(v.OriginCounts),
		CandidateCounts: maps.Clone(v.CandidateCounts),
		Diffs:           maps.Clone(v.Diffs),
	}
}
