/*
Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments
Copyright (C) ITsysCOM GmbH

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>
*/

package utils

import (
	"encoding/json"
	"errors"
	"fmt"
	"strconv"
	"time"
)

// ValueFormula for computing a dynamic value for Value field
type ValueFormula struct {
	Method string
	Params map[string]any
	Static float64
}

func ParseBalanceFilterValue(tor string, val string) (*ValueFormula, error) {
	if tor == MetaVoice { // Voice balance is parsed as nanoseconds with support for time duration strings
		if d, err := ParseDurationWithNanosecs(val); err == nil {
			return &ValueFormula{Static: float64(d.Nanoseconds())}, err
		}
	} else if u, err := strconv.ParseFloat(val, 64); err == nil {
		return &ValueFormula{Static: u}, err
	}
	var vf ValueFormula
	if err := json.Unmarshal([]byte(val), &vf); err == nil {
		return &vf, err
	}
	return nil, errors.New("Invalid value: " + val)
}

type valueFormula func(map[string]any) float64

const (
	INCREMENTAL = "*incremental"
)

var ValueFormulas = map[string]valueFormula{
	INCREMENTAL: incrementalFormula,
}

func (vf *ValueFormula) String() string {
	return ToJSON(vf)
}

func incrementalFormula(params map[string]any) float64 {
	// check parameters
	unitsInterface, unitsFound := params["Units"]
	intervalInterface, intervalFound := params["Interval"]
	incrementInterface, incrementFound := params["Increment"]

	if !unitsFound || !intervalFound || !incrementFound {
		return 0.0
	}
	units, ok := unitsInterface.(float64)
	if !ok {
		return 0.0
	}
	var interval string
	switch intr := intervalInterface.(type) {
	case string:
		interval = intr
	case []byte:
		interval = string(intr)
	default:
		return 0.0
	}
	var increment string
	switch incr := incrementInterface.(type) {
	case string:
		increment = incr
	case []byte:
		increment = string(incr)
	default:
		return 0.0
	}
	now := time.Now()
	if increment == "day" {
		if interval == "week" {
			return units / 7
		}
		if interval == "month" {
			return units / DaysInMonth(now.Year(), now.Month())
		}
		if interval == "year" {
			return units / DaysInYear(now.Year())
		}
	}
	if increment == "hour" {
		if interval == "day" {
			return units / 24
		}
		if interval == "month" {
			return units / (DaysInMonth(now.Year(), now.Month()) * 24)
		}
		if interval == "year" {
			return units / (DaysInYear(now.Year()) * 24)
		}
	}
	if increment == "minute" {
		if interval == "hour" {
			return units / 60
		}
	}
	return 0.0
}

func (vf *ValueFormula) FieldAsInterface(fldPath []string) (val any, err error) {
	if vf == nil || len(fldPath) == 0 {
		return nil, ErrNotFound
	}
	switch fldPath[0] {
	default:
		opath, indx := GetPathIndexString(fldPath[0])
		if indx != nil && opath == Params {
			return MapStorage(vf.Params).FieldAsInterface(append([]string{*indx}, fldPath[1:]...))
		}
		return nil, fmt.Errorf("unsupported field prefix: <%s>", fldPath[0])
	case Method:
		if len(fldPath) != 1 {
			return nil, ErrNotFound
		}
		return vf.Method, nil
	case Static:
		if len(fldPath) != 1 {
			return nil, ErrNotFound
		}
		return vf.Static, nil
	case Params:
		if len(fldPath) == 1 {
			return vf.Params, nil
		}
		return MapStorage(vf.Params).FieldAsInterface(fldPath[1:])
	}
}

func (vf *ValueFormula) FieldAsString(fldPath []string) (val string, err error) {
	var iface any
	iface, err = vf.FieldAsInterface(fldPath)
	if err != nil {
		return
	}
	return IfaceAsString(iface), nil
}
