package ledger

import (
	"fmt"
	"maps"
	"slices"
	"time"
)

type Register map[string]Balances

func (register Register) Latest() Balances {
	dates := register.Dates()
	if len(dates) == 0 {
		return nil
	}
	date := dates[len(dates)-1]
	return register[date]
}

func (register Register) WithBPIs(bpis BPIs) Register {
	result := make(Register)
	for d := range register {
		result[d] = register[d].WithBPIsAt(bpis, d)
	}
	return result
}

func (register Register) PushAll(other Register) {
	for date := range other {
		if _, ok := register[date]; !ok {
			register[date] = make(Balances)
		}
		register[date].PushAll(other[date])
	}
}

func (register Register) Names() []string {
	names := map[string]int{}
	for _, v := range register {
		for name := range v {
			names[name] = 1
		}
	}

	result := make([]string, 0, len(register))
	for k := range names {
		result = append(result, k)
	}
	slices.Sort(result)
	return result
}

func (register Register) Like(pattern string) Register {
	result := make(Register)
	for k, v := range register {
		if got := v.Like(pattern); len(got) > 0 {
			result[k] = got
		}
	}
	return result
}

func (register Register) NotLike(pattern string) Register {
	result := make(Register)
	for k, v := range register {
		if got := v.NotLike(pattern); len(got) > 0 {
			result[k] = got
		}
	}
	return result
}

func (register Register) Dates() []string {
	result := make([]string, 0, len(register))
	for k := range register {
		result = append(result, k)
	}
	slices.Sort(result)
	return result
}

func (register Register) Between(start, end time.Time) Register {
	result := make(Register)
	for k := range register {
		t := mustDateToTime(k)
		if t.Before(start) || t.After(end) {
			continue
		}
		result[k] = maps.Clone(register[k])
	}
	return result
}

func (register Register) Times() []time.Time {
	dates := register.Dates()
	result := make([]time.Time, len(dates))
	for i := range dates {
		result[i] = mustDateToTime(dates[i])
	}
	return result
}

func mustDateToTime(s string) time.Time {
	result, err := dateToTime(s)
	if err != nil {
		panic(err)
	}
	return result
}

func dateToTime(s string) (time.Time, error) {
	for _, layout := range []string{
		"2006-01-02",
		"2006-01",
	} {
		if t, err := time.ParseInLocation(layout, s, time.Local); err == nil {
			return t, err
		}
	}
	return time.Time{}, fmt.Errorf("no layout matching %q", s)
}

func (register Register) Debug() string {
	result := "{"
	for _, date := range register.Dates() {
		result += "{" + date
		for name, balance := range register[date] {
			result += "{" + name
			for cur, v := range balance {
				result += fmt.Sprintf("%s=%.2f ", cur, v)
			}
			result += "}"
		}
		result += "}"
	}
	return result + "}"
}
