package admin

import (
	"fmt"
	"io"
	"log"
	"os"
	"path/filepath"
	"sync"
	"time"
)

type rotatingLogWriter struct {
	path       string
	maxBytes   int64
	maxBackups int
	file       *os.File
	size       int64
	mu         sync.Mutex
}

func ConfigureLogging(cfg Config) (io.Closer, error) {
	if cfg.LogFile == "" {
		return nil, nil
	}
	writer, err := newRotatingLogWriter(cfg.LogFile, cfg.LogMaxBytes, cfg.LogMaxBackups)
	if err != nil {
		return nil, err
	}
	multi := io.MultiWriter(os.Stdout, writer)
	log.SetOutput(multi)
	log.SetFlags(log.LstdFlags | log.Lmicroseconds | log.LUTC)
	return writer, nil
}

func newRotatingLogWriter(path string, maxBytes int64, maxBackups int) (*rotatingLogWriter, error) {
	if maxBytes <= 0 {
		maxBytes = 10 * 1024 * 1024
	}
	if maxBackups <= 0 {
		maxBackups = 5
	}
	if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
		return nil, fmt.Errorf("create log dir: %w", err)
	}
	file, err := os.OpenFile(path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0o644)
	if err != nil {
		return nil, fmt.Errorf("open log file: %w", err)
	}
	info, err := file.Stat()
	if err != nil {
		file.Close()
		return nil, fmt.Errorf("stat log file: %w", err)
	}
	return &rotatingLogWriter{
		path:       path,
		maxBytes:   maxBytes,
		maxBackups: maxBackups,
		file:       file,
		size:       info.Size(),
	}, nil
}

func (w *rotatingLogWriter) Write(p []byte) (int, error) {
	w.mu.Lock()
	defer w.mu.Unlock()

	if w.size+int64(len(p)) > w.maxBytes {
		if err := w.rotateLocked(); err != nil {
			return 0, err
		}
	}
	n, err := w.file.Write(p)
	w.size += int64(n)
	return n, err
}

func (w *rotatingLogWriter) Close() error {
	w.mu.Lock()
	defer w.mu.Unlock()
	if w.file == nil {
		return nil
	}
	err := w.file.Close()
	w.file = nil
	return err
}

func (w *rotatingLogWriter) rotateLocked() error {
	if w.file != nil {
		if err := w.file.Close(); err != nil {
			return err
		}
	}
	stamp := time.Now().UTC().Format("20060102T150405")
	rotated := fmt.Sprintf("%s.%s", w.path, stamp)
	if err := os.Rename(w.path, rotated); err != nil && !os.IsNotExist(err) {
		return fmt.Errorf("rotate log: %w", err)
	}
	if err := w.pruneLocked(); err != nil {
		return err
	}
	file, err := os.OpenFile(w.path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0o644)
	if err != nil {
		return fmt.Errorf("open rotated log file: %w", err)
	}
	w.file = file
	w.size = 0
	return nil
}

func (w *rotatingLogWriter) pruneLocked() error {
	pattern := w.path + ".*"
	matches, err := filepath.Glob(pattern)
	if err != nil {
		return fmt.Errorf("list rotated logs: %w", err)
	}
	if len(matches) <= w.maxBackups {
		return nil
	}
	sortStrings(matches)
	for _, path := range matches[:len(matches)-w.maxBackups] {
		if err := os.Remove(path); err != nil && !os.IsNotExist(err) {
			return fmt.Errorf("remove old log %s: %w", path, err)
		}
	}
	return nil
}

func sortStrings(values []string) {
	for i := 1; i < len(values); i++ {
		value := values[i]
		j := i - 1
		for ; j >= 0 && values[j] > value; j-- {
			values[j+1] = values[j]
		}
		values[j+1] = value
	}
}
