package log

import (
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"time"

	"github.com/thrasher-corp/gocryptotrader/common/file"
)

var (
	errExceedsMaxFileSize = errors.New("exceeds max file size")
	errFileNameIsEmpty    = errors.New("filename is empty")
)

// Write implementation to satisfy io.Writer handles length check and rotation
func (r *Rotate) Write(output []byte) (n int, err error) {
	outputLen := int64(len(output))
	if outputLen > r.maxSize() {
		return 0, fmt.Errorf(
			"write length %v %w %v", outputLen, errExceedsMaxFileSize, r.maxSize(),
		)
	}

	if r.output == nil {
		err = r.openOrCreateFile(outputLen)
		if err != nil {
			return 0, err
		}
	}

	if *r.Rotate {
		if r.size+outputLen > r.maxSize() {
			err = r.rotateFile()
			if err != nil {
				return 0, err
			}
		}
	}

	n, err = r.output.Write(output)
	r.size += int64(n)
	return n, err
}

func (r *Rotate) openOrCreateFile(n int64) error {
	logFile := filepath.Join(GetLogPath(), r.FileName)
	info, err := os.Stat(logFile)
	if err != nil {
		if os.IsNotExist(err) {
			return r.openNew()
		}
		return fmt.Errorf("error opening log file info: %s", err)
	}

	if *r.Rotate {
		if info.Size()+n >= r.maxSize() {
			return r.rotateFile()
		}
	}

	file, err := os.OpenFile(logFile, os.O_APPEND|os.O_WRONLY, 0o600)
	if err != nil {
		return r.openNew()
	}

	r.output = file
	r.size = info.Size()

	return nil
}

func (r *Rotate) openNew() error {
	if r.FileName == "" {
		return fmt.Errorf("cannot open new file: %w", errFileNameIsEmpty)
	}
	name := filepath.Join(GetLogPath(), r.FileName)
	_, err := os.Stat(name)

	if err == nil {
		timestamp := time.Now().Format("2006-01-02T15-04-05")
		newName := filepath.Join(GetLogPath(), timestamp+"-"+r.FileName)

		err = file.Move(name, newName)
		if err != nil {
			return fmt.Errorf("can't rename log file: %s", err)
		}
	}

	file, err := os.OpenFile(name, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0o600)
	if err != nil {
		return fmt.Errorf("can't open new logfile: %s", err)
	}

	r.output = file
	r.size = 0
	return nil
}

func (r *Rotate) close() (err error) {
	if r.output == nil {
		return nil
	}
	err = r.output.Close()
	r.output = nil
	return err
}

// Close handler for open file
func (r *Rotate) Close() error {
	return r.close()
}

func (r *Rotate) rotateFile() (err error) {
	err = r.close()
	if err != nil {
		return
	}
	return r.openNew()
}

func (r *Rotate) maxSize() int64 {
	if r.MaxSize == 0 {
		return defaultMaxSize * megabyte
	}
	return r.MaxSize * megabyte
}
