package storage

import (
	"context"
	"encoding/json"
	"errors"
	"os"
	"path/filepath"
	"strings"
	"time"
)

type LocalClient struct {
	DataDir   string
	listeners map[string]chan []byte
}

func (c *LocalClient) Poll(path string, ctx context.Context) ([]byte, error) {
	ch := make(chan []byte)
	c.listeners[path] = ch
	select {
	case d := <-ch:
		return d, nil
	case <-time.After(time.Second * 30):
		return c.Read(path)
	case <-ctx.Done():
		return nil, nil
	}
}

func (c *LocalClient) Read(path string) ([]byte, error) {
	p := filepath.Join(c.DataDir, path)
	fi, err := os.Stat(p)
	if errors.Is(err, os.ErrNotExist) {
		return nil, nil
	}
	if err != nil {
		return nil, err
	}
	if fi.IsDir() {
		contents, err := c.ReadDir(path)
		if err != nil {
			return nil, err
		}
		b, err := json.Marshal(contents)
		if err != nil {
			return nil, err
		}
		return b, nil
	}
	s, err := c.ReadFile(path)
	if err != nil {
		return nil, err
	}
	return []byte(s), nil
}

func (c *LocalClient) ReadFile(path string) (string, error) {
	b, err := os.ReadFile(filepath.Join(c.DataDir, path))
	if errors.Is(err, os.ErrNotExist) {
		return "", nil
	}
	return strings.TrimSpace(string(b)), nil
}

func (c *LocalClient) ReadDir(path string) ([]string, error) {
	entries, err := os.ReadDir(filepath.Join(c.DataDir, path))
	if err != nil {
		return nil, err
	}
	res := []string{}
	for _, e := range entries {
		res = append(res, e.Name())
	}
	return res, nil
}

func (c *LocalClient) WriteFile(path string, data string) error {
	p := filepath.Join(c.DataDir, path)
	err := os.MkdirAll(filepath.Dir(p), 0755)
	if err != nil {
		return err
	}
	f, err := os.OpenFile(p, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
	if err != nil {
		return err
	}
	defer f.Close()
	b := []byte(strings.TrimSpace(data))
	_, err = f.Write(b)
	if err != nil {
		return err
	}
	if c.listeners[path] != nil {
		c.listeners[path] <- b
	}
	return nil
}

func (c *LocalClient) Delete(path string) error {
	err := os.RemoveAll(filepath.Join(c.DataDir, path))
	if err != nil {
		return err
	}
	if c.listeners[path] != nil {
		c.listeners[path] <- nil
	}
	return nil
}
