package file

import (
	"io/fs"
	"os"
	"path/filepath"
	"skapp/pkg/logger"
	"sync"
)

type Manager struct {
	FileManager *sync.Map
	lock        *sync.RWMutex
}

func NewManager() *Manager {
	return &Manager{
		FileManager: &sync.Map{},
		lock:        &sync.RWMutex{},
	}
}

func (m *Manager) Len() (count int64) {
	defer func() {
		m.lock.RUnlock()
	}()
	m.lock.RLock()

	m.Each(func(file *os.File) bool {
		count++
		return true
	})
	return
}

func (m *Manager) Each(cb func(*os.File) bool) {
	defer func() {
		m.lock.RUnlock()
	}()
	m.lock.RLock()

	m.FileManager.Range(func(key, value any) bool {
		flagManager := value.(*sync.Map)
		flagManager.Range(func(key, value any) bool {
			fileModeManager := value.(*sync.Map)
			fileModeManager.Range(func(key, value any) bool {
				f := (value).(*os.File)
				return cb(f)
			})
			return true
		})
		return true
	})
}

func (m *Manager) Remove(path string, flag int, perm fs.FileMode) {
	if m.Get(path, flag, perm) == nil {
		return
	}

	defer func() {
		m.lock.Unlock()
	}()
	m.lock.Lock()

	absPath, err := filepath.Abs(path)
	if err != nil {
		return
	}

	logger.Log.Infof("Remove %+v %+v %+v\n", absPath, flag, perm)
	var value interface{}
	value, _ = m.FileManager.Load(absPath)
	flagManager := value.(*sync.Map)

	value, _ = flagManager.Load(flag)
	fileModeManager := value.(*sync.Map)

	value, _ = fileModeManager.LoadAndDelete(perm.String())
	f := value.(*os.File)
	_ = f.Close()
}

func (m *Manager) Get(path string, flag int, perm fs.FileMode) *os.File {
	defer func() {
		m.lock.RUnlock()
	}()
	m.lock.RLock()

	absPath, err := filepath.Abs(path)
	if err != nil {
		return nil
	}

	logger.Log.Infof("Get %+v %+v %+v\n", absPath, flag, perm)
	value, ok := m.FileManager.Load(absPath)
	if !ok {
		return nil
	}

	switch flagManager := value.(type) {
	case *sync.Map:
		value, ok := flagManager.Load(flag)
		if !ok {
			return nil
		}

		switch fileModeManager := value.(type) {
		case *sync.Map:
			f, ok := fileModeManager.Load(perm.String())
			if !ok {
				return nil
			}

			return f.(*os.File)
		}
	}

	return nil
}

func (m *Manager) Close() {
	m.Each(func(f *os.File) bool {
		_ = f.Close()
		return true
	})
}

func (m *Manager) Set(file *os.File, path string, flag int, perm fs.FileMode) {
	defer func() {
		m.lock.Unlock()
	}()
	m.lock.Lock()

	absPath, err := filepath.Abs(path)
	if err != nil {
		return
	}

	logger.Log.Infof("Set %+v %+v %+v\n", absPath, flag, perm)
	value, ok := m.FileManager.Load(path)
	if !ok {
		flagManager := sync.Map{}
		fileModeManager := sync.Map{}
		fileModeManager.Store(perm.String(), file)
		flagManager.Store(flag, &fileModeManager)
		m.FileManager.Store(absPath, &flagManager)
		return
	}

	switch flagManager := value.(type) {
	case *sync.Map:
		value, ok := flagManager.Load(flag)
		if !ok {
			fileModeManager := sync.Map{}
			fileModeManager.Store(perm.String(), file)
			flagManager.Store(flag, &fileModeManager)
		}

		switch fileModeManager := value.(type) {
		case *sync.Map:
			_, ok := fileModeManager.Load(perm.String())
			if !ok {
				fileModeManager.Store(perm.String(), file)
			}
		}
	}
}
