package cache

import (
	"context"
	"encoding/json"
	"fmt"
	"log"
	"os"
	"path"
	"time"

	"gogs.inhome.blapointe.com/ana-ledger/src/bank"
)

type Client struct {
	Client bank.Agg
}

var _ bank.Agg = Client{}

func New(client bank.Agg) Client {
	return Client{Client: client}
}

func (c Client) Accounts(ctx context.Context) ([]bank.Account, error) {
	k := "accounts"
	result := []bank.Account{}
	if err := fromCache(k, &result); err != nil {
		log.Printf("%q not in cache: %v", k, err)
	} else {
		return result, nil
	}

	result, err := c.Client.Accounts(ctx)
	if err != nil {
		return nil, err
	}

	toCache(k, result)

	return result, nil
}

func (c Client) Transactions(ctx context.Context, a bank.Account) ([]bank.Transaction, error) {
	k := path.Join("accounts.d", a.Account)
	result := []bank.Transaction{}
	if err := fromCache(k, &result); err != nil {
		log.Printf("%q not in cache: %v", k, err)
	} else {
		return result, nil
	}

	result, err := c.Client.Transactions(ctx, a)
	if err != nil {
		return nil, err
	}

	toCache(k, result)

	return result, nil
}

var (
	d = path.Join("/tmp/ana_ledger_bank_cache.d")
)

func toCache(k string, v interface{}) {
	if err := _toCache(k, v); err != nil {
		log.Printf("failed to cache %s: %v", k, err)
	}
}

func _toCache(k string, v interface{}) error {
	b, err := json.Marshal(v)
	if err != nil {
		return err
	}

	p := path.Join(d, k)
	os.MkdirAll(path.Dir(p), os.ModePerm)
	if err := os.WriteFile(p, b, os.ModePerm); err != nil {
		os.Remove(p)
		return err
	}

	return nil
}

func fromCache(k string, ptr interface{}) error {
	p := path.Join(d, k)
	if stat, err := os.Stat(p); err != nil {
		return err
	} else if time.Since(stat.ModTime()) > 24*time.Hour {
		return fmt.Errorf("stale")
	}

	b, err := os.ReadFile(p)
	if err != nil {
		return err
	}

	if err := json.Unmarshal(b, ptr); err != nil {
		return err
	}

	return nil
}
