package dataswamp

import (
	"caj-larsson/bog/dataswamp/namespace"
	"caj-larsson/bog/dataswamp/swampfile"
	"io"
	"strconv"
	"time"
	//	"errors"
	//	"fmt"
)

type SwampFileService struct {
	namespace_repo             namespace.Repository
	swamp_file_repo            swampfile.Repository
	default_allowance_bytes    int64
	default_allowance_duration time.Duration
	logger                     Logger
}

func NewSwampFileService(
	namespace_repo namespace.Repository,
	swamp_file_repo swampfile.Repository,
	da_bytes int64,
	da_duration time.Duration,
	logger Logger,
) SwampFileService {
	return SwampFileService{namespace_repo, swamp_file_repo, da_bytes, da_duration, logger}
}

func (s SwampFileService) getOrCreateNs(namespace_in string) *namespace.Namespace {
	ns, err := s.namespace_repo.GetByName(namespace_in)

	if err == namespace.ErrNotExists {
		new_ns := namespace.Namespace{
			0,
			namespace_in,
			time.Now(),
			s.default_allowance_duration,
			namespace.FileSizeQuota{s.default_allowance_bytes, 0},
		}
		created_ns, err := s.namespace_repo.Create(new_ns)

		if err != nil {
			panic(err)
		}

		return created_ns
	}

	if err != nil {
		panic(err)
	}

	return ns
}

func (s SwampFileService) SaveFile(ref swampfile.FileReference, src io.Reader, size int64) error {
	ns := s.getOrCreateNs(ref.UserAgent)

	err := ref.Clean(true)

	if err != nil {
		return err
	}

	if !ns.FileQuota.Allows(size) {
		return namespace.ErrExceedQuota
	}

	f, err := s.swamp_file_repo.Create(ref.Path, strconv.FormatInt(ns.ID, 10))

	if err != nil {
		return err
	}

	written, err := io.CopyN(f, src, size)

	if written < size {
		s.swamp_file_repo.Delete(ref.Path, strconv.FormatInt(ns.ID, 10))
		return swampfile.ErrContentSizeExaggerated
	}

	var buf = make([]byte, 1)

	overread, err := src.Read(buf)

	if overread > 0 || err != io.EOF {
		s.swamp_file_repo.Delete(ref.Path, strconv.FormatInt(ns.ID, 10))
		return swampfile.ErrContentSizeExceeded
	}

	f.Close()
	ns.FileQuota.Add(size)
	s.namespace_repo.Update(ns.ID, *ns)

	return nil
}

func (s SwampFileService) OpenOutFile(ref swampfile.FileReference) (swampfile.SwampOutFile, error) {
	ns := s.getOrCreateNs(ref.UserAgent)

	err := ref.Clean(true)

	if err != nil {
		return nil, err
	}

	f, err := s.swamp_file_repo.Open(ref.Path, strconv.FormatInt(ns.ID, 10))

	if err != nil {
		return nil, err
	}

	return f, nil
}

func (s SwampFileService) CleanUpExpiredFiles() error {
	s.logger.Info("Cleaning up expired files")
	nss, err := s.namespace_repo.All()

	if err != nil {
		return err
	}

	for _, ns := range nss {
		expiry := time.Now().Add(-ns.AllowanceDuration)
		dfs, err := s.swamp_file_repo.DeleteOlderThan(strconv.FormatInt(ns.ID, 10), expiry)

		for _, df := range dfs {
			ns.FileQuota.Remove(df.Size)
		}

		if err != nil {
			panic(err)
		}

		s.namespace_repo.Update(ns.ID, ns)
	}
	return nil
}