// Copyright (C) 2023 Marius Schellenberger

package types

import (
	"maps"
	"sync"
	"time"

	"git.giftfish.de/ston1th/keyctl/pkg/key"
)

const maxSize = 10

type ApprovalMap map[string]Approval

type Approver struct {
	// protects m
	mu sync.RWMutex

	m ApprovalMap
}

func NewApprover() *Approver {
	return &Approver{
		m: make(ApprovalMap),
	}
}

func (a *Approver) New(id, name, ip string) (aid string, err error) {
	a.mu.Lock()
	defer a.mu.Unlock()
	size := len(a.m)
	if size > maxSize {
		err = ErrTooManyApprovals
		return
	}
	aid, err = key.GenerateID()
	if err != nil {
		return
	}
	a.m[aid] = Approval{
		ID:   id,
		Name: name,
		IP:   ip,
	}
	return
}

func (a *Approver) List() (m ApprovalMap) {
	a.mu.RLock()
	m = maps.Clone(a.m)
	a.mu.RUnlock()
	return
}
func (a *Approver) Status(id, aid, ip string) (s Status) {
	a.mu.RLock()
	if app, ok := a.m[aid]; ok {
		if app.ID == id && app.IP == ip {
			s = app.Status
		} else {
			s = Rejected
		}
	} else {
		s = NotFound
	}
	a.mu.RUnlock()
	if s == Approved || s == Rejected {
		a.mu.Lock()
		delete(a.m, aid)
		a.mu.Unlock()
	}
	return
}

func (a *Approver) Update(aid string, s Status) (err error) {
	a.mu.Lock()
	if app, ok := a.m[aid]; ok {
		app.Status = s
		a.m[aid] = app
		go a.cleanup(aid)
	} else {
		err = ErrReqNotFound
	}
	a.mu.Unlock()
	return
}

func (a *Approver) cleanup(aid string) {
	time.Sleep(time.Second * 10)
	a.mu.Lock()
	delete(a.m, aid)
	a.mu.Unlock()
}

type Approval struct {
	ID     string
	Name   string
	IP     string
	Status Status
}

type Status int

const (
	Pending Status = iota
	Approved
	Rejected
	NotFound
)

func (s Status) String() string {
	switch s {
	case Pending:
		return "pending"
	case Approved:
		return "approved"
	case Rejected:
		return "rejected"
	case NotFound:
		return "notfound"
	}
	return "[invalid]"
}
