package main

import (
	"fmt"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/rs/zerolog/log"
	"gopkg.in/yaml.v2"
)

type config struct {
	Jobs         []jobconfig          `yaml:"jobs"`
	Notification []notificationConfig `yaml:"notification"`
}

type jobconfig struct {
	Name         string `yaml:"name"`
	Hour         string `yaml:"hour"`
	Minute       string `yaml:"minute"`
	Command      string `yaml:"command"`
	Notification string `yaml:"notification,omitempty"`
}

type notificationConfig struct {
	Name    string       `yaml:"name"`
	Success notification `yaml:"success,omitempty"`
	Error   notification `yaml:"error,omitempty"`
}

type notification struct {
	Gotify gotifyConfig `yaml:"gotify,omitempty"`
}

type gotifyConfig struct {
	URL string `yaml:"url"`
}

func (n notification) SendNotification(job jobconfig, execCode int, outString string, err error) error {
	if n.Gotify.URL != "" {
	}
	return nil
}

func ReadFromFile(path string) (config, error) {
	f, err := os.ReadFile(path)
	if err != nil {
		return config{}, fmt.Errorf(ErrWrapTemplate, ErrCantReadConfigFile, err)
	}

	c := config{}
	err = yaml.Unmarshal(f, &c)
	if err != nil {
		return config{}, fmt.Errorf(ErrWrapTemplate, ErrCantParseConfigFile, err)
	}

	return c, nil
}

func (jc jobconfig) MatchCurrentTime(t time.Time) (bool, error) {
	matchHour, err := matchTime(jc.Hour, t.Hour())
	if err != nil {
		return false, err
	}
	if !matchHour {
		return false, nil
	}

	matchMinute, err := matchTime(jc.Minute, t.Minute())
	if err != nil {
		return false, err
	}
	if !matchMinute {
		return false, nil
	}

	return true, nil
}

func matchTime(pattern string, currentValue int) (bool, error) {
	pattern = strings.TrimSpace(pattern)
	log.Debug().Str("pattern", pattern).Int("currentValue", currentValue).Msg("Check if Pattern match current Value")
	// All numbers
	if pattern == "*" {
		return true, nil
	}

	// Multible arguments
	details := strings.Split(pattern, ",")
	if len(details) > 1 {
		for i := range details {
			res, err := matchTime(details[i], currentValue)
			if err != nil {
				return false, fmt.Errorf(ErrWrapTemplate, ErrCantParseTimePattern, err)
			}
			if res {
				return res, nil
			}
		}

		return false, nil // Non of the part match so nothing will match
	}
	// Singel number
	valueAsInt, err := strconv.Atoi(pattern)
	if err == nil {
		if valueAsInt == currentValue {
			return true, nil
		}
		return false, nil
	}

	// Normal ranges no ranges with defision
	if strings.Contains(pattern, "-") && !strings.Contains(pattern, "/") {
		smallNumber, largeNumber, err := getNumbersOfRange(pattern)
		if err != nil {
			return false, err
		}
		if smallNumber < currentValue && largeNumber > currentValue {
			return true, nil
		}
		return false, nil
	}

	if strings.Contains(pattern, "/") {
		numbers := strings.Split(pattern, "/")
		if len(numbers) != 2 {
			return false, fmt.Errorf(ErrWrapTemplate, ErrCantParseTimePattern, "pattern dont include 2 numbers")
		}
		numberDivisor, err := strconv.Atoi(numbers[1])
		if err != nil {
			return false, fmt.Errorf(ErrWrapTemplate, ErrCantParseTimePattern, err)
		}
		if numbers[0] == "*" {
			res := currentValue % numberDivisor
			if res == 0 {
				return true, nil
			}
			return false, nil
		}
		if strings.Contains(numbers[0], "-") {
			smallNumber, largeNumber, err := getNumbersOfRange(numbers[0])
			if err != nil {
				return false, err
			}
			if !(smallNumber < currentValue && largeNumber > currentValue) {
				return false, nil
			}

			newValue := currentValue - smallNumber
			res := newValue % numberDivisor
			if res == 0 {
				return true, nil
			}

			return false, nil
		}
	}

	// Cant Parse the time
	return false, ErrCantParseTimePattern
}

func getNumbersOfRange(pattern string) (int, int, error) {
	numbers := strings.Split(pattern, "-")
	if len(numbers) != 2 {
		return 0, 0, fmt.Errorf(ErrWrapTemplate, ErrCantParseTimePattern, "range include more than 2 parameter")
	}
	smallNumber, err := strconv.Atoi(strings.TrimSpace(numbers[0]))
	if err != nil {
		return 0, 0, fmt.Errorf(ErrWrapTemplate, ErrCantParseTimePattern, err)
	}
	largeNumber, err := strconv.Atoi(strings.TrimSpace(numbers[1]))
	if err != nil {
		return 0, 0, fmt.Errorf(ErrWrapTemplate, ErrCantParseTimePattern, err)
	}
	if smallNumber >= largeNumber {
		return 0, 0, fmt.Errorf(ErrWrapTemplate, ErrCantParseTimePattern, "smaler number should be first")
	}
	return smallNumber, largeNumber, nil
}
